From ad071d4edb4e61499c434c3d202b7b8f4b1d69b6 Mon Sep 17 00:00:00 2001
From: Oleksandr Zhurbenko <xaerok.od.ua@gmail.com>
Date: Wed, 11 Oct 2017 17:52:57 -0700
Subject: [PATCH] Merged 2.0 and 1.1 annotations -> shapes api

---
 .../imports/api/1.1/shapes/index.js           |  20 +-
 .../api/1.1/shapes/server/eventHandlers.js    |  14 +-
 .../server/handlers/whiteboardCleared.js      |  20 +-
 .../server/handlers/whiteboardGetReply.js     |  38 +-
 .../shapes/server/handlers/whiteboardSend.js  |  16 +-
 .../shapes/server/handlers/whiteboardUndo.js  |  12 +-
 .../imports/api/1.1/shapes/server/index.js    |   0
 .../imports/api/1.1/shapes/server/methods.js  |   4 +
 .../shapes/server/methods/clearWhiteboard.js  |  21 +-
 .../shapes/server/methods/sendAnnotation.js   |  55 ++-
 .../shapes/server/methods/undoAnnotation.js   |  21 +-
 .../1.1/shapes/server/modifiers/addShape.js   | 392 +++++++++++++++---
 .../shapes/server/modifiers/clearShapes.js    |  34 +-
 .../server/modifiers/clearShapesWhiteboard.js |  23 -
 .../shapes/server/modifiers/removeShape.js    |  16 +-
 .../api/1.1/shapes/server/publishers.js       |  14 +-
 .../imports/api/2.0/annotations/index.js      |  19 -
 .../2.0/annotations/server/eventHandlers.js   |  10 -
 .../server/handlers/whiteboardAnnotations.js  |  22 -
 .../server/handlers/whiteboardCleared.js      |  19 -
 .../server/handlers/whiteboardSend.js         |  17 -
 .../server/handlers/whiteboardUndo.js         |  13 -
 .../api/2.0/annotations/server/index.js       |   3 -
 .../api/2.0/annotations/server/methods.js     |  10 -
 .../server/methods/clearWhiteboard.js         |  31 --
 .../server/methods/sendAnnotation.js          |  58 ---
 .../server/methods/undoAnnotation.js          |  31 --
 .../server/modifiers/addAnnotation.js         | 372 -----------------
 .../server/modifiers/clearAnnotations.js      |  36 --
 .../server/modifiers/removeAnnotation.js      |  25 --
 .../api/2.0/annotations/server/publishers.js  |  24 --
 31 files changed, 525 insertions(+), 865 deletions(-)
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/index.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/eventHandlers.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardCleared.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardGetReply.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardSend.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardUndo.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/index.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/methods.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/methods/clearWhiteboard.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/methods/sendAnnotation.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/methods/undoAnnotation.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/addShape.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/clearShapes.js
 delete mode 100644 bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/clearShapesWhiteboard.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/removeShape.js
 mode change 100644 => 100755 bigbluebutton-html5/imports/api/1.1/shapes/server/publishers.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/index.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/eventHandlers.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardAnnotations.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardCleared.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardSend.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardUndo.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/index.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/methods.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/methods/clearWhiteboard.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/methods/sendAnnotation.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/methods/undoAnnotation.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/addAnnotation.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/clearAnnotations.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/removeAnnotation.js
 delete mode 100644 bigbluebutton-html5/imports/api/2.0/annotations/server/publishers.js

diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/index.js b/bigbluebutton-html5/imports/api/1.1/shapes/index.js
old mode 100644
new mode 100755
index f15f19a892..7241f893ae
--- a/bigbluebutton-html5/imports/api/1.1/shapes/index.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/index.js
@@ -1 +1,19 @@
-export default new Mongo.Collection('shapes');
+import { Meteor } from 'meteor/meteor';
+
+const Annotations = new Mongo.Collection('annotations');
+
+if (Meteor.isServer) {
+  // types of queries for the annotations:
+  // 1. meetingId, whiteboardId
+  // 2. meetingId, whiteboardId, userId
+  // 3. meetingId, id, userId
+  // 4. meetingId, whiteboardId, id
+  // These 2 indexes seem to cover all of the cases
+  // Either mongo uses a whole or a part of the compound index
+  // Or it uses 'id' and then matches other fields
+
+  Annotations._ensureIndex({ id: 1 });
+  Annotations._ensureIndex({ meetingId: 1, whiteboardId: 1, userId: 1 });
+}
+
+export default Annotations;
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/eventHandlers.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/eventHandlers.js
old mode 100644
new mode 100755
index efe74211d8..99dbb3c799
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/eventHandlers.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/eventHandlers.js
@@ -1,10 +1,10 @@
-import RedisPubSub from '/imports/startup/server/redis';
-import handleWhiteboardGetReply from './handlers/whiteboardGetReply';
-import handleWhiteboardSend from './handlers/whiteboardSend';
+import RedisPubSub from '/imports/startup/server/redis2x';
 import handleWhiteboardCleared from './handlers/whiteboardCleared';
 import handleWhiteboardUndo from './handlers/whiteboardUndo';
+import handleWhiteboardSend from './handlers/whiteboardSend';
+import handleWhiteboardAnnotations from './handlers/whiteboardAnnotations';
 
-RedisPubSub.on('get_whiteboard_shapes_reply', handleWhiteboardGetReply);
-RedisPubSub.on('send_whiteboard_shape_message', handleWhiteboardSend);
-RedisPubSub.on('whiteboard_cleared_message', handleWhiteboardCleared);
-RedisPubSub.on('undo_whiteboard_request', handleWhiteboardUndo);
+RedisPubSub.on('ClearWhiteboardEvtMsg', handleWhiteboardCleared);
+RedisPubSub.on('UndoWhiteboardEvtMsg', handleWhiteboardUndo);
+RedisPubSub.on('SendWhiteboardAnnotationEvtMsg', handleWhiteboardSend);
+RedisPubSub.on('GetWhiteboardAnnotationsRespMsg', handleWhiteboardAnnotations);
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardCleared.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardCleared.js
old mode 100644
new mode 100755
index 52dcf8f1e3..7e6b25d151
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardCleared.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardCleared.js
@@ -1,13 +1,19 @@
 import { check } from 'meteor/check';
 
-import clearShapesWhiteboard from '../modifiers/clearShapesWhiteboard';
+import clearAnnotations from '../modifiers/clearAnnotations';
 
