From a76ab6e7e6859ca56d4228ba4491885148f8bd93 Mon Sep 17 00:00:00 2001
From: Oswaldo Acauan <oswaldoacauan@gmail.com>
Date: Fri, 21 Oct 2016 11:41:17 +0000
Subject: [PATCH] Refactor API Slides

---
 .../server/modifiers/addChatToCollection.js   |   4 +-
 .../imports/api/common/server/helpers.js      |  10 +-
 .../server/methods/switchSlideMessage.js      |  44 ------
 .../server/modifiers/eventHandlers.js         |  55 +-------
 .../api/slides/server/eventHandlers.js        |   6 +
 .../api/slides/server/handlers/slideChange.js |  16 +++
 .../api/slides/server/handlers/slideResize.js |  17 +++
 .../imports/api/slides/server/index.js        |   3 +
 .../imports/api/slides/server/methods.js      |   7 +
 .../api/slides/server/methods/switchSlide.js  |  51 +++++++
 .../server/modifiers/addSlideToCollection.js  | 126 +++++++++---------
 .../server/modifiers/changeCurrentSlide.js    |  56 ++++++++
 .../server/modifiers/clearSlidesCollection.js |  14 +-
 .../modifiers/clearSlidesPresentation.js      |  21 +++
 .../server/modifiers/displayThisSlide.js      |  39 ------
 .../slides/server/modifiers/resizeSlide.js    |  38 ++++++
 .../imports/api/slides/server/publications.js |  10 --
 .../imports/api/slides/server/publishers.js   |  22 +++
 .../imports/utils/mimeTypes.js                |  32 +++++
 bigbluebutton-html5/package.json              |   3 +-
 bigbluebutton-html5/server/main.js            |   6 +-
 21 files changed, 351 insertions(+), 229 deletions(-)
 delete mode 100755 bigbluebutton-html5/imports/api/presentations/server/methods/switchSlideMessage.js
 create mode 100644 bigbluebutton-html5/imports/api/slides/server/eventHandlers.js
 create mode 100644 bigbluebutton-html5/imports/api/slides/server/handlers/slideChange.js
 create mode 100644 bigbluebutton-html5/imports/api/slides/server/handlers/slideResize.js
 create mode 100644 bigbluebutton-html5/imports/api/slides/server/index.js
 create mode 100644 bigbluebutton-html5/imports/api/slides/server/methods.js
 create mode 100644 bigbluebutton-html5/imports/api/slides/server/methods/switchSlide.js
 create mode 100755 bigbluebutton-html5/imports/api/slides/server/modifiers/changeCurrentSlide.js
 create mode 100644 bigbluebutton-html5/imports/api/slides/server/modifiers/clearSlidesPresentation.js
 delete mode 100755 bigbluebutton-html5/imports/api/slides/server/modifiers/displayThisSlide.js
 create mode 100644 bigbluebutton-html5/imports/api/slides/server/modifiers/resizeSlide.js
 delete mode 100755 bigbluebutton-html5/imports/api/slides/server/publications.js
 create mode 100644 bigbluebutton-html5/imports/api/slides/server/publishers.js
 create mode 100644 bigbluebutton-html5/imports/utils/mimeTypes.js