-export default function handleWhiteboardCleared({ payload }) {
-  const meetingId = payload.meeting_id;
-  const whiteboardId = payload.whiteboard_id;
+export default function handleWhiteboardCleared({ body }, meetingId) {
+  check(body, {
+    userId: String,
+    whiteboardId: String,
+    fullClear: Boolean,
+  });
 
-  check(meetingId, String);
-  check(whiteboardId, String);
+  const { whiteboardId, fullClear, userId } = body;
 
-  return clearShapesWhiteboard(meetingId, whiteboardId);
+  if (fullClear) {
+    return clearAnnotations(meetingId, whiteboardId);
+  }
+
+  return clearAnnotations(meetingId, whiteboardId, userId);
 }
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardGetReply.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardGetReply.js
old mode 100644
new mode 100755
index d644ad43d4..e6f99e452a
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardGetReply.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardGetReply.js
@@ -1,32 +1,22 @@
+import _ from 'lodash';
 import { check } from 'meteor/check';
-import { inReplyToHTML5Client } from '/imports/api/common/server/helpers';
-import Shapes from './../../';
-
-import addShape from '../modifiers/addShape';
-import removeShape from '../modifiers/removeShape';
-
-export default function handleWhiteboardGetReply({ payload }) {
-  if (!inReplyToHTML5Client({ payload })) {
-    return;
-  }
-
-  const meetingId = payload.meeting_id;
-  const shapes = payload.shapes;
+import clearAnnotations from '../modifiers/clearAnnotations';
+import addAnnotation from '../modifiers/addAnnotation';
 
+export default function handleWhiteboardAnnotations({ body }, meetingId) {
   check(meetingId, String);
-  check(shapes, Array);
+  check(body, Object);
 
-  const shapesIds = shapes.map(_ => _.id);
-  const shapesToRemove = Shapes.find({
-    meetingId,
-    'shape.id': { $nin: shapesIds },
-  }).fetch();
+  const { annotations, whiteboardId } = body;
 
-  shapesToRemove.forEach(s => removeShape(meetingId, s.shape.wb_id, s.shape.id));
+  check(whiteboardId, String);
+  clearAnnotations(meetingId, whiteboardId);
 
-  const shapesAdded = [];
-  shapes.forEach((shape) => {
-    const whiteboardId = shape.wb_id;
-    shapesAdded.push(addShape(meetingId, whiteboardId, shape));
+  const annotationsAdded = [];
+  _.each(annotations, (annotation) => {
+    const { wbId, userId } = annotation;
+    annotationsAdded.push(addAnnotation(meetingId, wbId, userId, annotation));
   });
+
+  return annotationsAdded;
 }
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardSend.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardSend.js
old mode 100644
new mode 100755
index 4956381e53..2ca1d84964
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardSend.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardSend.js
@@ -1,17 +1,17 @@
 import { check } from 'meteor/check';
 
-import addShape from '../modifiers/addShape';
+import addAnnotation from '../modifiers/addAnnotation';
 
-export default function handleWhiteboardSend({ payload }) {
-  const meetingId = payload.meeting_id;
-  const shape = payload.shape;
+export default function handleWhiteboardSend({ header, body }, meetingId) {
+  const userId = header.userId;
+  const annotation = body.annotation;
 
-  check(meetingId, String);
-  check(shape, Object);
+  check(userId, String);
+  check(annotation, Object);
 
-  const whiteboardId = shape.wb_id;
+  const whiteboardId = annotation.wbId;
 
   check(whiteboardId, String);
 
-  return addShape(meetingId, whiteboardId, shape);
+  return addAnnotation(meetingId, whiteboardId, userId, annotation);
 }
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardUndo.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardUndo.js
old mode 100644
new mode 100755
index e4e25a0647..3194362af3
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardUndo.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/handlers/whiteboardUndo.js
@@ -1,15 +1,13 @@
 import { check } from 'meteor/check';
 
-import removeShape from '../modifiers/removeShape';
+import removeAnnotation from '../modifiers/removeAnnotation';
 
-export default function handleWhiteboardUndo({ payload }) {
-  const meetingId = payload.meeting_id;
-  const whiteboardId = payload.whiteboard_id;
-  const shapeId = payload.shape_id;
+export default function handleWhiteboardUndo({ body }, meetingId) {
+  const whiteboardId = body.whiteboardId;
+  const shapeId = body.annotationId;
 
-  check(meetingId, String);
   check(whiteboardId, String);
   check(shapeId, String);
 
-  return removeShape(meetingId, whiteboardId, shapeId);
+  return removeAnnotation(meetingId, whiteboardId, shapeId);
 }
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/index.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/index.js
old mode 100644
new mode 100755
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/methods.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/methods.js
old mode 100644
new mode 100755
index 1edfcdbf7e..57d5c4d0d4
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/methods.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/methods.js
@@ -1,6 +1,10 @@
 import { Meteor } from 'meteor/meteor';
+import undoAnnotation from './methods/undoAnnotation';
+import clearWhiteboard from './methods/clearWhiteboard';
 import sendAnnotation from './methods/sendAnnotation';
 
 Meteor.methods({
+  undoAnnotation,
+  clearWhiteboard,
   sendAnnotation,
 });
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/clearWhiteboard.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/clearWhiteboard.js
old mode 100644
new mode 100755
index bc1587996d..406329a2e2
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/clearWhiteboard.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/clearWhiteboard.js
@@ -1,11 +1,13 @@
-import RedisPubSub from '/imports/startup/server/redis';
+import Acl from '/imports/startup/acl';
+import { getMultiUserStatus } from '/imports/api/common/server/helpers';
+import RedisPubSub from '/imports/startup/server/redis2x';
 import { Meteor } from 'meteor/meteor';
 import { check } from 'meteor/check';
 
 export default function clearWhiteboard(credentials, whiteboardId) {
   const REDIS_CONFIG = Meteor.settings.redis;
-  const CHANNEL = REDIS_CONFIG.channels.toBBBApps.whiteboard;
-  const EVENT_NAME = 'clear_whiteboard_request';
+  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+  const EVENT_NAME = 'ClearWhiteboardPubMsg';
 
   const { meetingId, requesterUserId, requesterToken } = credentials;
 
@@ -14,11 +16,16 @@ export default function clearWhiteboard(credentials, whiteboardId) {
   check(requesterToken, String);
   check(whiteboardId, String);
 
+  const allowed = Acl.can('methods.clearWhiteboard', credentials) || getMultiUserStatus(meetingId);
+  if (!allowed) {
+    throw new Meteor.Error(
+      'not-allowed', `User ${requesterUserId} is not allowed to clear the whiteboard`,
+    );
+  }
+
   const payload = {
-    requester_id: requesterUserId,
-    meeting_id: meetingId,
-    whiteboard_id: whiteboardId,
+    whiteboardId,
   };
 
-  return RedisPubSub.publish(CHANNEL, EVENT_NAME, payload);
+  return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
 }
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/sendAnnotation.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/sendAnnotation.js
old mode 100644
new mode 100755
index bda3acae4c..0a6fb16c27
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/sendAnnotation.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/sendAnnotation.js
@@ -1,23 +1,58 @@
-import RedisPubSub from '/imports/startup/server/redis';
+import Acl from '/imports/startup/acl';
+import { getMultiUserStatus } from '/imports/api/common/server/helpers';
+import RedisPubSub from '/imports/startup/server/redis2x';
 import { Meteor } from 'meteor/meteor';
 import { check } from 'meteor/check';
-import Logger from '/imports/startup/server/logger';
+import Annotations from '/imports/api/2.0/annotations';
 
-export default function sendAnnotation(credentials, payload) {
+function isLastMessage(annotation, userId) {
+  const DRAW_END = Meteor.settings.public.whiteboard.annotations.status.end;
+
+  if (annotation.status === DRAW_END) {
+    const selector = {
+      id: annotation.id,
+      userId,
+    };
+
+    const _annotation = Annotations.findOne(selector);
+    return _annotation !== null;
+  }
+
+  return false;
+}
+
+export default function sendAnnotation(credentials, annotation) {
   const REDIS_CONFIG = Meteor.settings.redis;
-  const CHANNEL = REDIS_CONFIG.channels.toBBBApps.whiteboard;
-  const EVENT_NAME = 'send_whiteboard_annotation_request';
+  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+  const EVENT_NAME = 'SendWhiteboardAnnotationPubMsg';
 
   const { meetingId, requesterUserId, requesterToken } = credentials;
 
   check(meetingId, String);
   check(requesterUserId, String);
   check(requesterToken, String);
-  check(payload, Object);
+  check(annotation, Object);
+
+  // We allow messages to pass through in 3 cases:
+  // 1. When it's a standard message in presenter mode (Acl check)
+  // 2. When it's a standard message in multi-user mode (getMultUserStatus check)
+  // 3. When it's the last message, happens when the user is currently drawing
+  // and then slide/presentation changes, the user lost presenter rights,
+  // or multi-user whiteboard gets turned off
+  // So we allow the last "DRAW_END" message to pass through, to finish the shape.
+  const allowed = Acl.can('methods.sendAnnotation', credentials) ||
+    getMultiUserStatus(meetingId) ||
+    isLastMessage(annotation, requesterUserId);
+
+  if (!allowed) {
+    throw new Meteor.Error(
+      'not-allowed', `User ${requesterUserId} is not allowed to send an annotation`,
+    );
+  }
 
-  payload.annotation.id = `${requesterUserId}-${payload.annotation.id}`;
-  payload.requester_id = requesterUserId;
-  payload.meeting_id = meetingId;
+  const payload = {
+    annotation,
+  };
 
-  return RedisPubSub.publish(CHANNEL, EVENT_NAME, payload);
+  return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
 }
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/undoAnnotation.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/undoAnnotation.js
old mode 100644
new mode 100755
index 2435344462..d7adf86b5e
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/undoAnnotation.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/methods/undoAnnotation.js
@@ -1,11 +1,13 @@
-import RedisPubSub from '/imports/startup/server/redis';
+import Acl from '/imports/startup/acl';
+import { getMultiUserStatus } from '/imports/api/common/server/helpers';
+import RedisPubSub from '/imports/startup/server/redis2x';
 import { Meteor } from 'meteor/meteor';
 import { check } from 'meteor/check';
 
 export default function undoAnnotation(credentials, whiteboardId) {
   const REDIS_CONFIG = Meteor.settings.redis;
-  const CHANNEL = REDIS_CONFIG.channels.toBBBApps.whiteboard;
-  const EVENT_NAME = 'undo_whiteboard_request';
+  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+  const EVENT_NAME = 'UndoWhiteboardPubMsg';
 
   const { meetingId, requesterUserId, requesterToken } = credentials;
 
@@ -14,11 +16,16 @@ export default function undoAnnotation(credentials, whiteboardId) {
   check(requesterToken, String);
   check(whiteboardId, String);
 
+  const allowed = Acl.can('methods.undoAnnotation', credentials) || getMultiUserStatus(meetingId);
+  if (!allowed) {
+    throw new Meteor.Error(
+      'not-allowed', `User ${requesterUserId} is not allowed to undo the annotation`,
+    );
+  }
+
   const payload = {
-    requester_id: requesterUserId,
-    meeting_id: meetingId,
-    whiteboard_id: whiteboardId,
+    whiteboardId,
   };
 
-  return RedisPubSub.publish(CHANNEL, EVENT_NAME, payload);
+  return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
 }
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/addShape.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/addShape.js
old mode 100644
new mode 100755
index 26b319aa2a..b1e8c26670
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/addShape.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/addShape.js
@@ -1,88 +1,372 @@
 import { check } from 'meteor/check';
 import Logger from '/imports/startup/server/logger';
-import Shapes from './../../';
+import Annotations from '/imports/api/2.0/annotations';
 
+const ANNOTATION_TYPE_TEXT = 'text';
+const ANNOTATION_TYPE_PENCIL = 'pencil';
 
-const SHAPE_TYPE_TEXT = 'text';
-const SHAPE_TYPE_POLL_RESULT = 'poll_result';
-
-export default function addShape(meetingId, whiteboardId, shape) {
-  check(meetingId, String);
-  check(whiteboardId, String);
-  check(shape, Object);
+// line, triangle, ellipse, rectangle
+function handleCommonAnnotation(meetingId, whiteboardId, userId, annotation) {
+  const { id, status, annotationType, annotationInfo, wbId, position } = annotation;
 
   const selector = {
     meetingId,
-    'shape.id': shape.id,
+    id,
+    userId,
   };
 
   const modifier = {
     $set: {
+      whiteboardId,
       meetingId,
+      id,
+      status,
+      annotationType,
+      annotationInfo,
+      wbId,
+      position,
+    },
+    $inc: { version: 1 },
+  };
+
+  return { selector, modifier };
+}
+
+function handleTextUpdate(meetingId, whiteboardId, userId, annotation) {
+  const { id, status, annotationType, annotationInfo, wbId, position } = annotation;
+
+  const selector = {
+    meetingId,
+    id,
+    userId,
+  };
+
+  annotationInfo.text = annotationInfo.text.replace(/[\r]/g, '\n');
+
+  const modifier = {
+    $set: {
       whiteboardId,
-      'shape.id': shape.id,
-      'shape.wb_id': shape.wb_id,
-      'shape.shape_type': shape.shape_type,
-      'shape.status': shape.status,
-      'shape.shape.type': shape.shape.type,
-      'shape.shape.status': shape.shape.status,
+      meetingId,
+      id,
+      status,
+      annotationType,
+      annotationInfo,
+      wbId,
+      position,
     },
-    $inc: { 'shape.shape.version': 1 },
+    $inc: { version: 1 },
   };
 
-  const shapeType = shape.shape_type;
-
-  switch (shapeType) {
-    case SHAPE_TYPE_TEXT:
-      modifier.$set = Object.assign(modifier.$set, {
-        'shape.shape.textBoxHeight': shape.shape.textBoxHeight,
-        'shape.shape.fontColor': shape.shape.fontColor,
-        'shape.shape.dataPoints': shape.shape.dataPoints,
-        'shape.shape.x': shape.shape.x,
-        'shape.shape.textBoxWidth': shape.shape.textBoxWidth,
-        'shape.shape.whiteboardId': shape.shape.whiteboardId,
-        'shape.shape.fontSize': shape.shape.fontSize,
-        'shape.shape.id': shape.shape.id,
-        'shape.shape.y': shape.shape.y,
-        'shape.shape.calcedFontSize': shape.shape.calcedFontSize,
-        'shape.shape.text': shape.shape.text.replace(/[\r]/g, '\n'),
+  return { selector, modifier };
+}
+
+function handlePencilUpdate(meetingId, whiteboardId, userId, annotation) {
+  // fetching annotation statuses from the config
+  const ANOTATION_STATUSES = Meteor.settings.public.whiteboard.annotations.status;
+  const DRAW_START = ANOTATION_STATUSES.start;
+  const DRAW_UPDATE = ANOTATION_STATUSES.update;
+  const DRAW_END = ANOTATION_STATUSES.end;
+
+  const SERVER_CONFIG = Meteor.settings.app;
+  const PENCIL_CHUNK_SIZE = SERVER_CONFIG.pencilChunkLength || 100;
+
+  const { id, status, annotationType, annotationInfo, wbId, position } = annotation;
+
+  const baseSelector = {
+    meetingId,
+    id,
+    userId,
+  };
+  let baseModifier;
+  let chunkSelector;
+  let chunkModifier;
+
+  // fetching the Annotation object
+  const Annotation = Annotations.findOne(baseSelector);
+
+  // a helper func, to split the initial annotation.points into subdocuments
+  // returns an array of { selector, modifier } objects for subdocuments.
+  const createPencilObjects = () => {
+    const chunks = [];
+    // if the length of the points < PENCIL_CHUNK_SIZE then we simply return an array with one chunk
+    if (annotationInfo.points.length < PENCIL_CHUNK_SIZE) {
+      const chunkId = `${id}--${1}`;
+      chunks.push({
+        selector: {
+          meetingId,
+          userId,
+          id: chunkId,
+        },
+        modifier: {
+          $set: {
+            whiteboardId,
+            meetingId,
+            id: chunkId,
+            status,
+            annotationType,
+            annotationInfo,
+            wbId,
+            position,
+          },
+          $inc: { version: 1 },
+        },
       });
-      break;
+      return chunks;
+    }
 
-    case SHAPE_TYPE_POLL_RESULT:
-      shape.shape.result = JSON.parse(shape.shape.result);
-      break;
+    // *default flow*
+    // length of the points >= PENCIL_CHUNK_SIZE, so we split them into subdocuments
 
-    default:
-      modifier.$set = Object.assign(modifier.$set, {
-        'shape.shape.points': shape.shape.points,
-        'shape.shape.whiteboardId': shape.shape.whiteboardId,
-        'shape.shape.id': shape.shape.id,
-        'shape.shape.square': shape.shape.square,
-        'shape.shape.transparency': shape.shape.transparency,
-        'shape.shape.thickness': shape.shape.thickness,
-        'shape.shape.color': shape.shape.color,
-        'shape.shape.result': shape.shape.result,
-        'shape.shape.num_respondents': shape.shape.num_respondents,
-        'shape.shape.num_responders': shape.shape.num_responders,
+    // counter is used for generating ids.
+    let i = 0;
+    let counter = 1;
+    for (; i <= annotationInfo.points.length; i += PENCIL_CHUNK_SIZE, counter += 1) {
+      const chunkId = `${id}--${counter}`;
+
+      // we always need to attach the last coordinate from the previous subdocument
+      // to the front of the current subdocument, to connect the pencil path
+      const _annotationInfo = annotationInfo;
+      _annotationInfo.points = annotationInfo.points.slice(i === 0 ? 0 : i - 2, PENCIL_CHUNK_SIZE);
+
+      chunks.push({
+        selector: {
+          meetingId,
+          userId,
+          id: chunkId,
+        },
+        modifier: {
+          $set: {
+            whiteboardId,
+            meetingId,
+            id: chunkId,
+            status,
+            annotationType,
+            annotationInfo: _annotationInfo,
+            wbId,
+            position,
+          },
+          $inc: { version: 1 },
+        },
       });
+    }
+
+    return chunks;
+  };
+
+  switch (status) {
+    case DRAW_START: {
+      // on start we split the points
+      const chunks = createPencilObjects();
+
+      // create the 'pencil_base'
+      baseModifier = {
+        id,
+        userId,
+        meetingId,
+        position,
+        annotationType: 'pencil_base',
+        numberOfChunks: chunks.length,
+        lastChunkLength: chunks[chunks.length - 1].length,
+        lastCoordinate: [
+          annotationInfo.points[annotationInfo.points.length - 2],
+          annotationInfo.points[annotationInfo.points.length - 1],
+        ],
+      };
+
+      // upserting all the chunks
+      for (let i = 0; i < chunks.length; i += 1) {
+        Annotations.upsert(chunks[i].selector, chunks[i].modifier);
+      }
+
+      // base will be updated in the main addAnnotation event
+      return { selector: baseSelector, modifier: baseModifier };
+    }
+    case DRAW_UPDATE: {
+      // checking if "pencil_base" exists
+      if (Annotation) {
+        const { numberOfChunks, lastChunkLength } = Annotation;
+
+        // if lastChunkLength < PENCIL_CHUNK_SIZE then we can simply push points to the last object
+        if (lastChunkLength < PENCIL_CHUNK_SIZE) {
+          // creating a modifier for 'pencil_base'
+          baseModifier = {
+            $set: {
+              lastChunkLength: lastChunkLength + annotation.annotationInfo.points.length,
+              lastCoordinate: [
+                annotationInfo.points[annotationInfo.points.length - 2],
+                annotationInfo.points[annotationInfo.points.length - 1],
+              ],
+            },
+          };
+
+          const chunkId = `${id}--${numberOfChunks}`;
+          chunkSelector = {
+            meetingId,
+            userId,
+            id: chunkId,
+          };
+
+          // fetching the last pencil sub-document
+          const chunk = Annotations.findOne(chunkSelector);
+          // adding the coordinates to the end of the last sub-document
+          annotationInfo.points = chunk.annotationInfo.points.concat(annotationInfo.points);
+
+          chunkModifier = {
+            $set: {
+              annotationInfo,
+            },
+            $inc: { version: 1 },
+          };
+
+        // if lastChunkLength > PENCIL_CHUNK_SIZE then we need to create another chunk
+        } else if (lastChunkLength >= PENCIL_CHUNK_SIZE) {
+          baseModifier = {
+            $set: {
+              numberOfChunks: numberOfChunks + 1,
+              lastChunkLength: annotationInfo.points.length,
+              lastCoordinate: [
+                annotationInfo.points[annotationInfo.points.length - 2],
+                annotationInfo.points[annotationInfo.points.length - 1],
+              ],
+            },
+          };
+
+          const chunkId = `${id}--${numberOfChunks + 1}`;
+          chunkSelector = {
+            meetingId,
+            userId,
+            id: chunkId,
+          };
+
+          // pushing the last coordinate to the front of the current chunk's points
+          annotationInfo.points.unshift(Annotation.lastCoordinate[0], Annotation.lastCoordinate[1]);
+
+          chunkModifier = {
+            $set: {
+              whiteboardId,
+              meetingId,
+              userId,
+              id: chunkId,
+              status,
+              annotationType,
+              annotationInfo,
+              wbId,
+              position: Annotation.position,
+            },
+            $inc: { version: 1 },
+          };
+        }
+
+        // upserting the new subdocument
+        Annotations.upsert(chunkSelector, chunkModifier);
+        // base will be updated in the main AddAnnotation func
+        return { selector: baseSelector, modifier: baseModifier };
+      }
+
+      // **default flow**
+      // if we are here then it means that Annotation object is not in the db
+      // So creating everything similar to DRAW_START case
+      const _chunks = createPencilObjects();
+
+      // creating 'pencil_base' based on the info we received from createPencilObjects()
+      baseModifier = {
+        id,
+        userId,
+        meetingId,
+        position,
+        annotationType: 'pencil_base',
+        numberOfChunks: _chunks.length,
+        lastChunkLength: _chunks[_chunks.length - 1].length,
+        lastCoordinate: [
+          annotationInfo.points[annotationInfo.points.length - 2],
+          annotationInfo.points[annotationInfo.points.length - 1],
+        ],
+      };
+
+      // upserting all the chunks
+      for (let i = 0; i < _chunks.length; i += 1) {
+        Annotations.upsert(_chunks[i].selector, _chunks[i].modifier);
+      }
+
+      // base will be updated in the main AddAnnotation func
+      return { selector: baseSelector, modifier: baseModifier };
+    }
+    case DRAW_END: {
+      // If a user just finished drawing with the pencil
+      // Removing all the sub-documents and replacing the 'pencil_base'
+      if (Annotation && Annotation.annotationType === 'pencil_base') {
+        // delete everything and replace base
+        const chunkIds = [];
+        for (let i = 0; i <= Annotation.numberOfChunks; i += 1) {
+          chunkIds.push(`${Annotation.id}--${i}`);
+        }
+        chunkSelector = {
+          meetingId,
+          userId,
+          id: { $in: chunkIds },
+        };
+
+        Annotations.remove(chunkSelector);
+      }
+
+      // Updating the main pencil object with the final info
+      baseModifier = {
+        $set: {
+          whiteboardId,
+          meetingId,
+          id,
+          status,
+          annotationType,
+          annotationInfo,
+          wbId,
+          position,
+        },
+        $inc: { version: 1 },
+        $unset: {
+          numberOfChunks: '',
+          lastChunkLength: '',
+          lastCoordinate: '',
+        },
+      };
+      return { selector: baseSelector, modifier: baseModifier };
+    }
+    default: {
+      return {};
+    }
+  }
+}
+
+export default function addAnnotation(meetingId, whiteboardId, userId, annotation) {
+  check(meetingId, String);
+  check(whiteboardId, String);
+  check(annotation, Object);
+
+  let query;
+
+  switch (annotation.annotationType) {
+    case ANNOTATION_TYPE_TEXT:
+      query = handleTextUpdate(meetingId, whiteboardId, userId, annotation);
+      break;
+    case ANNOTATION_TYPE_PENCIL:
+      query = handlePencilUpdate(meetingId, whiteboardId, userId, annotation);
+      break;
+    default:
+      query = handleCommonAnnotation(meetingId, whiteboardId, userId, annotation);
       break;
   }
 
   const cb = (err, numChanged) => {
     if (err) {
-      return Logger.error(`Adding shape to collection: ${err}`);
+      return Logger.error(`Adding annotation2x to collection: ${err}`);
     }
 
     const { insertedId } = numChanged;
     if (insertedId) {
-      return Logger.info(`Added shape id=${shape.id} whiteboard=${whiteboardId}`);
+      return Logger.info(`Added annotation2x id=${annotation.id} whiteboard=${whiteboardId}`);
     }
 
-    if (numChanged) {
-      return Logger.info(`Upserted shape id=${shape.id} whiteboard=${whiteboardId}`);
-    }
+    return Logger.info(`Upserted annotation2x id=${annotation.id} whiteboard=${whiteboardId}`);
   };
 
-  return Shapes.upsert(selector, modifier, cb);
+  return Annotations.upsert(query.selector, query.modifier, cb);
 }
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/clearShapes.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/clearShapes.js
old mode 100644
new mode 100755
index e6b1442f0d..5917be30cd
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/clearShapes.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/clearShapes.js
@@ -1,10 +1,36 @@
+import Annotations from '/imports/api/2.0/annotations';
 import Logger from '/imports/startup/server/logger';
-import Shapes from './../../';
 
-export default function clearShapes(meetingId) {
+export default function clearAnnotations(meetingId, whiteboardId, userId) {
+  const selector = {};
+
   if (meetingId) {
-    return Shapes.remove({ meetingId }, Logger.info(`Cleared Shapes (${meetingId})`));
+    selector.meetingId = meetingId;
+  }
+
+  if (whiteboardId) {
+    selector.whiteboardId = whiteboardId;
+  }
+
+  if (userId) {
+    selector.userId = userId;
   }
 
-  return Shapes.remove({}, Logger.info('Cleared Shapes (all)'));
+  const cb = (err) => {
+    if (err) {
+      return Logger.error(`Removing Shapes2x from collection: ${err}`);
+    }
+
+    if (!meetingId) {
+      return Logger.info('Cleared Annotations (all)');
+    }
+
+    if (userId) {
+      return Logger.info(`Removed Shapes2x for userId=${userId} where whiteboard=${whiteboardId}`);
+    }
+
+    return Logger.info(`Removed Shapes2x where whiteboard=${whiteboardId}`);
+  };
+
+  return Annotations.remove(selector, cb);
 }
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/clearShapesWhiteboard.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/clearShapesWhiteboard.js
deleted file mode 100644
index efaf0bf5f6..0000000000
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/clearShapesWhiteboard.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { check } from 'meteor/check';
-import Logger from '/imports/startup/server/logger';
-import Shapes from './../../';
-
-export default function clearShapesWhiteboard(meetingId, whiteboardId) {
-  check(meetingId, String);
-  check(whiteboardId, String);
-
-  const selector = {
-    meetingId,
-    whiteboardId,
-  };
-
-  const cb = (err) => {
-    if (err) {
-      return Logger.error(`Removing Shapes from collection: ${err}`);
-    }
-
-    return Logger.info(`Removed Shapes where whiteboard=${whiteboardId}`);
-  };
-
-  return Shapes.remove(selector, cb);
-}
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/removeShape.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/removeShape.js
old mode 100644
new mode 100755
index 16dbdbd594..04a8ec71ab
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/removeShape.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/modifiers/removeShape.js
@@ -1,8 +1,8 @@
 import { check } from 'meteor/check';
+import Annotations from '/imports/api/2.0/annotations';
 import Logger from '/imports/startup/server/logger';
-import Shapes from './../../';
 
-export default function removeShape(meetingId, whiteboardId, shapeId) {
+export default function removeAnnotation(meetingId, whiteboardId, shapeId) {
   check(meetingId, String);
   check(whiteboardId, String);
   check(shapeId, String);
@@ -10,18 +10,16 @@ export default function removeShape(meetingId, whiteboardId, shapeId) {
   const selector = {
     meetingId,
     whiteboardId,
-    'shape.id': shapeId,
+    id: shapeId,
   };
 
-  const cb = (err, numChanged) => {
+  const cb = (err) => {
     if (err) {
-      return Logger.error(`Removing shape from collection: ${err}`);
+      return Logger.error(`Removing annotation from collection: ${err}`);
     }
 
-    if (numChanged) {
-      return Logger.info(`Removed shape id=${shapeId} whiteboard=${whiteboardId}`);
-    }
+    return Logger.info(`Removed annotation id=${shapeId} whiteboard=${whiteboardId}`);
   };
 
-  return Shapes.remove(selector, cb);
+  return Annotations.remove(selector, cb);
 }
diff --git a/bigbluebutton-html5/imports/api/1.1/shapes/server/publishers.js b/bigbluebutton-html5/imports/api/1.1/shapes/server/publishers.js
old mode 100644
new mode 100755
index c4029f2dbe..429497ed52
--- a/bigbluebutton-html5/imports/api/1.1/shapes/server/publishers.js
+++ b/bigbluebutton-html5/imports/api/1.1/shapes/server/publishers.js
@@ -1,24 +1,24 @@
+import Annotations from '/imports/api/2.0/annotations';
 import { Meteor } from 'meteor/meteor';
 import { check } from 'meteor/check';
 import Logger from '/imports/startup/server/logger';
 import mapToAcl from '/imports/startup/mapToAcl';
-import Shapes from './../';
 
-function shapes(credentials) {
+function annotations(credentials) {
   const { meetingId, requesterUserId, requesterToken } = credentials;
 
   check(meetingId, String);
   check(requesterUserId, String);
   check(requesterToken, String);
 
-  Logger.info(`Publishing Shapes for ${meetingId} ${requesterUserId} ${requesterToken}`);
+  Logger.info(`Publishing Annotations2x for ${meetingId} ${requesterUserId} ${requesterToken}`);
 
-  return Shapes.find({ meetingId });
+  return Annotations.find({ meetingId });
 }
 
 function publish(...args) {
-  const boundShapes = shapes.bind(this);
-  return mapToAcl('subscriptions.shapes', boundShapes)(args);
+  const boundAnnotations = annotations.bind(this);
+  return mapToAcl('subscriptions.annotations', boundAnnotations)(args);
 }
 
-Meteor.publish('shapes', publish);
+Meteor.publish('annotations', publish);
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/index.js b/bigbluebutton-html5/imports/api/2.0/annotations/index.js
deleted file mode 100644
index 7241f893ae..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/index.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-
-const Annotations = new Mongo.Collection('annotations');
-
-if (Meteor.isServer) {
-  // types of queries for the annotations:
-  // 1. meetingId, whiteboardId
-  // 2. meetingId, whiteboardId, userId
-  // 3. meetingId, id, userId
-  // 4. meetingId, whiteboardId, id
-  // These 2 indexes seem to cover all of the cases
-  // Either mongo uses a whole or a part of the compound index
-  // Or it uses 'id' and then matches other fields
-
-  Annotations._ensureIndex({ id: 1 });
-  Annotations._ensureIndex({ meetingId: 1, whiteboardId: 1, userId: 1 });
-}
-
-export default Annotations;
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/eventHandlers.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/eventHandlers.js
deleted file mode 100644
index 99dbb3c799..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/eventHandlers.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import RedisPubSub from '/imports/startup/server/redis2x';
-import handleWhiteboardCleared from './handlers/whiteboardCleared';
-import handleWhiteboardUndo from './handlers/whiteboardUndo';
-import handleWhiteboardSend from './handlers/whiteboardSend';
-import handleWhiteboardAnnotations from './handlers/whiteboardAnnotations';
-
-RedisPubSub.on('ClearWhiteboardEvtMsg', handleWhiteboardCleared);
-RedisPubSub.on('UndoWhiteboardEvtMsg', handleWhiteboardUndo);
-RedisPubSub.on('SendWhiteboardAnnotationEvtMsg', handleWhiteboardSend);
-RedisPubSub.on('GetWhiteboardAnnotationsRespMsg', handleWhiteboardAnnotations);
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardAnnotations.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardAnnotations.js
deleted file mode 100644
index e6f99e452a..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardAnnotations.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import _ from 'lodash';
-import { check } from 'meteor/check';
-import clearAnnotations from '../modifiers/clearAnnotations';
-import addAnnotation from '../modifiers/addAnnotation';
-
-export default function handleWhiteboardAnnotations({ body }, meetingId) {
-  check(meetingId, String);
-  check(body, Object);
-
-  const { annotations, whiteboardId } = body;
-
-  check(whiteboardId, String);
-  clearAnnotations(meetingId, whiteboardId);
-
-  const annotationsAdded = [];
-  _.each(annotations, (annotation) => {
-    const { wbId, userId } = annotation;
-    annotationsAdded.push(addAnnotation(meetingId, wbId, userId, annotation));
-  });
-
-  return annotationsAdded;
-}
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardCleared.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardCleared.js
deleted file mode 100644
index 7e6b25d151..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardCleared.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { check } from 'meteor/check';
-
-import clearAnnotations from '../modifiers/clearAnnotations';
-
-export default function handleWhiteboardCleared({ body }, meetingId) {
-  check(body, {
-    userId: String,
-    whiteboardId: String,
-    fullClear: Boolean,
-  });
-
-  const { whiteboardId, fullClear, userId } = body;
-
-  if (fullClear) {
-    return clearAnnotations(meetingId, whiteboardId);
-  }
-
-  return clearAnnotations(meetingId, whiteboardId, userId);
-}
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardSend.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardSend.js
deleted file mode 100644
index 2ca1d84964..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardSend.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { check } from 'meteor/check';
-
-import addAnnotation from '../modifiers/addAnnotation';
-
-export default function handleWhiteboardSend({ header, body }, meetingId) {
-  const userId = header.userId;
-  const annotation = body.annotation;
-
-  check(userId, String);
-  check(annotation, Object);
-
-  const whiteboardId = annotation.wbId;
-
-  check(whiteboardId, String);
-
-  return addAnnotation(meetingId, whiteboardId, userId, annotation);
-}
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardUndo.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardUndo.js
deleted file mode 100644
index 3194362af3..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/handlers/whiteboardUndo.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { check } from 'meteor/check';
-
-import removeAnnotation from '../modifiers/removeAnnotation';
-
-export default function handleWhiteboardUndo({ body }, meetingId) {
-  const whiteboardId = body.whiteboardId;
-  const shapeId = body.annotationId;
-
-  check(whiteboardId, String);
-  check(shapeId, String);
-
-  return removeAnnotation(meetingId, whiteboardId, shapeId);
-}
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/index.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/index.js
deleted file mode 100644
index 92451ac76b..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import './eventHandlers';
-import './methods';
-import './publishers';
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/methods.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/methods.js
deleted file mode 100644
index 57d5c4d0d4..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/methods.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import undoAnnotation from './methods/undoAnnotation';
-import clearWhiteboard from './methods/clearWhiteboard';
-import sendAnnotation from './methods/sendAnnotation';
-
-Meteor.methods({
-  undoAnnotation,
-  clearWhiteboard,
-  sendAnnotation,
-});
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/methods/clearWhiteboard.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/methods/clearWhiteboard.js
deleted file mode 100644
index 406329a2e2..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/methods/clearWhiteboard.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import Acl from '/imports/startup/acl';
-import { getMultiUserStatus } from '/imports/api/common/server/helpers';
-import RedisPubSub from '/imports/startup/server/redis2x';
-import { Meteor } from 'meteor/meteor';
-import { check } from 'meteor/check';
-
-export default function clearWhiteboard(credentials, whiteboardId) {
-  const REDIS_CONFIG = Meteor.settings.redis;
-  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
-  const EVENT_NAME = 'ClearWhiteboardPubMsg';
-
-  const { meetingId, requesterUserId, requesterToken } = credentials;
-
-  check(meetingId, String);
-  check(requesterUserId, String);
-  check(requesterToken, String);
-  check(whiteboardId, String);
-
-  const allowed = Acl.can('methods.clearWhiteboard', credentials) || getMultiUserStatus(meetingId);
-  if (!allowed) {
-    throw new Meteor.Error(
-      'not-allowed', `User ${requesterUserId} is not allowed to clear the whiteboard`,
-    );
-  }
-
-  const payload = {
-    whiteboardId,
-  };
-
-  return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
-}
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/methods/sendAnnotation.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/methods/sendAnnotation.js
deleted file mode 100644
index 0a6fb16c27..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/methods/sendAnnotation.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import Acl from '/imports/startup/acl';
-import { getMultiUserStatus } from '/imports/api/common/server/helpers';
-import RedisPubSub from '/imports/startup/server/redis2x';
-import { Meteor } from 'meteor/meteor';
-import { check } from 'meteor/check';
-import Annotations from '/imports/api/2.0/annotations';
-
-function isLastMessage(annotation, userId) {
-  const DRAW_END = Meteor.settings.public.whiteboard.annotations.status.end;
-
-  if (annotation.status === DRAW_END) {
-    const selector = {
-      id: annotation.id,
-      userId,
-    };
-
-    const _annotation = Annotations.findOne(selector);
-    return _annotation !== null;
-  }
-
-  return false;
-}
-
-export default function sendAnnotation(credentials, annotation) {
-  const REDIS_CONFIG = Meteor.settings.redis;
-  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
-  const EVENT_NAME = 'SendWhiteboardAnnotationPubMsg';
-
-  const { meetingId, requesterUserId, requesterToken } = credentials;
-
-  check(meetingId, String);
-  check(requesterUserId, String);
-  check(requesterToken, String);
-  check(annotation, Object);
-
-  // We allow messages to pass through in 3 cases:
-  // 1. When it's a standard message in presenter mode (Acl check)
-  // 2. When it's a standard message in multi-user mode (getMultUserStatus check)
-  // 3. When it's the last message, happens when the user is currently drawing
-  // and then slide/presentation changes, the user lost presenter rights,
-  // or multi-user whiteboard gets turned off
-  // So we allow the last "DRAW_END" message to pass through, to finish the shape.
-  const allowed = Acl.can('methods.sendAnnotation', credentials) ||
-    getMultiUserStatus(meetingId) ||
-    isLastMessage(annotation, requesterUserId);
-
-  if (!allowed) {
-    throw new Meteor.Error(
-      'not-allowed', `User ${requesterUserId} is not allowed to send an annotation`,
-    );
-  }
-
-  const payload = {
-    annotation,
-  };
-
-  return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
-}
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/methods/undoAnnotation.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/methods/undoAnnotation.js
deleted file mode 100644
index d7adf86b5e..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/methods/undoAnnotation.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import Acl from '/imports/startup/acl';
-import { getMultiUserStatus } from '/imports/api/common/server/helpers';
-import RedisPubSub from '/imports/startup/server/redis2x';
-import { Meteor } from 'meteor/meteor';
-import { check } from 'meteor/check';
-
-export default function undoAnnotation(credentials, whiteboardId) {
-  const REDIS_CONFIG = Meteor.settings.redis;
-  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
-  const EVENT_NAME = 'UndoWhiteboardPubMsg';
-
-  const { meetingId, requesterUserId, requesterToken } = credentials;
-
-  check(meetingId, String);
-  check(requesterUserId, String);
-  check(requesterToken, String);
-  check(whiteboardId, String);
-
-  const allowed = Acl.can('methods.undoAnnotation', credentials) || getMultiUserStatus(meetingId);
-  if (!allowed) {
-    throw new Meteor.Error(
-      'not-allowed', `User ${requesterUserId} is not allowed to undo the annotation`,
-    );
-  }
-
-  const payload = {
-    whiteboardId,
-  };
-
-  return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
-}
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/addAnnotation.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/addAnnotation.js
deleted file mode 100644
index b1e8c26670..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/addAnnotation.js
+++ /dev/null
@@ -1,372 +0,0 @@
-import { check } from 'meteor/check';
-import Logger from '/imports/startup/server/logger';
-import Annotations from '/imports/api/2.0/annotations';
-
-const ANNOTATION_TYPE_TEXT = 'text';
-const ANNOTATION_TYPE_PENCIL = 'pencil';
-
-// line, triangle, ellipse, rectangle
-function handleCommonAnnotation(meetingId, whiteboardId, userId, annotation) {
-  const { id, status, annotationType, annotationInfo, wbId, position } = annotation;
-
-  const selector = {
-    meetingId,
-    id,
-    userId,
-  };
-
-  const modifier = {
-    $set: {
-      whiteboardId,
-      meetingId,
-      id,
-      status,
-      annotationType,
-      annotationInfo,
-      wbId,
-      position,
-    },
-    $inc: { version: 1 },
-  };
-
-  return { selector, modifier };
-}
-
-function handleTextUpdate(meetingId, whiteboardId, userId, annotation) {
-  const { id, status, annotationType, annotationInfo, wbId, position } = annotation;
-
-  const selector = {
-    meetingId,
-    id,
-    userId,
-  };
-
-  annotationInfo.text = annotationInfo.text.replace(/[\r]/g, '\n');
-
-  const modifier = {
-    $set: {
-      whiteboardId,
-      meetingId,
-      id,
-      status,
-      annotationType,
-      annotationInfo,
-      wbId,
-      position,
-    },
-    $inc: { version: 1 },
-  };
-
-  return { selector, modifier };
-}
-
-function handlePencilUpdate(meetingId, whiteboardId, userId, annotation) {
-  // fetching annotation statuses from the config
-  const ANOTATION_STATUSES = Meteor.settings.public.whiteboard.annotations.status;
-  const DRAW_START = ANOTATION_STATUSES.start;
-  const DRAW_UPDATE = ANOTATION_STATUSES.update;
-  const DRAW_END = ANOTATION_STATUSES.end;
-
-  const SERVER_CONFIG = Meteor.settings.app;
-  const PENCIL_CHUNK_SIZE = SERVER_CONFIG.pencilChunkLength || 100;
-
-  const { id, status, annotationType, annotationInfo, wbId, position } = annotation;
-
-  const baseSelector = {
-    meetingId,
-    id,
-    userId,
-  };
-  let baseModifier;
-  let chunkSelector;
-  let chunkModifier;
-
-  // fetching the Annotation object
-  const Annotation = Annotations.findOne(baseSelector);
-
-  // a helper func, to split the initial annotation.points into subdocuments
-  // returns an array of { selector, modifier } objects for subdocuments.
-  const createPencilObjects = () => {
-    const chunks = [];
-    // if the length of the points < PENCIL_CHUNK_SIZE then we simply return an array with one chunk
-    if (annotationInfo.points.length < PENCIL_CHUNK_SIZE) {
-      const chunkId = `${id}--${1}`;
-      chunks.push({
-        selector: {
-          meetingId,
-          userId,
-          id: chunkId,
-        },
-        modifier: {
-          $set: {
-            whiteboardId,
-            meetingId,
-            id: chunkId,
-            status,
-            annotationType,
-            annotationInfo,
-            wbId,
-            position,
-          },
-          $inc: { version: 1 },
-        },
-      });
-      return chunks;
-    }
-
-    // *default flow*
-    // length of the points >= PENCIL_CHUNK_SIZE, so we split them into subdocuments
-
-    // counter is used for generating ids.
-    let i = 0;
-    let counter = 1;
-    for (; i <= annotationInfo.points.length; i += PENCIL_CHUNK_SIZE, counter += 1) {
-      const chunkId = `${id}--${counter}`;
-
-      // we always need to attach the last coordinate from the previous subdocument
-      // to the front of the current subdocument, to connect the pencil path
-      const _annotationInfo = annotationInfo;
-      _annotationInfo.points = annotationInfo.points.slice(i === 0 ? 0 : i - 2, PENCIL_CHUNK_SIZE);
-
-      chunks.push({
-        selector: {
-          meetingId,
-          userId,
-          id: chunkId,
-        },
-        modifier: {
-          $set: {
-            whiteboardId,
-            meetingId,
-            id: chunkId,
-            status,
-            annotationType,
-            annotationInfo: _annotationInfo,
-            wbId,
-            position,
-          },
-          $inc: { version: 1 },
-        },
-      });
-    }
-
-    return chunks;
-  };
-
-  switch (status) {
-    case DRAW_START: {
-      // on start we split the points
-      const chunks = createPencilObjects();
-
-      // create the 'pencil_base'
-      baseModifier = {
-        id,
-        userId,
-        meetingId,
-        position,
-        annotationType: 'pencil_base',
-        numberOfChunks: chunks.length,
-        lastChunkLength: chunks[chunks.length - 1].length,
-        lastCoordinate: [
-          annotationInfo.points[annotationInfo.points.length - 2],
-          annotationInfo.points[annotationInfo.points.length - 1],
-        ],
-      };
-
-      // upserting all the chunks
-      for (let i = 0; i < chunks.length; i += 1) {
-        Annotations.upsert(chunks[i].selector, chunks[i].modifier);
-      }
-
-      // base will be updated in the main addAnnotation event
-      return { selector: baseSelector, modifier: baseModifier };
-    }
-    case DRAW_UPDATE: {
-      // checking if "pencil_base" exists
-      if (Annotation) {
-        const { numberOfChunks, lastChunkLength } = Annotation;
-
-        // if lastChunkLength < PENCIL_CHUNK_SIZE then we can simply push points to the last object
-        if (lastChunkLength < PENCIL_CHUNK_SIZE) {
-          // creating a modifier for 'pencil_base'
-          baseModifier = {
-            $set: {
-              lastChunkLength: lastChunkLength + annotation.annotationInfo.points.length,
-              lastCoordinate: [
-                annotationInfo.points[annotationInfo.points.length - 2],
-                annotationInfo.points[annotationInfo.points.length - 1],
-              ],
-            },
-          };
-
-          const chunkId = `${id}--${numberOfChunks}`;
-          chunkSelector = {
-            meetingId,
-            userId,
-            id: chunkId,
-          };
-
-          // fetching the last pencil sub-document
-          const chunk = Annotations.findOne(chunkSelector);
-          // adding the coordinates to the end of the last sub-document
-          annotationInfo.points = chunk.annotationInfo.points.concat(annotationInfo.points);
-
-          chunkModifier = {
-            $set: {
-              annotationInfo,
-            },
-            $inc: { version: 1 },
-          };
-
-        // if lastChunkLength > PENCIL_CHUNK_SIZE then we need to create another chunk
-        } else if (lastChunkLength >= PENCIL_CHUNK_SIZE) {
-          baseModifier = {
-            $set: {
-              numberOfChunks: numberOfChunks + 1,
-              lastChunkLength: annotationInfo.points.length,
-              lastCoordinate: [
-                annotationInfo.points[annotationInfo.points.length - 2],
-                annotationInfo.points[annotationInfo.points.length - 1],
-              ],
-            },
-          };
-
-          const chunkId = `${id}--${numberOfChunks + 1}`;
-          chunkSelector = {
-            meetingId,
-            userId,
-            id: chunkId,
-          };
-
-          // pushing the last coordinate to the front of the current chunk's points
-          annotationInfo.points.unshift(Annotation.lastCoordinate[0], Annotation.lastCoordinate[1]);
-
-          chunkModifier = {
-            $set: {
-              whiteboardId,
-              meetingId,
-              userId,
-              id: chunkId,
-              status,
-              annotationType,
-              annotationInfo,
-              wbId,
-              position: Annotation.position,
-            },
-            $inc: { version: 1 },
-          };
-        }
-
-        // upserting the new subdocument
-        Annotations.upsert(chunkSelector, chunkModifier);
-        // base will be updated in the main AddAnnotation func
-        return { selector: baseSelector, modifier: baseModifier };
-      }
-
-      // **default flow**
-      // if we are here then it means that Annotation object is not in the db
-      // So creating everything similar to DRAW_START case
-      const _chunks = createPencilObjects();
-
-      // creating 'pencil_base' based on the info we received from createPencilObjects()
-      baseModifier = {
-        id,
-        userId,
-        meetingId,
-        position,
-        annotationType: 'pencil_base',
-        numberOfChunks: _chunks.length,
-        lastChunkLength: _chunks[_chunks.length - 1].length,
-        lastCoordinate: [
-          annotationInfo.points[annotationInfo.points.length - 2],
-          annotationInfo.points[annotationInfo.points.length - 1],
-        ],
-      };
-
-      // upserting all the chunks
-      for (let i = 0; i < _chunks.length; i += 1) {
-        Annotations.upsert(_chunks[i].selector, _chunks[i].modifier);
-      }
-
-      // base will be updated in the main AddAnnotation func
-      return { selector: baseSelector, modifier: baseModifier };
-    }
-    case DRAW_END: {
-      // If a user just finished drawing with the pencil
-      // Removing all the sub-documents and replacing the 'pencil_base'
-      if (Annotation && Annotation.annotationType === 'pencil_base') {
-        // delete everything and replace base
-        const chunkIds = [];
-        for (let i = 0; i <= Annotation.numberOfChunks; i += 1) {
-          chunkIds.push(`${Annotation.id}--${i}`);
-        }
-        chunkSelector = {
-          meetingId,
-          userId,
-          id: { $in: chunkIds },
-        };
-
-        Annotations.remove(chunkSelector);
-      }
-
-      // Updating the main pencil object with the final info
-      baseModifier = {
-        $set: {
-          whiteboardId,
-          meetingId,
-          id,
-          status,
-          annotationType,
-          annotationInfo,
-          wbId,
-          position,
-        },
-        $inc: { version: 1 },
-        $unset: {
-          numberOfChunks: '',
-          lastChunkLength: '',
-          lastCoordinate: '',
-        },
-      };
-      return { selector: baseSelector, modifier: baseModifier };
-    }
-    default: {
-      return {};
-    }
-  }
-}
-
-export default function addAnnotation(meetingId, whiteboardId, userId, annotation) {
-  check(meetingId, String);
-  check(whiteboardId, String);
-  check(annotation, Object);
-
-  let query;
-
-  switch (annotation.annotationType) {
-    case ANNOTATION_TYPE_TEXT:
-      query = handleTextUpdate(meetingId, whiteboardId, userId, annotation);
-      break;
-    case ANNOTATION_TYPE_PENCIL:
-      query = handlePencilUpdate(meetingId, whiteboardId, userId, annotation);
-      break;
-    default:
-      query = handleCommonAnnotation(meetingId, whiteboardId, userId, annotation);
-      break;
-  }
-
-  const cb = (err, numChanged) => {
-    if (err) {
-      return Logger.error(`Adding annotation2x to collection: ${err}`);
-    }
-
-    const { insertedId } = numChanged;
-    if (insertedId) {
-      return Logger.info(`Added annotation2x id=${annotation.id} whiteboard=${whiteboardId}`);
-    }
-
-    return Logger.info(`Upserted annotation2x id=${annotation.id} whiteboard=${whiteboardId}`);
-  };
-
-  return Annotations.upsert(query.selector, query.modifier, cb);
-}
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/clearAnnotations.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/clearAnnotations.js
deleted file mode 100644
index 5917be30cd..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/clearAnnotations.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import Annotations from '/imports/api/2.0/annotations';
-import Logger from '/imports/startup/server/logger';
-
-export default function clearAnnotations(meetingId, whiteboardId, userId) {
-  const selector = {};
-
-  if (meetingId) {
-    selector.meetingId = meetingId;
-  }
-
-  if (whiteboardId) {
-    selector.whiteboardId = whiteboardId;
-  }
-
-  if (userId) {
-    selector.userId = userId;
-  }
-
-  const cb = (err) => {
-    if (err) {
-      return Logger.error(`Removing Shapes2x from collection: ${err}`);
-    }
-
-    if (!meetingId) {
-      return Logger.info('Cleared Annotations (all)');
-    }
-
-    if (userId) {
-      return Logger.info(`Removed Shapes2x for userId=${userId} where whiteboard=${whiteboardId}`);
-    }
-
-    return Logger.info(`Removed Shapes2x where whiteboard=${whiteboardId}`);
-  };
-
-  return Annotations.remove(selector, cb);
-}
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/removeAnnotation.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/removeAnnotation.js
deleted file mode 100644
index 04a8ec71ab..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/modifiers/removeAnnotation.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { check } from 'meteor/check';
-import Annotations from '/imports/api/2.0/annotations';
-import Logger from '/imports/startup/server/logger';
-
-export default function removeAnnotation(meetingId, whiteboardId, shapeId) {
-  check(meetingId, String);
-  check(whiteboardId, String);
-  check(shapeId, String);
-
-  const selector = {
-    meetingId,
-    whiteboardId,
-    id: shapeId,
-  };
-
-  const cb = (err) => {
-    if (err) {
-      return Logger.error(`Removing annotation from collection: ${err}`);
-    }
-
-    return Logger.info(`Removed annotation id=${shapeId} whiteboard=${whiteboardId}`);
-  };
-
-  return Annotations.remove(selector, cb);
-}
diff --git a/bigbluebutton-html5/imports/api/2.0/annotations/server/publishers.js b/bigbluebutton-html5/imports/api/2.0/annotations/server/publishers.js
deleted file mode 100644
index 429497ed52..0000000000
--- a/bigbluebutton-html5/imports/api/2.0/annotations/server/publishers.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import Annotations from '/imports/api/2.0/annotations';
-import { Meteor } from 'meteor/meteor';
-import { check } from 'meteor/check';
-import Logger from '/imports/startup/server/logger';
-import mapToAcl from '/imports/startup/mapToAcl';
-
-function annotations(credentials) {
-  const { meetingId, requesterUserId, requesterToken } = credentials;
-
-  check(meetingId, String);
-  check(requesterUserId, String);
-  check(requesterToken, String);
-
-  Logger.info(`Publishing Annotations2x for ${meetingId} ${requesterUserId} ${requesterToken}`);
-
-  return Annotations.find({ meetingId });
-}
-
-function publish(...args) {
-  const boundAnnotations = annotations.bind(this);
-  return mapToAcl('subscriptions.annotations', boundAnnotations)(args);
-}
-
-Meteor.publish('annotations', publish);
-- 
GitLab