diff --git a/bigbluebutton-html5/imports/api/chat/server/modifiers/addChatToCollection.js b/bigbluebutton-html5/imports/api/chat/server/modifiers/addChatToCollection.js
index f289e89a29..62ee24b3fb 100755
--- a/bigbluebutton-html5/imports/api/chat/server/modifiers/addChatToCollection.js
+++ b/bigbluebutton-html5/imports/api/chat/server/modifiers/addChatToCollection.js
@@ -54,8 +54,8 @@ export default function addChatToCollection(meetingId, message) {
   };
 
   const cb = (err, numChanged) => {
-    if (err != null) {
-      Logger.error(`Adding chat to collection: ${err}`);
+    if (err) {
+      return Logger.error(`Adding chat to collection: ${err}`);
     }
 
     const { insertedId } = numChanged;
diff --git a/bigbluebutton-html5/imports/api/common/server/helpers.js b/bigbluebutton-html5/imports/api/common/server/helpers.js
index 72763a4eaa..6e5de896b5 100755
--- a/bigbluebutton-html5/imports/api/common/server/helpers.js
+++ b/bigbluebutton-html5/imports/api/common/server/helpers.js
@@ -1,7 +1,7 @@
 import { clearUsersCollection } from '/imports/api/users/server/modifiers/clearUsersCollection';
-import { clearChatCollection} from '/imports/api/chat/server/modifiers/clearChatCollection';
+import clearChatCollection from '/imports/api/chat/server/modifiers/clearChatCollection';
 import { clearShapesCollection } from '/imports/api/shapes/server/modifiers/clearShapesCollection';
-import { clearSlidesCollection } from '/imports/api/slides/server/modifiers/clearSlidesCollection';
+import clearSlidesCollection from '/imports/api/slides/server/modifiers/clearSlidesCollection';
 import { clearPresentationsCollection }
   from '/imports/api/presentations/server/modifiers/clearPresentationsCollection';
 import { clearMeetingsCollection }
@@ -32,9 +32,9 @@ export function clearCollections() {
     refreshes. Related to: https://github.com/meteor/meteor/issues/6576
   */
 
-  if (process.env.NODE_ENV === 'development') {
-    return;
-  }
+  // if (process.env.NODE_ENV === 'development') {
+  //   return;
+  // }
 
   const meetingId = arguments[0];
   if (meetingId != null) {
diff --git a/bigbluebutton-html5/imports/api/presentations/server/methods/switchSlideMessage.js b/bigbluebutton-html5/imports/api/presentations/server/methods/switchSlideMessage.js
deleted file mode 100755
index ee9b91b36c..0000000000
--- a/bigbluebutton-html5/imports/api/presentations/server/methods/switchSlideMessage.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { publish } from '/imports/api/common/server/helpers';
-import { isAllowedTo } from '/imports/startup/server/userPermissions';
-import { appendMessageHeader } from '/imports/api/common/server/helpers';
-import Presentations from '/imports/api/presentations';
-import Slides from '/imports/api/slides';
-
-Meteor.methods({
-  switchSlideMessage(credentials, requestedSlideNum) {
-    const REDIS_CONFIG = Meteor.settings.redis;
-    const { meetingId, requesterUserId, requesterToken } = credentials;
-
-    const currentPresentationDoc = Presentations.findOne({
-      meetingId: meetingId,
-      'presentation.current': true,
-    });
-
-    if (currentPresentationDoc != null) {
-      const currentSlideDoc = Slides.findOne({
-        meetingId: meetingId,
-        presentationId: currentPresentationDoc.presentation.id,
-        'slide.current': true,
-      });
-      if (currentSlideDoc != null) {
-        const requestedSlideDoc = Slides.findOne({
-          meetingId: meetingId,
-          presentationId: currentPresentationDoc.presentation.id,
-          'slide.num': parseInt(requestedSlideNum),
-        });
-
-        if ((requestedSlideDoc != null) && isAllowedTo('switchSlide', credentials)) {
-          let message = {
-            payload: {
-              page: requestedSlideDoc.slide.id,
-              meeting_id: meetingId,
-            },
-          };
-
-          message = appendMessageHeader('go_to_slide', message);
-          return publish(REDIS_CONFIG.channels.toBBBApps.presentation, message);
-        }
-      }
-    }
-  },
-});
diff --git a/bigbluebutton-html5/imports/api/presentations/server/modifiers/eventHandlers.js b/bigbluebutton-html5/imports/api/presentations/server/modifiers/eventHandlers.js
index 8104ebed68..1808939f76 100755
--- a/bigbluebutton-html5/imports/api/presentations/server/modifiers/eventHandlers.js
+++ b/bigbluebutton-html5/imports/api/presentations/server/modifiers/eventHandlers.js
@@ -1,66 +1,13 @@
 import { eventEmitter } from '/imports/startup/server';
 import { removePresentationFromCollection } from './removePresentationFromCollection';
 import { addPresentationToCollection } from './addPresentationToCollection';
-import { displayThisSlide } from '/imports/api/slides/server/modifiers/displayThisSlide';
 import { appendMessageHeader, publish, inReplyToHTML5Client }
   from '/imports/api/common/server/helpers';
-import { addSlideToCollection } from '/imports/api/slides/server/modifiers/addSlideToCollection';
+import addSlideToCollection from '/imports/api/slides/server/modifiers/addSlideToCollection';
 import Slides from '/imports/api/slides';
 import Presentations from '/imports/api/presentations';
 import { logger } from '/imports/startup/server/logger';
 
-eventEmitter.on('presentation_page_resized_message', function (arg) {
-  const payload = arg.payload;
-  const page = payload.page;
-  if (page != null && page.id != null && page.height_ratio != null
-    && page.width_ratio != null && page.x_offset != null && page.y_offset != null) {
-    const slideId = page.id;
-    const heightRatio = page.height_ratio;
-    const widthRatio = page.width_ratio;
-    const xOffset = page.x_offset;
-    const yOffset = page.y_offset;
-    const presentationId = slideId.split('/')[0];
-
-    // In the case when we don't resize, but switch a slide, this message
-    // follows a 'presentation_page_changed' and all these properties are already set.
-    let currentSlide = Slides.findOne(
-      { presentationId: presentationId,
-        'slide.current': true, });
-    if (currentSlide) {
-      currentSlide = currentSlide.slide;
-    }
-
-    if (currentSlide != null && (currentSlide.height_ratio != heightRatio ||
-      currentSlide.width_ratio != widthRatio ||
-      currentSlide.x_offset != xOffset ||
-      currentSlide.y_offset != yOffset)) {
-      Slides.update({
-        presentationId: presentationId,
-        'slide.current': true,
-      }, {
-        $set: {
-          'slide.height_ratio': heightRatio,
-          'slide.width_ratio': widthRatio,
-          'slide.x_offset': xOffset,
-          'slide.y_offset': yOffset,
-        },
-      });
-    }
-  }
-
-  return arg.callback();
-});
-
-eventEmitter.on('presentation_page_changed_message', function (arg) {
-  const newSlide = arg.payload.page;
-  const meetingId = arg.payload.meeting_id;
-  if (newSlide != null && newSlide.id != null && meetingId != null) {
-    displayThisSlide(meetingId, newSlide.id, newSlide);
-  }
-
-  return arg.callback();
-});
-
 eventEmitter.on('presentation_removed_message', function (arg) {
   const meetingId = arg.payload.meeting_id;
   const presentationId = arg.payload.presentation_id;
diff --git a/bigbluebutton-html5/imports/api/slides/server/eventHandlers.js b/bigbluebutton-html5/imports/api/slides/server/eventHandlers.js
new file mode 100644
index 0000000000..46a0aa1922
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/slides/server/eventHandlers.js
@@ -0,0 +1,6 @@
+import RedisPubSub from '/imports/startup/server/redis';
+import handleSlideResize from './handlers/slideResize';
+import handleSlideChange from './handlers/slideChange';
+
+RedisPubSub.on('presentation_page_resized_message', handleSlideResize);
+RedisPubSub.on('presentation_page_changed_message', handleSlideChange);
diff --git a/bigbluebutton-html5/imports/api/slides/server/handlers/slideChange.js b/bigbluebutton-html5/imports/api/slides/server/handlers/slideChange.js
new file mode 100644
index 0000000000..dd24043b97
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/slides/server/handlers/slideChange.js
@@ -0,0 +1,16 @@
+import Logger from '/imports/startup/server/logger';
+import { check } from 'meteor/check';
+import changeCurrentSlide from '../modifiers/changeCurrentSlide';
+
+export default function handleSlideChange({ payload }) {
+  const meetingId = payload.meeting_id;
+  const slide = payload.page;
+
+  check(meetingId, String);
+  check(slide, Object);
+
+  const slideId = slide.id;
+  const presentationId = slideId.split('/')[0];
+
+  return changeCurrentSlide(meetingId, presentationId, slideId);
+};
diff --git a/bigbluebutton-html5/imports/api/slides/server/handlers/slideResize.js b/bigbluebutton-html5/imports/api/slides/server/handlers/slideResize.js
new file mode 100644
index 0000000000..880d08daeb
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/slides/server/handlers/slideResize.js
@@ -0,0 +1,17 @@
+import Logger from '/imports/startup/server/logger';
+import Slides from '/imports/api/slides';
+import { check } from 'meteor/check';
+import resizeSlide from '../modifiers/resizeSlide';
+
+export default function handleSlideResize({ payload }) {
+  const meetingId = payload.meeting_id;
+  const slide = payload.page;
+
+  check(meetingId, String);
+  check(slide, Object);
+
+  const slideId = slide.id;
+  const presentationId = slideId.split('/')[0];
+
+  return resizeSlide(meetingId, presentationId, slideId, slide);
+};
diff --git a/bigbluebutton-html5/imports/api/slides/server/index.js b/bigbluebutton-html5/imports/api/slides/server/index.js
new file mode 100644
index 0000000000..92451ac76b
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/slides/server/index.js
@@ -0,0 +1,3 @@
+import './eventHandlers';
+import './methods';
+import './publishers';
diff --git a/bigbluebutton-html5/imports/api/slides/server/methods.js b/bigbluebutton-html5/imports/api/slides/server/methods.js
new file mode 100644
index 0000000000..774a6fc921
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/slides/server/methods.js
@@ -0,0 +1,7 @@
+import { Meteor } from 'meteor/meteor';
+import switchSlide from './methods/switchSlide';
+
+Meteor.methods({
+  switchSlide,
+  switchSlideMessage: switchSlide, // legacy
+});
diff --git a/bigbluebutton-html5/imports/api/slides/server/methods/switchSlide.js b/bigbluebutton-html5/imports/api/slides/server/methods/switchSlide.js
new file mode 100644
index 0000000000..75114a7212
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/slides/server/methods/switchSlide.js
@@ -0,0 +1,51 @@
+import Presetations from '/imports/api/presentations';
+import Slides from '/imports/api/slides';
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+import Logger from '/imports/startup/server/logger';
+import RedisPubSub from '/imports/startup/server/redis';
+import { isAllowedTo } from '/imports/startup/server/userPermissions';
+
+export default function switchSlide(credentials, slideNumber) {
+  const REDIS_CONFIG = Meteor.settings.redis;
+
+  const CHANNEL = REDIS_CONFIG.channels.toBBBApps.presentation;
+  const EVENT_NAME = 'go_to_slide';
+
+  const { meetingId, requesterUserId, requesterToken } = credentials;
+
+  check(meetingId, String);
+  check(requesterUserId, String);
+  check(requesterToken, String);
+  check(slideNumber, Number);
+
+  if (!isAllowedTo('switchSlide', credentials)) {
+    throw new Meteor.Error('not-allowed', `You are not allowed to switchSlide`);
+  }
+
+  const Presentation = Presentations.findOne({
+    meetingId,
+    'presentation.current': true,
+  });
+
+  if (!Presentation) {
+    throw new Meteor.Error('presentation-not-found', `You need a presentation to be able to switch slides`);
+  }
+
+  const Slide = Slides.findOne({
+    meetingId,
+    presentationId: Presentation.presentation.id,
+    'slide.num': parseInt(slideNumber),
+  });
+
+  if (!Slide) {
+    throw new Meteor.Error('slide-not-found', `Slide number ${slideNumber} not found in the current presentation`);
+  }
+
+  let payload = {
+    page: Slide.slide.id,
+    meeting_id: meetingId,
+  };
+
+  return RedisPubSub.publish(CHANNEL, EVENT_NAME, payload);
+};
diff --git a/bigbluebutton-html5/imports/api/slides/server/modifiers/addSlideToCollection.js b/bigbluebutton-html5/imports/api/slides/server/modifiers/addSlideToCollection.js
index 5c8ef98ba0..020ae8c8bb 100755
--- a/bigbluebutton-html5/imports/api/slides/server/modifiers/addSlideToCollection.js
+++ b/bigbluebutton-html5/imports/api/slides/server/modifiers/addSlideToCollection.js
@@ -1,71 +1,77 @@
-import sizeOf from 'image-size';
+import probe from 'probe-image-size';
+import { check } from 'meteor/check';
 import Slides from '/imports/api/slides';
+import Logger from '/imports/startup/server/logger';
+import { SVG, PNG } from '/imports/utils/mimeTypes';
 
-export function addSlideToCollection(meetingId, presentationId, slideObject) {
-  const APP_CONFIG = Meteor.settings.public.app;
-  const url = Npm.require('url');
+const SUPPORTED_TYPES = [SVG, PNG];
 
-  const imageUri = slideObject.svg_uri != null ? slideObject.svg_uri : slideObject.png_uri;
-  if (Slides.findOne({
-    meetingId: meetingId,
-    'slide.id': slideObject.id,
-  }) == null) {
-    const options = url.parse(imageUri);
+export default function addSlideToCollection(meetingId, presentationId, slide) {
+  check(meetingId, String);
+  check(presentationId, String);
+  check(slide, Object);
 
-    let addSlideHelper = function (response) {
-      let contentType = response.headers['content-type'];
+  const selector = {
+    meetingId,
+    presentationId,
+    'slide.id': slide.id,
+  };
 
-      if (contentType.match(/svg/gi) || contentType.match(/png/gi)) {
-        let chunks = [];
-        response.on('data', Meteor.bindEnvironment(function (chunk) {
-          chunks.push(chunk);
-        })).on('end', Meteor.bindEnvironment(function () {
-          let buffer = Buffer.concat(chunks);
-          const dimensions = sizeOf(buffer);
-          const entry = {
-            meetingId: meetingId,
-            presentationId: presentationId,
-            slide: {
-              height_ratio: slideObject.height_ratio,
-              y_offset: slideObject.y_offset,
-              num: slideObject.num,
-              x_offset: slideObject.x_offset,
-              current: slideObject.current,
-              img_uri: slideObject.svg_uri != null ? slideObject.svg_uri : slideObject.png_uri,
-              txt_uri: slideObject.txt_uri,
-              id: slideObject.id,
-              width_ratio: slideObject.width_ratio,
-              swf_uri: slideObject.swf_uri,
-              thumb_uri: slideObject.thumb_uri,
-              width: dimensions.width,
-              height: dimensions.height,
-            },
-          };
-          Slides.insert(entry);
-        }));
-      } else {
-        console.log(`Slide file is not accessible or not ready yet`);
-        console.log(`response content-type`, response.headers['content-type']);
-      }
-    };
+  const imageUri = slide.svg_uri || slide.png_uri;
 
-    // HTTPS connection
-    if (APP_CONFIG.httpsConnection) {
-      const https = Npm.require('https');
+  const modifier = {
+    $set: {
+      meetingId: meetingId,
+      presentationId: presentationId,
+      slide: {
+        height_ratio: slide.height_ratio,
+        y_offset: slide.y_offset,
+        num: slide.num,
+        x_offset: slide.x_offset,
+        current: slide.current,
+        img_uri: imageUri,
+        txt_uri: slide.txt_uri,
+        id: slide.id,
+        width_ratio: slide.width_ratio,
+        swf_uri: slide.swf_uri,
+        thumb_uri: slide.thumb_uri,
+        width: slide.width,
+        height: slide.height,
+      },
+    },
+  };
 
-      https.get(options, Meteor.bindEnvironment(function (response) {
-        addSlideHelper(response);
-      }));
-    } else {
-      // HTTP connection
-      const http = Npm.require('http');
+  const cb = (err, numChanged) => {
+    if (err) {
+      return Logger.error(`Adding slide to collection: ${err}`);
+    }
+
+    const { insertedId } = numChanged;
 
-      http.get(options, Meteor.bindEnvironment(function (response) {
-        addSlideHelper(response);
-      }));
+    if (insertedId) {
+      fetchImageSizes(insertedId, imageUri);
+      return Logger.info(`Added slide id=${insertedId} to presentation=${presentationId}`);
     }
+  };
 
-    //logger.info "added slide id =[#{id}]:#{slideObject.id} in #{meetingId}. Now there
-    // are #{Slides.find({meetingId: meetingId}).count()} slides in the meeting"
-  }
+  return Slides.upsert(selector, modifier, cb);
 };
+
+const fetchImageSizes = (slideId, imageUri) =>
+  probe(imageUri)
+  .then(result => {
+    if (!SUPPORTED_TYPES.includes(result.mime)) {
+      throw `Invalid image type, received ${result.mime} expecting ${SUPPORTED_TYPES.join()}`;
+    }
+
+    return Slides.update(slideId, {
+      $set: {
+        width: result.width,
+        height: result.height,
+      },
+    });
+  })
+  .catch(reason => {
+    Logger.error(`Error parsing image size. ${reason}. slide=${slide.id} uri=${imageUri}`);
+    return reason;
+  });
diff --git a/bigbluebutton-html5/imports/api/slides/server/modifiers/changeCurrentSlide.js b/bigbluebutton-html5/imports/api/slides/server/modifiers/changeCurrentSlide.js
new file mode 100755
index 0000000000..f3b6db32e7
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/slides/server/modifiers/changeCurrentSlide.js
@@ -0,0 +1,56 @@
+import { check } from 'meteor/check';
+import Slides from '/imports/api/slides';
+import Logger from '/imports/startup/server/logger';
+
+export default function changeCurrentSlide(meetingId, presentationId, slideId) {
+  check(meetingId, String);
+  check(presentationId, String);
+  check(slideId, String);
+
+  const oldCurrent = {
+    selector: {
+      meetingId,
+      presentationId,
+      'slide.current': true,
+    },
+    modifier: {
+      $set: { 'slide.current': false },
+    },
+    callback: (err) => {
+      if (err) {
+        return Logger.error(`Unsetting the current slide: ${err}`);
+      }
+
+      return Logger.info(`Unsetted as current slide`);
+    },
+  };
+
+  const newCurrent = {
+    selector: {
+      meetingId,
+      presentationId,
+      'slide.id': slideId,
+    },
+    modifier: {
+      $set: { 'slide.current': true },
+    },
+    callback: (err) => {
+      if (err) {
+        return Logger.error(`Setting as current slide id=${slideId}: ${err}`);
+      }
+
+      return Logger.info(`Setted as current slide id=${slideId}`);
+    },
+  };
+
+  const oldSlide = Slides.findOne(oldCurrent.selector);
+  const newSlide = Slides.findOne(newCurrent.selector);
+
+  if (newSlide) {
+    Slides.update(newSlide.id, newCurrent.modifier, newCurrent.callback);
+  }
+
+  if (oldSlide) {
+    Slides.update(oldSlide.id, oldCurrent.modifier, oldCurrent.callback);
+  }
+};
diff --git a/bigbluebutton-html5/imports/api/slides/server/modifiers/clearSlidesCollection.js b/bigbluebutton-html5/imports/api/slides/server/modifiers/clearSlidesCollection.js
index ecfa73af39..9de4c1f04f 100755
--- a/bigbluebutton-html5/imports/api/slides/server/modifiers/clearSlidesCollection.js
+++ b/bigbluebutton-html5/imports/api/slides/server/modifiers/clearSlidesCollection.js
@@ -1,14 +1,10 @@
 import Slides from '/imports/api/slides';
-import { logger } from '/imports/startup/server/logger';
+import Logger from '/imports/startup/server/logger';
 
-// called on server start and meeting end
-export function clearSlidesCollection() {
-  const meetingId = arguments[0];
-  if (meetingId != null) {
-    return Slides.remove({
-      meetingId: meetingId,
-    }, logger.info(`cleared Slides Collection (meetingId: ${meetingId}!`));
+export default function clearSlidesCollection(meetingId) {
+  if (meetingId) {
+    return Slides.remove({ meetingId: meetingId }, Logger.info(`Cleared Slides (${meetingId})`));
   } else {
-    return Slides.remove({}, logger.info('cleared Slides Collection (all meetings)!'));
+    return Slides.remove({}, Logger.info('Cleared Slides (all)'));
   }
 };
diff --git a/bigbluebutton-html5/imports/api/slides/server/modifiers/clearSlidesPresentation.js b/bigbluebutton-html5/imports/api/slides/server/modifiers/clearSlidesPresentation.js
new file mode 100644
index 0000000000..ff7be924fe
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/slides/server/modifiers/clearSlidesPresentation.js
@@ -0,0 +1,21 @@
+import Slides from '/imports/api/slides';
+import Logger from '/imports/startup/server/logger';
+import { check } from 'meteor/check';
+
+export default function clearSlidesPresentation(presentationId) {
+  check(presentationId, String);
+
+  const selector = {
+    presentationId,
+  };
+
+  const cb = (err) => {
+    if (err) {
+      return Logger.error(`Removing Slides from collection: ${err}`);
+    }
+
+    return Logger.info(`Removed Slides where presentationId=${presentationId}`);
+  };
+
+  return Slides.remove(selector, cb);
+};
diff --git a/bigbluebutton-html5/imports/api/slides/server/modifiers/displayThisSlide.js b/bigbluebutton-html5/imports/api/slides/server/modifiers/displayThisSlide.js
deleted file mode 100755
index 86e76971fc..0000000000
--- a/bigbluebutton-html5/imports/api/slides/server/modifiers/displayThisSlide.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import Slides from '/imports/api/slides';
-
-export function displayThisSlide(meetingId, newSlideId, slideObject) {
-
-  // grab the presentationId part of the slideId
-  const presentationId = newSlideId.split('/')[0];
-
-  let currentSlide = Slides.findOne({
-    presentationId: presentationId,
-    'slide.current': true,
-  });
-
-  let newSlide = Slides.findOne({
-    presentationId: presentationId,
-    'slide.id': newSlideId,
-  });
-
-  // first update the new slide as current and update its ratios/offsets
-  if (newSlide != undefined) {
-    Slides.update(newSlide._id, {
-      $set: {
-        'slide.current': true,
-        'slide.height_ratio': slideObject.height_ratio,
-        'slide.width_ratio': slideObject.width_ratio,
-        'slide.x_offset': slideObject.x_offset,
-        'slide.y_offset': slideObject.y_offset,
-      },
-    });
-  }
-
-  // change current to false for the old slide after update the new one
-  if (currentSlide != undefined) {
-    Slides.update(currentSlide._id, {
-      $set: {
-        'slide.current': false,
-      },
-    });
-  }
-};
diff --git a/bigbluebutton-html5/imports/api/slides/server/modifiers/resizeSlide.js b/bigbluebutton-html5/imports/api/slides/server/modifiers/resizeSlide.js
new file mode 100644
index 0000000000..80a5ebed30
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/slides/server/modifiers/resizeSlide.js
@@ -0,0 +1,38 @@
+import probe from 'probe-image-size';
+import { check } from 'meteor/check';
+import Slides from '/imports/api/slides';
+import Logger from '/imports/startup/server/logger';
+
+export default function resizeSlide(meetingId, presentationId, slideId, slide) {
+  check(meetingId, String);
+  check(presentationId, String);
+  check(slideId, String);
+  check(slide, Object);
+
+  const selector = {
+    meetingId,
+    presentationId,
+    'slide.id': slideId,
+  };
+
+  const modifier = {
+    $set: {
+      slide: {
+        width_ratio: slide.width_ratio,
+        height_ratio: slide.height_ratio,
+        x_offset: slide.x_offset,
+        y_offset: slide.y_offset,
+      },
+    },
+  };
+
+  const cb = (err) => {
+    if (err) {
+      return Logger.error(`Resizing slide id=${slideId}: ${err}`);
+    }
+
+    return Logger.info(`Resized slide id=${slideId}`);
+  };
+
+  return Slides.update(selector, modifier, cb);
+};
diff --git a/bigbluebutton-html5/imports/api/slides/server/publications.js b/bigbluebutton-html5/imports/api/slides/server/publications.js
deleted file mode 100755
index be05ff47c3..0000000000
--- a/bigbluebutton-html5/imports/api/slides/server/publications.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import Slides from '/imports/api/slides';
-import { logger } from '/imports/startup/server/logger';
-
-Meteor.publish('slides', function (credentials) {
-  const { meetingId } = credentials;
-  logger.info(`publishing slides for ${meetingId}`);
-  return Slides.find({
-    meetingId: meetingId,
-  });
-});
diff --git a/bigbluebutton-html5/imports/api/slides/server/publishers.js b/bigbluebutton-html5/imports/api/slides/server/publishers.js
new file mode 100644
index 0000000000..d1c68318a5
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/slides/server/publishers.js
@@ -0,0 +1,22 @@
+import Slides from '/imports/api/slides';
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+import Logger from '/imports/startup/server/logger';
+import { isAllowedTo } from '/imports/startup/server/userPermissions';
+
+Meteor.publish('slides', (credentials) => {
+  // TODO: Some publishers have ACL and others dont
+  // if (!isAllowedTo('@@@', credentials)) {
+  //   this.error(new Meteor.Error(402, "The user was not authorized to subscribe for 'slides'"));
+  // }
+
+  const { meetingId, requesterUserId, requesterToken } = credentials;
+
+  check(meetingId, String);
+  check(requesterUserId, String);
+  check(requesterToken, String);
+
+  Logger.info(`Publishing Slides for ${meetingId} ${requesterUserId} ${requesterToken}`);
+
+  return Slides.find({ meetingId: meetingId });
+});
diff --git a/bigbluebutton-html5/imports/utils/mimeTypes.js b/bigbluebutton-html5/imports/utils/mimeTypes.js
new file mode 100644
index 0000000000..07bbe4b98f
--- /dev/null
+++ b/bigbluebutton-html5/imports/utils/mimeTypes.js
@@ -0,0 +1,32 @@
+export const XLS  = 'application/vnd.ms-excel';
+export const XLSX = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
+export const DOC  = 'application/msword';
+export const DOCX = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
+export const PPT  = 'application/vnd.ms-powerpoint';
+export const PPTX = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
+export const ODT  = 'application/vnd.oasis.opendocument.text';
+export const RTF  = 'application/rtf';
+export const TXT  = 'text/plain';
+export const ODS  = 'application/vnd.oasis.opendocument.spreadsheet';
+export const ODP  = 'application/vnd.oasis.opendocument.presentation';
+export const PDF  = 'application/pdf';
+export const JPEG = 'image/jpeg';
+export const PNG  = 'image/png';
+export const SVG  = 'image/svg+xml';
+
+export const UPLOAD_SUPORTED = [
+  XLS,
+  XLSX,
+  DOC,
+  DOCX,
+  PPT,
+  PPTX,
+  ODT,
+  RTF,
+  TXT,
+  ODS,
+  ODP,
+  PDF,
+  JPEG,
+  PNG,
+];
diff --git a/bigbluebutton-html5/package.json b/bigbluebutton-html5/package.json
index 2d0845199e..71379629b9 100644
--- a/bigbluebutton-html5/package.json
+++ b/bigbluebutton-html5/package.json
@@ -27,7 +27,8 @@
     "react-router": "~2.7.0",
     "redis": "^2.6.2",
     "underscore": "~1.8.3",
-    "winston": "^2.2.0"
+    "winston": "^2.2.0",
+    "probe-image-size": "~2.1.1"
   },
   "devDependencies": {
     "autoprefixer": "^6.3.6",
diff --git a/bigbluebutton-html5/server/main.js b/bigbluebutton-html5/server/main.js
index eb20f0fd16..cea0f8c7e9 100755
--- a/bigbluebutton-html5/server/main.js
+++ b/bigbluebutton-html5/server/main.js
@@ -29,7 +29,6 @@ import '/imports/api/polls/server/modifiers/updatePollCollection';
 import '/imports/api/polls/server/modifiers/eventHandlers';
 
 import '/imports/api/presentations/server/publications';
-import '/imports/api/presentations/server/methods/switchSlideMessage';
 import '/imports/api/presentations/server/modifiers/addPresentationToCollection';
 import '/imports/api/presentations/server/modifiers/clearPresentationsCollection';
 import '/imports/api/presentations/server/modifiers/removePresentationFromCollection';
@@ -42,10 +41,7 @@ import '/imports/api/shapes/server/modifiers/removeAllShapesFromSlide';
 import '/imports/api/shapes/server/modifiers/removeShapeFromSlide';
 import '/imports/api/shapes/server/modifiers/eventHandlers';
 
-import '/imports/api/slides/server/publications';
-import '/imports/api/slides/server/modifiers/addSlideToCollection';
-import '/imports/api/slides/server/modifiers/clearSlidesCollection';
-import '/imports/api/slides/server/modifiers/displayThisSlide';
+import '/imports/api/slides/server';
 
 import '/imports/api/captions/server/publications';
 import '/imports/api/captions/server/modifiers/clearCaptionsCollection';
-- 
GitLab