Bladeren bron

Prettier & eslint project style update

Justin Reynolds 6 jaren geleden
bovenliggende
commit
3eb4d2c341
100 gewijzigde bestanden met toevoegingen van 5194 en 4413 verwijderingen
  1. 5 8
      .eslintrc.json
  2. 90 58
      client/components/activities/activities.js
  3. 41 35
      client/components/activities/comments.js
  4. 36 27
      client/components/boards/boardArchive.js
  5. 83 55
      client/components/boards/boardBody.js
  6. 119 87
      client/components/boards/boardHeader.js
  7. 50 44
      client/components/boards/boardsList.js
  8. 19 20
      client/components/cards/attachments.js
  9. 51 44
      client/components/cards/cardCustomFields.js
  10. 95 91
      client/components/cards/cardDate.js
  11. 221 168
      client/components/cards/cardDetails.js
  12. 37 25
      client/components/cards/cardTime.js
  13. 45 25
      client/components/cards/checklists.js
  14. 25 17
      client/components/cards/labels.js
  15. 8 7
      client/components/cards/minicard.js
  16. 37 19
      client/components/cards/subtasks.js
  17. 47 38
      client/components/import/import.js
  18. 1 1
      client/components/import/trelloMembersMapper.js
  19. 2 2
      client/components/import/wekanMembersMapper.js
  20. 32 10
      client/components/lists/list.js
  21. 317 229
      client/components/lists/listBody.js
  22. 87 55
      client/components/lists/listHeader.js
  23. 60 51
      client/components/main/editor.js
  24. 2 2
      client/components/main/header.js
  25. 40 32
      client/components/main/layouts.js
  26. 11 9
      client/components/mixins/infiniteScrolling.js
  27. 136 139
      client/components/rules/actions/boardActions.js
  28. 144 141
      client/components/rules/actions/cardActions.js
  29. 130 131
      client/components/rules/actions/checklistActions.js
  30. 28 29
      client/components/rules/actions/mailActions.js
  31. 0 2
      client/components/rules/ruleDetails.js
  32. 15 13
      client/components/rules/rulesActions.js
  33. 70 66
      client/components/rules/rulesMain.js
  34. 12 10
      client/components/rules/rulesTriggers.js
  35. 92 90
      client/components/rules/triggers/boardTriggers.js
  36. 114 112
      client/components/rules/triggers/cardTriggers.js
  37. 138 137
      client/components/rules/triggers/checklistTriggers.js
  38. 7 3
      client/components/settings/connectionMethod.js
  39. 69 51
      client/components/settings/peopleBody.js
  40. 90 53
      client/components/settings/settingBody.js
  41. 192 131
      client/components/sidebar/sidebar.js
  42. 70 63
      client/components/sidebar/sidebarArchives.js
  43. 115 100
      client/components/sidebar/sidebarCustomFields.js
  44. 78 76
      client/components/sidebar/sidebarFilters.js
  45. 7 5
      client/components/sidebar/sidebarSearches.js
  46. 62 48
      client/components/swimlanes/swimlaneHeader.js
  47. 101 62
      client/components/swimlanes/swimlanes.js
  48. 47 48
      client/components/users/userAvatar.js
  49. 48 36
      client/components/users/userHeader.js
  50. 1 1
      client/config/blazeHelpers.js
  51. 1 4
      client/lib/accessibility.js
  52. 10 10
      client/lib/cssEvents.js
  53. 54 48
      client/lib/datepicker.js
  54. 8 5
      client/lib/dropImage.js
  55. 25 23
      client/lib/escapeActions.js
  56. 213 221
      client/lib/filter.js
  57. 1 1
      client/lib/i18n.js
  58. 29 21
      client/lib/inlinedform.js
  59. 39 28
      client/lib/keyboard.js
  60. 8 8
      client/lib/modal.js
  61. 27 21
      client/lib/multiSelection.js
  62. 11 5
      client/lib/pasteImage.js
  63. 17 14
      client/lib/popup.js
  64. 3 6
      client/lib/textComplete.js
  65. 3 3
      client/lib/unsavedEdits.js
  66. 51 27
      client/lib/utils.js
  67. 38 22
      config/accounts.js
  68. 10 6
      config/router.js
  69. 3 8
      models/accountSettings.js
  70. 0 5
      models/actions.js
  71. 7 12
      models/activities.js
  72. 1 6
      models/announcements.js
  73. 2 2
      models/attachments.js
  74. 25 30
      models/boards.js
  75. 5 12
      models/cardComments.js
  76. 417 361
      models/cards.js
  77. 6 11
      models/checklistItems.js
  78. 9 14
      models/checklists.js
  79. 13 18
      models/customFields.js
  80. 77 48
      models/export.js
  81. 8 10
      models/import.js
  82. 19 24
      models/integrations.js
  83. 1 6
      models/invitationCodes.js
  84. 8 13
      models/lists.js
  85. 1 6
      models/rules.js
  86. 9 16
      models/settings.js
  87. 11 16
      models/swimlanes.js
  88. 192 168
      models/trelloCreator.js
  89. 1 6
      models/triggers.js
  90. 1 8
      models/unsavedEdits.js
  91. 18 22
      models/users.js
  92. 6 6
      models/watchable.js
  93. 301 278
      models/wekanCreator.js
  94. 2 2
      models/wekanmapper.js
  95. 1 1
      package.json
  96. 134 92
      sandstorm.js
  97. 12 7
      server/accounts-lockout.js
  98. 13 14
      server/authentication.js
  99. 11 7
      server/cors.js
  100. 5 5
      server/header-login.js

+ 5 - 8
.eslintrc.json

@@ -12,11 +12,8 @@
     "meteor": true
   },
   "parserOptions": {
-    "ecmaVersion": 2017,
-    "sourceType": "module",
-    "ecmaFeatures": {
-      "experimentalObjectRestSpread": true
-    }
+    "ecmaVersion": 2018,
+    "sourceType": "module"
   },
   "rules": {
     "strict": 0,
@@ -26,7 +23,7 @@
     "consistent-return": 2,
     "dot-notation": 2,
     "eqeqeq": 2,
-    "indent": [2, 2],
+    "indent": 0,
     "no-cond-assign": 2,
     "no-constant-condition": 2,
     "no-eval": 2,
@@ -50,7 +47,6 @@
     "quotes": [2, "single"],
     "semi-spacing": 2,
     "space-unary-ops": 2,
-    "arrow-parens": 2,
     "arrow-spacing": 2,
     "no-class-assign": 2,
     "no-dupe-class-members": 2,
@@ -69,7 +65,8 @@
         "singleQuote": true,
         "trailingComma": "all"
       }
-    ]
+    ],
+    "meteor/no-session": 0
   },
   "settings": {
     "import/resolver": {

+ 90 - 58
client/components/activities/activities.js

@@ -13,7 +13,7 @@ BlazeComponent.extendComponent({
       let thisId, searchId;
       if (mode === 'linkedcard' || mode === 'linkedboard') {
         thisId = Session.get('currentCard');
-        searchId = Cards.findOne({_id: thisId}).linkedId;
+        searchId = Cards.findOne({ _id: thisId }).linkedId;
         mode = mode.replace('linked', '');
       } else {
         thisId = Session.get(`current${capitalizedMode}`);
@@ -22,8 +22,7 @@ BlazeComponent.extendComponent({
       const limit = this.page.get() * activitiesPerPage;
       const user = Meteor.user();
       const hideSystem = user ? user.hasHiddenSystemMessages() : false;
-      if (searchId === null)
-        return;
+      if (searchId === null) return;
 
       this.subscribe('activities', mode, searchId, limit, hideSystem, () => {
         this.loadNextPageLocked = false;
@@ -50,9 +49,9 @@ BlazeComponent.extendComponent({
     }
   },
 
-  checkItem(){
+  checkItem() {
     const checkItemId = this.currentData().checklistItemId;
-    const checkItem = ChecklistItems.findOne({_id:checkItemId});
+    const checkItem = ChecklistItems.findOne({ _id: checkItemId });
     return checkItem.title;
   },
 
@@ -66,42 +65,58 @@ BlazeComponent.extendComponent({
 
   cardLink() {
     const card = this.currentData().card();
-    return card && Blaze.toHTML(HTML.A({
-      href: card.absoluteUrl(),
-      'class': 'action-card',
-    }, card.title));
+    return (
+      card &&
+      Blaze.toHTML(
+        HTML.A(
+          {
+            href: card.absoluteUrl(),
+            class: 'action-card',
+          },
+          card.title,
+        ),
+      )
+    );
   },
 
-  lastLabel(){
+  lastLabel() {
     const lastLabelId = this.currentData().labelId;
-    if (!lastLabelId)
-      return null;
-    const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(lastLabelId);
-    if(lastLabel.name === undefined || lastLabel.name === ''){
+    if (!lastLabelId) return null;
+    const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(
+      lastLabelId,
+    );
+    if (lastLabel.name === undefined || lastLabel.name === '') {
       return lastLabel.color;
-    }else{
+    } else {
       return lastLabel.name;
     }
   },
 
-  lastCustomField(){
-    const lastCustomField = CustomFields.findOne(this.currentData().customFieldId);
-    if (!lastCustomField)
-      return null;
+  lastCustomField() {
+    const lastCustomField = CustomFields.findOne(
+      this.currentData().customFieldId,
+    );
+    if (!lastCustomField) return null;
     return lastCustomField.name;
   },
 
-  lastCustomFieldValue(){
-    const lastCustomField = CustomFields.findOne(this.currentData().customFieldId);
-    if (!lastCustomField)
-      return null;
+  lastCustomFieldValue() {
+    const lastCustomField = CustomFields.findOne(
+      this.currentData().customFieldId,
+    );
+    if (!lastCustomField) return null;
     const value = this.currentData().value;
-    if (lastCustomField.settings.dropdownItems && lastCustomField.settings.dropdownItems.length > 0) {
-      const dropDownValue = _.find(lastCustomField.settings.dropdownItems, (item) => {
-        return item._id === value;
-      });
-      if (dropDownValue)
-        return dropDownValue.name;
+    if (
+      lastCustomField.settings.dropdownItems &&
+      lastCustomField.settings.dropdownItems.length > 0
+    ) {
+      const dropDownValue = _.find(
+        lastCustomField.settings.dropdownItems,
+        item => {
+          return item._id === value;
+        },
+      );
+      if (dropDownValue) return dropDownValue.name;
     }
     return value;
   },
@@ -112,11 +127,16 @@ BlazeComponent.extendComponent({
 
   sourceLink() {
     const source = this.currentData().source;
-    if(source) {
-      if(source.url) {
-        return Blaze.toHTML(HTML.A({
-          href: source.url,
-        }, source.system));
+    if (source) {
+      if (source.url) {
+        return Blaze.toHTML(
+          HTML.A(
+            {
+              href: source.url,
+            },
+            source.system,
+          ),
+        );
       } else {
         return source.system;
       }
@@ -133,38 +153,50 @@ BlazeComponent.extendComponent({
   attachmentLink() {
     const attachment = this.currentData().attachment();
     // trying to display url before file is stored generates js errors
-    return attachment && attachment.url({ download: true }) && Blaze.toHTML(HTML.A({
-      href: attachment.url({ download: true }),
-      target: '_blank',
-    }, attachment.name()));
+    return (
+      attachment &&
+      attachment.url({ download: true }) &&
+      Blaze.toHTML(
+        HTML.A(
+          {
+            href: attachment.url({ download: true }),
+            target: '_blank',
+          },
+          attachment.name(),
+        ),
+      )
+    );
   },
 
   customField() {
     const customField = this.currentData().customField();
-    if (!customField)
-      return null;
+    if (!customField) return null;
     return customField.name;
   },
 
   events() {
-    return [{
-      // XXX We should use Popup.afterConfirmation here
-      'click .js-delete-comment'() {
-        const commentId = this.currentData().commentId;
-        CardComments.remove(commentId);
-      },
-      'submit .js-edit-comment'(evt) {
-        evt.preventDefault();
-        const commentText = this.currentComponent().getValue().trim();
-        const commentId = Template.parentData().commentId;
-        if (commentText) {
-          CardComments.update(commentId, {
-            $set: {
-              text: commentText,
-            },
-          });
-        }
+    return [
+      {
+        // XXX We should use Popup.afterConfirmation here
+        'click .js-delete-comment'() {
+          const commentId = this.currentData().commentId;
+          CardComments.remove(commentId);
+        },
+        'submit .js-edit-comment'(evt) {
+          evt.preventDefault();
+          const commentText = this.currentComponent()
+            .getValue()
+            .trim();
+          const commentId = Template.parentData().commentId;
+          if (commentText) {
+            CardComments.update(commentId, {
+              $set: {
+                text: commentText,
+              },
+            });
+          }
+        },
       },
-    }];
+    ];
   },
 }).register('activities');

+ 41 - 35
client/components/activities/comments.js

@@ -14,39 +14,41 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-new-comment:not(.focus)'() {
-        commentFormIsOpen.set(true);
+    return [
+      {
+        'click .js-new-comment:not(.focus)'() {
+          commentFormIsOpen.set(true);
+        },
+        'submit .js-new-comment-form'(evt) {
+          const input = this.getInput();
+          const text = input.val().trim();
+          const card = this.currentData();
+          let boardId = card.boardId;
+          let cardId = card._id;
+          if (card.isLinkedCard()) {
+            boardId = Cards.findOne(card.linkedId).boardId;
+            cardId = card.linkedId;
+          }
+          if (text) {
+            CardComments.insert({
+              text,
+              boardId,
+              cardId,
+            });
+            resetCommentInput(input);
+            Tracker.flush();
+            autosize.update(input);
+          }
+          evt.preventDefault();
+        },
+        // Pressing Ctrl+Enter should submit the form
+        'keydown form textarea'(evt) {
+          if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
+            this.find('button[type=submit]').click();
+          }
+        },
       },
-      'submit .js-new-comment-form'(evt) {
-        const input = this.getInput();
-        const text = input.val().trim();
-        const card = this.currentData();
-        let boardId = card.boardId;
-        let cardId = card._id;
-        if (card.isLinkedCard()) {
-          boardId = Cards.findOne(card.linkedId).boardId;
-          cardId = card.linkedId;
-        }
-        if (text) {
-          CardComments.insert({
-            text,
-            boardId,
-            cardId,
-          });
-          resetCommentInput(input);
-          Tracker.flush();
-          autosize.update(input);
-        }
-        evt.preventDefault();
-      },
-      // Pressing Ctrl+Enter should submit the form
-      'keydown form textarea'(evt) {
-        if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
-          this.find('button[type=submit]').click();
-        }
-      },
-    }];
+    ];
   },
 }).register('commentForm');
 
@@ -69,7 +71,8 @@ Tracker.autorun(() => {
   });
 });
 
-EscapeActions.register('inlinedForm',
+EscapeActions.register(
+  'inlinedForm',
   () => {
     const draftKey = {
       fieldName: 'cardComment',
@@ -84,7 +87,10 @@ EscapeActions.register('inlinedForm',
     }
     resetCommentInput(commentInput);
   },
-  () => { return commentFormIsOpen.get(); }, {
+  () => {
+    return commentFormIsOpen.get();
+  },
+  {
     noClickEscapeOn: '.js-new-comment',
-  }
+  },
 );

+ 36 - 27
client/components/boards/boardArchive.js

@@ -4,36 +4,45 @@ BlazeComponent.extendComponent({
   },
 
   archivedBoards() {
-    return Boards.find({ archived: true }, {
-      sort: ['title'],
-    });
+    return Boards.find(
+      { archived: true },
+      {
+        sort: ['title'],
+      },
+    );
   },
 
   events() {
-    return [{
-      'click .js-restore-board'() {
-        // TODO : Make isSandstorm variable global
-        const isSandstorm = Meteor.settings && Meteor.settings.public &&
-          Meteor.settings.public.sandstorm;
-        if (isSandstorm && Session.get('currentBoard')) {
-          const currentBoard = Boards.findOne(Session.get('currentBoard'));
-          currentBoard.archive();
-        }
-        const board = this.currentData();
-        board.restore();
-        Utils.goBoardId(board._id);
+    return [
+      {
+        'click .js-restore-board'() {
+          // TODO : Make isSandstorm variable global
+          const isSandstorm =
+            Meteor.settings &&
+            Meteor.settings.public &&
+            Meteor.settings.public.sandstorm;
+          if (isSandstorm && Session.get('currentBoard')) {
+            const currentBoard = Boards.findOne(Session.get('currentBoard'));
+            currentBoard.archive();
+          }
+          const board = this.currentData();
+          board.restore();
+          Utils.goBoardId(board._id);
+        },
+        'click .js-delete-board': Popup.afterConfirm('boardDelete', function() {
+          Popup.close();
+          const isSandstorm =
+            Meteor.settings &&
+            Meteor.settings.public &&
+            Meteor.settings.public.sandstorm;
+          if (isSandstorm && Session.get('currentBoard')) {
+            const currentBoard = Boards.findOne(Session.get('currentBoard'));
+            Boards.remove(currentBoard._id);
+          }
+          Boards.remove(this._id);
+          FlowRouter.go('home');
+        }),
       },
-      'click .js-delete-board': Popup.afterConfirm('boardDelete', function() {
-        Popup.close();
-        const isSandstorm = Meteor.settings && Meteor.settings.public &&
-          Meteor.settings.public.sandstorm;
-        if (isSandstorm && Session.get('currentBoard')) {
-          const currentBoard = Boards.findOne(Session.get('currentBoard'));
-          Boards.remove(currentBoard._id);
-        }
-        Boards.remove(this._id);
-        FlowRouter.go('home');
-      }),
-    }];
+    ];
   },
 }).register('archivedBoards');

+ 83 - 55
client/components/boards/boardBody.js

@@ -12,8 +12,7 @@ BlazeComponent.extendComponent({
     // unfortunatly, Blaze doesn't have this notion.
     this.autorun(() => {
       const currentBoardId = Session.get('currentBoard');
-      if (!currentBoardId)
-        return;
+      if (!currentBoardId) return;
       const handle = subManager.subscribe('board', currentBoardId, false);
       Tracker.nonreactive(() => {
         Tracker.autorun(() => {
@@ -30,7 +29,6 @@ BlazeComponent.extendComponent({
   goHome() {
     FlowRouter.go('home');
   },
-
 }).register('board');
 
 BlazeComponent.extendComponent({
@@ -47,7 +45,7 @@ BlazeComponent.extendComponent({
     if (nullSortSwimlanes.count() > 0) {
       const swimlanes = currentBoardData.swimlanes();
       let count = 0;
-      swimlanes.forEach((s) => {
+      swimlanes.forEach(s => {
         Swimlanes.update(s._id, {
           $set: {
             sort: count,
@@ -62,7 +60,7 @@ BlazeComponent.extendComponent({
     if (nullSortLists.count() > 0) {
       const lists = currentBoardData.lists();
       let count = 0;
-      lists.forEach((l) => {
+      lists.forEach(l => {
         Lists.update(l._id, {
           $set: {
             sort: count,
@@ -110,12 +108,15 @@ BlazeComponent.extendComponent({
         // resize all swimlanes + headers to be a total of 150 px per row
         // this could be achieved by setIsDragging(true) but we want immediate
         // result
-        ui.item.siblings('.js-swimlane').css('height', `${swimlaneWhileSortingHeight - 26}px`);
+        ui.item
+          .siblings('.js-swimlane')
+          .css('height', `${swimlaneWhileSortingHeight - 26}px`);
 
         // set the new scroll height after the resize and insertion of
         // the placeholder. We want the element under the cursor to stay
         // at the same place on the screen
-        ui.item.parent().get(0).scrollTop = ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY;
+        ui.item.parent().get(0).scrollTop =
+          ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY;
       },
       beforeStop(evt, ui) {
         const parentOffset = ui.item.parent().offset();
@@ -124,7 +125,8 @@ BlazeComponent.extendComponent({
 
         // compute the new scroll height after the resize and removal of
         // the placeholder
-        const scrollTop = ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY;
+        const scrollTop =
+          ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY;
 
         // then reset the original view of the swimlane
         siblings.removeClass('moving-swimlane');
@@ -154,11 +156,14 @@ BlazeComponent.extendComponent({
       sort(evt, ui) {
         // get the mouse position in the sortable
         const parentOffset = ui.item.parent().offset();
-        const cursorY = evt.pageY - parentOffset.top + ui.item.parent().scrollTop();
+        const cursorY =
+          evt.pageY - parentOffset.top + ui.item.parent().scrollTop();
 
         // compute the intended index of the placeholder (we need to skip the
         // slots between the headers and the list of cards)
-        const newplaceholderIndex = Math.floor(cursorY / swimlaneWhileSortingHeight);
+        const newplaceholderIndex = Math.floor(
+          cursorY / swimlaneWhileSortingHeight,
+        );
         let destPlaceholderIndex = (newplaceholderIndex + 1) * 2;
 
         // if we are scrolling far away from the bottom of the list
@@ -169,9 +174,17 @@ BlazeComponent.extendComponent({
         // update the placeholder position in the DOM tree
         if (destPlaceholderIndex !== ui.placeholder.index()) {
           if (destPlaceholderIndex < boardComponent.origPlaceholderIndex) {
-            ui.placeholder.insertBefore(ui.placeholder.siblings().slice(destPlaceholderIndex - 2, destPlaceholderIndex - 1));
+            ui.placeholder.insertBefore(
+              ui.placeholder
+                .siblings()
+                .slice(destPlaceholderIndex - 2, destPlaceholderIndex - 1),
+            );
           } else {
-            ui.placeholder.insertAfter(ui.placeholder.siblings().slice(destPlaceholderIndex - 1, destPlaceholderIndex));
+            ui.placeholder.insertAfter(
+              ui.placeholder
+                .siblings()
+                .slice(destPlaceholderIndex - 1, destPlaceholderIndex),
+            );
           }
         }
       },
@@ -181,7 +194,11 @@ BlazeComponent.extendComponent({
     enableClickOnTouch('.js-swimlane:not(.placeholder)');
 
     function userIsMember() {
-      return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+      return (
+        Meteor.user() &&
+        Meteor.user().isBoardMember() &&
+        !Meteor.user().isCommentOnly()
+      );
     }
 
     // If there is no data in the board (ie, no lists) we autofocus the list
@@ -195,45 +212,49 @@ BlazeComponent.extendComponent({
   isViewSwimlanes() {
     const currentUser = Meteor.user();
     if (!currentUser) return false;
-    return ((currentUser.profile || {}).boardView === 'board-view-swimlanes');
+    return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
   },
 
   isViewLists() {
     const currentUser = Meteor.user();
     if (!currentUser) return true;
-    return ((currentUser.profile || {}).boardView === 'board-view-lists');
+    return (currentUser.profile || {}).boardView === 'board-view-lists';
   },
 
   isViewCalendar() {
     const currentUser = Meteor.user();
     if (!currentUser) return false;
-    return ((currentUser.profile || {}).boardView === 'board-view-cal');
+    return (currentUser.profile || {}).boardView === 'board-view-cal';
   },
 
   openNewListForm() {
     if (this.isViewSwimlanes()) {
       this.childComponents('swimlane')[0]
-        .childComponents('addListAndSwimlaneForm')[0].open();
+        .childComponents('addListAndSwimlaneForm')[0]
+        .open();
     } else if (this.isViewLists()) {
       this.childComponents('listsGroup')[0]
-        .childComponents('addListForm')[0].open();
+        .childComponents('addListForm')[0]
+        .open();
     }
   },
   events() {
-    return [{
-      // XXX The board-overlay div should probably be moved to the parent
-      // component.
-      'mouseenter .board-overlay'() {
-        if (this.mouseHasEnterCardDetails) {
-          this.showOverlay.set(false);
-        }
-      },
-      'mouseup'() {
-        if (this._isDragging) {
-          this._isDragging = false;
-        }
+    return [
+      {
+        // XXX The board-overlay div should probably be moved to the parent
+        // component.
+        'mouseenter .board-overlay'() {
+          if (this.mouseHasEnterCardDetails) {
+            this.showOverlay.set(false);
+          }
+        },
+        mouseup() {
+          if (this._isDragging) {
+            this._isDragging = false;
+          }
+        },
       },
-    }];
+    ];
   },
 
   // XXX Flow components allow us to avoid creating these two setter methods by
@@ -245,23 +266,24 @@ BlazeComponent.extendComponent({
 
   scrollLeft(position = 0) {
     const swimlanes = this.$('.js-swimlanes');
-    swimlanes && swimlanes.animate({
-      scrollLeft: position,
-    });
+    swimlanes &&
+      swimlanes.animate({
+        scrollLeft: position,
+      });
   },
 
   scrollTop(position = 0) {
     const swimlanes = this.$('.js-swimlanes');
-    swimlanes && swimlanes.animate({
-      scrollTop: position,
-    });
+    swimlanes &&
+      swimlanes.animate({
+        scrollTop: position,
+      });
   },
-
 }).register('boardBody');
 
 BlazeComponent.extendComponent({
   onRendered() {
-    this.autorun(function(){
+    this.autorun(function() {
       $('#calendar-view').fullCalendar('refetchEvents');
     });
   },
@@ -273,7 +295,8 @@ BlazeComponent.extendComponent({
       timezone: 'local',
       header: {
         left: 'title   today prev,next',
-        center: 'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,timelineMonth timelineYear',
+        center:
+          'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,timelineMonth timelineYear',
         right: '',
       },
       // height: 'parent', nope, doesn't work as the parent might be small
@@ -283,7 +306,7 @@ BlazeComponent.extendComponent({
       nowIndicator: true,
       businessHours: {
         // days of week. an array of zero-based day of week integers (0=Sunday)
-        dow: [ 1, 2, 3, 4, 5 ], // Monday - Friday
+        dow: [1, 2, 3, 4, 5], // Monday - Friday
         start: '8:00',
         end: '18:00',
       },
@@ -291,20 +314,25 @@ BlazeComponent.extendComponent({
       events(start, end, timezone, callback) {
         const currentBoard = Boards.findOne(Session.get('currentBoard'));
         const events = [];
-        currentBoard.cardsInInterval(start.toDate(), end.toDate()).forEach(function(card){
-          events.push({
-            id: card._id,
-            title: card.title,
-            start: card.startAt,
-            end: card.endAt,
-            allDay: Math.abs(card.endAt.getTime() - card.startAt.getTime()) / 1000 === 24*3600,
-            url: FlowRouter.url('card', {
-              boardId: currentBoard._id,
-              slug: currentBoard.slug,
-              cardId: card._id,
-            }),
+        currentBoard
+          .cardsInInterval(start.toDate(), end.toDate())
+          .forEach(function(card) {
+            events.push({
+              id: card._id,
+              title: card.title,
+              start: card.startAt,
+              end: card.endAt,
+              allDay:
+                Math.abs(card.endAt.getTime() - card.startAt.getTime()) /
+                  1000 ===
+                24 * 3600,
+              url: FlowRouter.url('card', {
+                boardId: currentBoard._id,
+                slug: currentBoard.slug,
+                cardId: card._id,
+              }),
+            });
           });
-        });
         callback(events);
       },
       eventResize(event, delta, revertFunc) {
@@ -339,6 +367,6 @@ BlazeComponent.extendComponent({
   isViewCalendar() {
     const currentUser = Meteor.user();
     if (!currentUser) return false;
-    return ((currentUser.profile || {}).boardView === 'board-view-cal');
+    return (currentUser.profile || {}).boardView === 'board-view-cal';
   },
 }).register('calendarView');

+ 119 - 87
client/components/boards/boardHeader.js

@@ -45,15 +45,21 @@ Template.boardMenuPopup.helpers({
 });
 
 Template.boardChangeTitlePopup.events({
-  submit(evt, tpl) {
-    const newTitle = tpl.$('.js-board-name').val().trim();
-    const newDesc = tpl.$('.js-board-desc').val().trim();
+  submit(event, templateInstance) {
+    const newTitle = templateInstance
+      .$('.js-board-name')
+      .val()
+      .trim();
+    const newDesc = templateInstance
+      .$('.js-board-desc')
+      .val()
+      .trim();
     if (newTitle) {
       this.rename(newTitle);
       this.setDescription(newDesc);
       Popup.close();
     }
-    evt.preventDefault();
+    event.preventDefault();
   },
 });
 
@@ -76,67 +82,79 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-edit-board-title': Popup.open('boardChangeTitle'),
-      'click .js-star-board'() {
-        Meteor.user().toggleBoardStar(Session.get('currentBoard'));
+    return [
+      {
+        'click .js-edit-board-title': Popup.open('boardChangeTitle'),
+        'click .js-star-board'() {
+          Meteor.user().toggleBoardStar(Session.get('currentBoard'));
+        },
+        'click .js-open-board-menu': Popup.open('boardMenu'),
+        'click .js-change-visibility': Popup.open('boardChangeVisibility'),
+        'click .js-watch-board': Popup.open('boardChangeWatch'),
+        'click .js-open-archived-board'() {
+          Modal.open('archivedBoards');
+        },
+        'click .js-toggle-board-view'() {
+          const currentUser = Meteor.user();
+          if (
+            (currentUser.profile || {}).boardView === 'board-view-swimlanes'
+          ) {
+            currentUser.setBoardView('board-view-cal');
+          } else if (
+            (currentUser.profile || {}).boardView === 'board-view-lists'
+          ) {
+            currentUser.setBoardView('board-view-swimlanes');
+          } else if (
+            (currentUser.profile || {}).boardView === 'board-view-cal'
+          ) {
+            currentUser.setBoardView('board-view-lists');
+          } else {
+            currentUser.setBoardView('board-view-swimlanes');
+          }
+        },
+        'click .js-toggle-sidebar'() {
+          Sidebar.toggle();
+        },
+        'click .js-open-filter-view'() {
+          Sidebar.setView('filter');
+        },
+        'click .js-filter-reset'(event) {
+          event.stopPropagation();
+          Sidebar.setView();
+          Filter.reset();
+        },
+        'click .js-open-search-view'() {
+          Sidebar.setView('search');
+        },
+        'click .js-open-rules-view'() {
+          Modal.openWide('rulesMain');
+        },
+        'click .js-multiselection-activate'() {
+          const currentCard = Session.get('currentCard');
+          MultiSelection.activate();
+          if (currentCard) {
+            MultiSelection.add(currentCard);
+          }
+        },
+        'click .js-multiselection-reset'(event) {
+          event.stopPropagation();
+          MultiSelection.disable();
+        },
+        'click .js-log-in'() {
+          FlowRouter.go('atSignIn');
+        },
       },
-      'click .js-open-board-menu': Popup.open('boardMenu'),
-      'click .js-change-visibility': Popup.open('boardChangeVisibility'),
-      'click .js-watch-board': Popup.open('boardChangeWatch'),
-      'click .js-open-archived-board'() {
-        Modal.open('archivedBoards');
-      },
-      'click .js-toggle-board-view'() {
-        const currentUser = Meteor.user();
-        if ((currentUser.profile || {}).boardView === 'board-view-swimlanes') {
-          currentUser.setBoardView('board-view-cal');
-        } else if ((currentUser.profile || {}).boardView === 'board-view-lists') {
-          currentUser.setBoardView('board-view-swimlanes');
-        } else if ((currentUser.profile || {}).boardView === 'board-view-cal') {
-          currentUser.setBoardView('board-view-lists');
-        } else {
-          currentUser.setBoardView('board-view-swimlanes');
-        }
-      },
-      'click .js-toggle-sidebar'() {
-        Sidebar.toggle();
-      },
-      'click .js-open-filter-view'() {
-        Sidebar.setView('filter');
-      },
-      'click .js-filter-reset'(evt) {
-        evt.stopPropagation();
-        Sidebar.setView();
-        Filter.reset();
-      },
-      'click .js-open-search-view'() {
-        Sidebar.setView('search');
-      },
-      'click .js-open-rules-view'() {
-        Modal.openWide('rulesMain');
-      },
-      'click .js-multiselection-activate'() {
-        const currentCard = Session.get('currentCard');
-        MultiSelection.activate();
-        if (currentCard) {
-          MultiSelection.add(currentCard);
-        }
-      },
-      'click .js-multiselection-reset'(evt) {
-        evt.stopPropagation();
-        MultiSelection.disable();
-      },
-      'click .js-log-in'() {
-        FlowRouter.go('atSignIn');
-      },
-    }];
+    ];
   },
 }).register('boardHeaderBar');
 
 Template.boardHeaderBar.helpers({
   canModifyBoard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 });
 
@@ -164,15 +182,17 @@ const CreateBoard = BlazeComponent.extendComponent({
     this.visibilityMenuIsOpen.set(!this.visibilityMenuIsOpen.get());
   },
 
-  onSubmit(evt) {
-    evt.preventDefault();
+  onSubmit(event) {
+    event.preventDefault();
     const title = this.find('.js-new-board-title').value;
     const visibility = this.visibility.get();
 
-    this.boardId.set(Boards.insert({
-      title,
-      permission: visibility,
-    }));
+    this.boardId.set(
+      Boards.insert({
+        title,
+        permission: visibility,
+      }),
+    );
 
     Swimlanes.insert({
       title: 'Default',
@@ -183,26 +203,28 @@ const CreateBoard = BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-select-visibility'() {
-        this.setVisibility(this.currentData());
+    return [
+      {
+        'click .js-select-visibility'() {
+          this.setVisibility(this.currentData());
+        },
+        'click .js-change-visibility': this.toggleVisibilityMenu,
+        'click .js-import': Popup.open('boardImportBoard'),
+        submit: this.onSubmit,
+        'click .js-import-board': Popup.open('chooseBoardSource'),
+        'click .js-board-template': Popup.open('searchElement'),
       },
-      'click .js-change-visibility': this.toggleVisibilityMenu,
-      'click .js-import': Popup.open('boardImportBoard'),
-      submit: this.onSubmit,
-      'click .js-import-board': Popup.open('chooseBoardSource'),
-      'click .js-board-template': Popup.open('searchElement'),
-    }];
+    ];
   },
 }).register('createBoardPopup');
 
 (class HeaderBarCreateBoard extends CreateBoard {
-  onSubmit(evt) {
-    super.onSubmit(evt);
+  onSubmit(event) {
+    super.onSubmit(event);
     // Immediately star boards crated with the headerbar popup.
     Meteor.user().toggleBoardStar(this.boardId.get());
   }
-}).register('headerBarCreateBoardPopup');
+}.register('headerBarCreateBoardPopup'));
 
 BlazeComponent.extendComponent({
   visibilityCheck() {
@@ -218,9 +240,11 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-select-visibility': this.selectBoardVisibility,
-    }];
+    return [
+      {
+        'click .js-select-visibility': this.selectBoardVisibility,
+      },
+    ];
   },
 }).register('boardChangeVisibilityPopup');
 
@@ -235,13 +259,21 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-select-watch'() {
-        const level = this.currentData();
-        Meteor.call('watch', 'board', Session.get('currentBoard'), level, (err, ret) => {
-          if (!err && ret) Popup.close();
-        });
+    return [
+      {
+        'click .js-select-watch'() {
+          const level = this.currentData();
+          Meteor.call(
+            'watch',
+            'board',
+            Session.get('currentBoard'),
+            level,
+            (err, ret) => {
+              if (!err && ret) Popup.close();
+            },
+          );
+        },
       },
-    }];
+    ];
   },
 }).register('boardChangeWatchPopup');

+ 50 - 44
client/components/boards/boardsList.js

@@ -21,11 +21,14 @@ BlazeComponent.extendComponent({
   },
 
   boards() {
-    return Boards.find({
-      archived: false,
-      'members.userId': Meteor.userId(),
-      type: 'board',
-    }, { sort: ['title'] });
+    return Boards.find(
+      {
+        archived: false,
+        'members.userId': Meteor.userId(),
+        type: 'board',
+      },
+      { sort: ['title'] },
+    );
   },
   isStarred() {
     const user = Meteor.user();
@@ -48,46 +51,49 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-add-board': Popup.open('createBoard'),
-      'click .js-star-board'(evt) {
-        const boardId = this.currentData()._id;
-        Meteor.user().toggleBoardStar(boardId);
-        evt.preventDefault();
-      },
-      'click .js-clone-board'(evt) {
-        Meteor.call('cloneBoard',
-          this.currentData()._id,
-          Session.get('fromBoard'),
-          (err, res) => {
-            if (err) {
-              this.setError(err.error);
-            } else {
-              Session.set('fromBoard', null);
-              Utils.goBoardId(res);
+    return [
+      {
+        'click .js-add-board': Popup.open('createBoard'),
+        'click .js-star-board'(evt) {
+          const boardId = this.currentData()._id;
+          Meteor.user().toggleBoardStar(boardId);
+          evt.preventDefault();
+        },
+        'click .js-clone-board'(evt) {
+          Meteor.call(
+            'cloneBoard',
+            this.currentData()._id,
+            Session.get('fromBoard'),
+            (err, res) => {
+              if (err) {
+                this.setError(err.error);
+              } else {
+                Session.set('fromBoard', null);
+                Utils.goBoardId(res);
+              }
+            },
+          );
+          evt.preventDefault();
+        },
+        'click .js-archive-board'(evt) {
+          const boardId = this.currentData()._id;
+          Meteor.call('archiveBoard', boardId);
+          evt.preventDefault();
+        },
+        'click .js-accept-invite'() {
+          const boardId = this.currentData()._id;
+          Meteor.user().removeInvite(boardId);
+        },
+        'click .js-decline-invite'() {
+          const boardId = this.currentData()._id;
+          Meteor.call('quitBoard', boardId, (err, ret) => {
+            if (!err && ret) {
+              Meteor.user().removeInvite(boardId);
+              FlowRouter.go('home');
             }
-          }
-        );
-        evt.preventDefault();
-      },
-      'click .js-archive-board'(evt) {
-        const boardId = this.currentData()._id;
-        Meteor.call('archiveBoard', boardId);
-        evt.preventDefault();
-      },
-      'click .js-accept-invite'() {
-        const boardId = this.currentData()._id;
-        Meteor.user().removeInvite(boardId);
-      },
-      'click .js-decline-invite'() {
-        const boardId = this.currentData()._id;
-        Meteor.call('quitBoard', boardId, (err, ret) => {
-          if (!err && ret) {
-            Meteor.user().removeInvite(boardId);
-            FlowRouter.go('home');
-          }
-        });
+          });
+        },
       },
-    }];
+    ];
   },
 }).register('boardList');

+ 19 - 20
client/components/cards/attachments.js

@@ -1,10 +1,11 @@
 Template.attachmentsGalery.events({
   'click .js-add-attachment': Popup.open('cardAttachments'),
-  'click .js-confirm-delete': Popup.afterConfirm('attachmentDelete',
+  'click .js-confirm-delete': Popup.afterConfirm(
+    'attachmentDelete',
     function() {
       Attachments.remove(this._id);
       Popup.close();
-    }
+    },
   ),
   // If we let this event bubble, FlowRouter will handle it and empty the page
   // content, see #101.
@@ -17,8 +18,8 @@ Template.attachmentsGalery.events({
   'click .js-remove-cover'() {
     Cards.findOne(this.cardId).unsetCover();
   },
-  'click .js-preview-image'(evt) {
-    Popup.open('previewAttachedImage').call(this, evt);
+  'click .js-preview-image'(event) {
+    Popup.open('previewAttachedImage').call(this, event);
     // when multiple thumbnails, if click one then another very fast,
     // we might get a wrong width from previous img.
     // when popup reused, onRendered() won't be called, so we cannot get there.
@@ -31,31 +32,29 @@ Template.attachmentsGalery.events({
       // if the image is too large, we resize & center the popup.
       if (w > 300) {
         $('div.pop-over').css({
-          width: (w + 20),
+          width: w + 20,
           position: 'absolute',
-          left: (window.innerWidth - w)/2,
-          top: (window.innerHeight - h)/2,
+          left: (window.innerWidth - w) / 2,
+          top: (window.innerHeight - h) / 2,
         });
       }
     };
-    const url = $(evt.currentTarget).attr('src');
-    if (img.src === url && img.complete)
-      rePosPopup();
-    else
-      img.onload = rePosPopup;
+    const url = $(event.currentTarget).attr('src');
+    if (img.src === url && img.complete) rePosPopup();
+    else img.onload = rePosPopup;
   },
 });
 
 Template.previewAttachedImagePopup.events({
-  'click .js-large-image-clicked'(){
+  'click .js-large-image-clicked'() {
     Popup.close();
   },
 });
 
 Template.cardAttachmentsPopup.events({
-  'change .js-attach-file'(evt) {
+  'change .js-attach-file'(event) {
     const card = this;
-    FS.Utility.eachFile(evt, (f) => {
+    FS.Utility.eachFile(event, f => {
       const file = new FS.File(f);
       if (card.isLinkedCard()) {
         file.boardId = Cards.findOne(card.linkedId).boardId;
@@ -77,9 +76,9 @@ Template.cardAttachmentsPopup.events({
       Popup.close();
     });
   },
-  'click .js-computer-upload'(evt, tpl) {
-    tpl.find('.js-attach-file').click();
-    evt.preventDefault();
+  'click .js-computer-upload'(event, templateInstance) {
+    templateInstance.find('.js-attach-file').click();
+    event.preventDefault();
   },
   'click .js-upload-clipboard-image': Popup.open('previewClipboardImage'),
 });
@@ -88,7 +87,7 @@ let pastedResults = null;
 
 Template.previewClipboardImagePopup.onRendered(() => {
   // we can paste image from clipboard
-  $(document.body).pasteImageReader((results) => {
+  $(document.body).pasteImageReader(results => {
     if (results.dataURL.startsWith('data:image/')) {
       $('img.preview-clipboard-image').attr('src', results.dataURL);
       pastedResults = results;
@@ -96,7 +95,7 @@ Template.previewClipboardImagePopup.onRendered(() => {
   });
 
   // we can also drag & drop image file to it
-  $(document.body).dropImageReader((results) => {
+  $(document.body).dropImageReader(results => {
     if (results.dataURL.startsWith('data:image/')) {
       $('img.preview-clipboard-image').attr('src', results.dataURL);
       pastedResults = results;

+ 51 - 44
client/components/cards/cardCustomFields.js

@@ -7,22 +7,21 @@ Template.cardCustomFieldsPopup.helpers({
 });
 
 Template.cardCustomFieldsPopup.events({
-  'click .js-select-field'(evt) {
+  'click .js-select-field'(event) {
     const card = Cards.findOne(Session.get('currentCard'));
     const customFieldId = this._id;
     card.toggleCustomField(customFieldId);
-    evt.preventDefault();
+    event.preventDefault();
   },
-  'click .js-settings'(evt) {
+  'click .js-settings'(event) {
     EscapeActions.executeUpTo('detailsPane');
     Sidebar.setView('customFields');
-    evt.preventDefault();
+    event.preventDefault();
   },
 });
 
 // cardCustomField
 const CardCustomField = BlazeComponent.extendComponent({
-
   getTemplate() {
     return `cardCustomField-${this.data().definition.type}`;
   },
@@ -34,52 +33,55 @@ const CardCustomField = BlazeComponent.extendComponent({
   },
 
   canModifyCard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 });
 CardCustomField.register('cardCustomField');
 
 // cardCustomField-text
 (class extends CardCustomField {
-
   onCreated() {
     super.onCreated();
   }
 
   events() {
-    return [{
-      'submit .js-card-customfield-text'(evt) {
-        evt.preventDefault();
-        const value = this.currentComponent().getValue();
-        this.card.setCustomField(this.customFieldId, value);
+    return [
+      {
+        'submit .js-card-customfield-text'(event) {
+          event.preventDefault();
+          const value = this.currentComponent().getValue();
+          this.card.setCustomField(this.customFieldId, value);
+        },
       },
-    }];
+    ];
   }
-
-}).register('cardCustomField-text');
+}.register('cardCustomField-text'));
 
 // cardCustomField-number
 (class extends CardCustomField {
-
   onCreated() {
     super.onCreated();
   }
 
   events() {
-    return [{
-      'submit .js-card-customfield-number'(evt) {
-        evt.preventDefault();
-        const value = parseInt(this.find('input').value, 10);
-        this.card.setCustomField(this.customFieldId, value);
+    return [
+      {
+        'submit .js-card-customfield-number'(event) {
+          event.preventDefault();
+          const value = parseInt(this.find('input').value, 10);
+          this.card.setCustomField(this.customFieldId, value);
+        },
       },
-    }];
+    ];
   }
-
-}).register('cardCustomField-number');
+}.register('cardCustomField-number'));
 
 // cardCustomField-date
 (class extends CardCustomField {
-
   onCreated() {
     super.onCreated();
     const self = this;
@@ -108,8 +110,10 @@ CardCustomField.register('cardCustomField');
   }
 
   classes() {
-    if (this.date.get().isBefore(this.now.get(), 'minute') &&
-            this.now.get().isBefore(this.data().value)) {
+    if (
+      this.date.get().isBefore(this.now.get(), 'minute') &&
+      this.now.get().isBefore(this.data().value)
+    ) {
       return 'current';
     }
     return '';
@@ -120,12 +124,13 @@ CardCustomField.register('cardCustomField');
   }
 
   events() {
-    return [{
-      'click .js-edit-date': Popup.open('cardCustomField-date'),
-    }];
+    return [
+      {
+        'click .js-edit-date': Popup.open('cardCustomField-date'),
+      },
+    ];
   }
-
-}).register('cardCustomField-date');
+}.register('cardCustomField-date'));
 
 // cardCustomField-datePopup
 (class extends DatePicker {
@@ -144,11 +149,10 @@ CardCustomField.register('cardCustomField');
   _deleteDate() {
     this.card.setCustomField(this.customFieldId, '');
   }
-}).register('cardCustomField-datePopup');
+}.register('cardCustomField-datePopup'));
 
 // cardCustomField-dropdown
 (class extends CardCustomField {
-
   onCreated() {
     super.onCreated();
     this._items = this.data().definition.settings.dropdownItems;
@@ -160,20 +164,23 @@ CardCustomField.register('cardCustomField');
   }
 
   selectedItem() {
-    const selected = this._items.find((item) => {
+    const selected = this._items.find(item => {
       return item._id === this.data().value;
     });
-    return (selected) ? selected.name : TAPi18n.__('custom-field-dropdown-unknown');
+    return selected
+      ? selected.name
+      : TAPi18n.__('custom-field-dropdown-unknown');
   }
 
   events() {
-    return [{
-      'submit .js-card-customfield-dropdown'(evt) {
-        evt.preventDefault();
-        const value = this.find('select').value;
-        this.card.setCustomField(this.customFieldId, value);
+    return [
+      {
+        'submit .js-card-customfield-dropdown'(event) {
+          event.preventDefault();
+          const value = this.find('select').value;
+          this.card.setCustomField(this.customFieldId, value);
+        },
       },
-    }];
+    ];
   }
-
-}).register('cardCustomField-dropdown');
+}.register('cardCustomField-dropdown'));

+ 95 - 91
client/components/cards/cardDate.js

@@ -11,15 +11,20 @@ BlazeComponent.extendComponent({
   },
 
   onRendered() {
-    const $picker = this.$('.js-datepicker').datepicker({
-      todayHighlight: true,
-      todayBtn: 'linked',
-      language: TAPi18n.getLanguage(),
-    }).on('changeDate', function(evt) {
-      this.find('#date').value = moment(evt.date).format('L');
-      this.error.set('');
-      this.find('#time').focus();
-    }.bind(this));
+    const $picker = this.$('.js-datepicker')
+      .datepicker({
+        todayHighlight: true,
+        todayBtn: 'linked',
+        language: TAPi18n.getLanguage(),
+      })
+      .on(
+        'changeDate',
+        function(evt) {
+          this.find('#date').value = moment(evt.date).format('L');
+          this.error.set('');
+          this.find('#time').focus();
+        }.bind(this),
+      );
 
     if (this.date.get().isValid()) {
       $picker.datepicker('update', this.date.get().toDate());
@@ -27,13 +32,11 @@ BlazeComponent.extendComponent({
   },
 
   showDate() {
-    if (this.date.get().isValid())
-      return this.date.get().format('L');
+    if (this.date.get().isValid()) return this.date.get().format('L');
     return '';
   },
   showTime() {
-    if (this.date.get().isValid())
-      return this.date.get().format('LT');
+    if (this.date.get().isValid()) return this.date.get().format('LT');
     return '';
   },
   dateFormat() {
@@ -44,51 +47,58 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'keyup .js-date-field'() {
-        // parse for localized date format in strict mode
-        const dateMoment = moment(this.find('#date').value, 'L', true);
-        if (dateMoment.isValid()) {
-          this.error.set('');
-          this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
-        }
-      },
-      'keyup .js-time-field'() {
-        // parse for localized time format in strict mode
-        const dateMoment = moment(this.find('#time').value, 'LT', true);
-        if (dateMoment.isValid()) {
-          this.error.set('');
-        }
-      },
-      'submit .edit-date'(evt) {
-        evt.preventDefault();
-
-        // if no time was given, init with 12:00
-        const time = evt.target.time.value || moment(new Date().setHours(12, 0, 0)).format('LT');
-
-        const dateString = `${evt.target.date.value} ${time}`;
-        const newDate = moment(dateString, 'L LT', true);
-        if (newDate.isValid()) {
-          this._storeDate(newDate.toDate());
+    return [
+      {
+        'keyup .js-date-field'() {
+          // parse for localized date format in strict mode
+          const dateMoment = moment(this.find('#date').value, 'L', true);
+          if (dateMoment.isValid()) {
+            this.error.set('');
+            this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
+          }
+        },
+        'keyup .js-time-field'() {
+          // parse for localized time format in strict mode
+          const dateMoment = moment(this.find('#time').value, 'LT', true);
+          if (dateMoment.isValid()) {
+            this.error.set('');
+          }
+        },
+        'submit .edit-date'(evt) {
+          evt.preventDefault();
+
+          // if no time was given, init with 12:00
+          const time =
+            evt.target.time.value ||
+            moment(new Date().setHours(12, 0, 0)).format('LT');
+
+          const dateString = `${evt.target.date.value} ${time}`;
+          const newDate = moment(dateString, 'L LT', true);
+          if (newDate.isValid()) {
+            this._storeDate(newDate.toDate());
+            Popup.close();
+          } else {
+            this.error.set('invalid-date');
+            evt.target.date.focus();
+          }
+        },
+        'click .js-delete-date'(evt) {
+          evt.preventDefault();
+          this._deleteDate();
           Popup.close();
-        }
-        else {
-          this.error.set('invalid-date');
-          evt.target.date.focus();
-        }
-      },
-      'click .js-delete-date'(evt) {
-        evt.preventDefault();
-        this._deleteDate();
-        Popup.close();
+        },
       },
-    }];
+    ];
   },
 });
 
 Template.dateBadge.helpers({
   canModifyCard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 });
 
@@ -96,7 +106,8 @@ Template.dateBadge.helpers({
 (class extends DatePicker {
   onCreated() {
     super.onCreated();
-    this.data().getReceived() && this.date.set(moment(this.data().getReceived()));
+    this.data().getReceived() &&
+      this.date.set(moment(this.data().getReceived()));
   }
 
   _storeDate(date) {
@@ -106,8 +117,7 @@ Template.dateBadge.helpers({
   _deleteDate() {
     this.card.setReceived(null);
   }
-}).register('editCardReceivedDatePopup');
-
+}.register('editCardReceivedDatePopup'));
 
 // editCardStartDatePopup
 (class extends DatePicker {
@@ -119,7 +129,10 @@ Template.dateBadge.helpers({
   onRendered() {
     super.onRendered();
     if (moment.isDate(this.card.getReceived())) {
-      this.$('.js-datepicker').datepicker('setStartDate', this.card.getReceived());
+      this.$('.js-datepicker').datepicker(
+        'setStartDate',
+        this.card.getReceived(),
+      );
     }
   }
 
@@ -130,7 +143,7 @@ Template.dateBadge.helpers({
   _deleteDate() {
     this.card.setStart(null);
   }
-}).register('editCardStartDatePopup');
+}.register('editCardStartDatePopup'));
 
 // editCardDueDatePopup
 (class extends DatePicker {
@@ -153,7 +166,7 @@ Template.dateBadge.helpers({
   _deleteDate() {
     this.card.setDue(null);
   }
-}).register('editCardDueDatePopup');
+}.register('editCardDueDatePopup'));
 
 // editCardEndDatePopup
 (class extends DatePicker {
@@ -176,8 +189,7 @@ Template.dateBadge.helpers({
   _deleteDate() {
     this.card.setEnd(null);
   }
-}).register('editCardEndDatePopup');
-
+}.register('editCardEndDatePopup'));
 
 // Display received, start, due & end dates
 const CardDate = BlazeComponent.extendComponent({
@@ -224,17 +236,20 @@ class CardReceivedDate extends CardDate {
     const startAt = this.data().getStart();
     const theDate = this.date.get();
     // if dueAt, endAt and startAt exist & are > receivedAt, receivedAt doesn't need to be flagged
-    if (((startAt) && (theDate.isAfter(dueAt))) ||
-       ((endAt) && (theDate.isAfter(endAt))) ||
-       ((dueAt) && (theDate.isAfter(dueAt))))
+    if (
+      (startAt && theDate.isAfter(dueAt)) ||
+      (endAt && theDate.isAfter(endAt)) ||
+      (dueAt && theDate.isAfter(dueAt))
+    )
       classes += 'long-overdue';
-    else
-      classes += 'current';
+    else classes += 'current';
     return classes;
   }
 
   showTitle() {
-    return `${TAPi18n.__('card-received-on')} ${this.date.get().format('LLLL')}`;
+    return `${TAPi18n.__('card-received-on')} ${this.date
+      .get()
+      .format('LLLL')}`;
   }
 
   events() {
@@ -261,13 +276,10 @@ class CardStartDate extends CardDate {
     const theDate = this.date.get();
     const now = this.now.get();
     // if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged
-    if (((endAt) && (theDate.isAfter(endAt))) ||
-       ((dueAt) && (theDate.isAfter(dueAt))))
+    if ((endAt && theDate.isAfter(endAt)) || (dueAt && theDate.isAfter(dueAt)))
       classes += 'long-overdue';
-    else if (theDate.isBefore(now, 'minute'))
-      classes += 'almost-due';
-    else
-      classes += 'current';
+    else if (theDate.isBefore(now, 'minute')) classes += 'almost-due';
+    else classes += 'current';
     return classes;
   }
 
@@ -298,17 +310,12 @@ class CardDueDate extends CardDate {
     const theDate = this.date.get();
     const now = this.now.get();
     // if the due date is after the end date, green - done early
-    if ((endAt) && (theDate.isAfter(endAt)))
-      classes += 'current';
+    if (endAt && theDate.isAfter(endAt)) classes += 'current';
     // if there is an end date, don't need to flag the due date
-    else if (endAt)
-      classes += '';
-    else if (now.diff(theDate, 'days') >= 2)
-      classes += 'long-overdue';
-    else if (now.diff(theDate, 'minute') >= 0)
-      classes += 'due';
-    else if (now.diff(theDate, 'days') >= -1)
-      classes += 'almost-due';
+    else if (endAt) classes += '';
+    else if (now.diff(theDate, 'days') >= 2) classes += 'long-overdue';
+    else if (now.diff(theDate, 'minute') >= 0) classes += 'due';
+    else if (now.diff(theDate, 'days') >= -1) classes += 'almost-due';
     return classes;
   }
 
@@ -337,12 +344,9 @@ class CardEndDate extends CardDate {
     let classes = 'end-date' + ' ';
     const dueAt = this.data().getDue();
     const theDate = this.date.get();
-    if (theDate.diff(dueAt, 'days') >= 2)
-      classes += 'long-overdue';
-    else if (theDate.diff(dueAt, 'days') >= 0)
-      classes += 'due';
-    else if (theDate.diff(dueAt, 'days') >= -2)
-      classes += 'almost-due';
+    if (theDate.diff(dueAt, 'days') >= 2) classes += 'long-overdue';
+    else if (theDate.diff(dueAt, 'days') >= 0) classes += 'due';
+    else if (theDate.diff(dueAt, 'days') >= -2) classes += 'almost-due';
     return classes;
   }
 
@@ -362,22 +366,22 @@ CardEndDate.register('cardEndDate');
   showDate() {
     return this.date.get().format('l');
   }
-}).register('minicardReceivedDate');
+}.register('minicardReceivedDate'));
 
 (class extends CardStartDate {
   showDate() {
     return this.date.get().format('l');
   }
-}).register('minicardStartDate');
+}.register('minicardStartDate'));
 
 (class extends CardDueDate {
   showDate() {
     return this.date.get().format('l');
   }
-}).register('minicardDueDate');
+}.register('minicardDueDate'));
 
 (class extends CardEndDate {
   showDate() {
     return this.date.get().format('l');
   }
-}).register('minicardEndDate');
+}.register('minicardEndDate'));

+ 221 - 168
client/components/cards/cardDetails.js

@@ -27,7 +27,7 @@ BlazeComponent.extendComponent({
   onCreated() {
     this.currentBoard = Boards.findOne(Session.get('currentBoard'));
     this.isLoaded = new ReactiveVar(false);
-    const boardBody =  this.parentComponent().parentComponent();
+    const boardBody = this.parentComponent().parentComponent();
     //in Miniview parent is Board, not BoardBody.
     if (boardBody !== null) {
       boardBody.showOverlay.set(true);
@@ -48,7 +48,11 @@ BlazeComponent.extendComponent({
   },
 
   canModifyCard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 
   scrollParentContainer() {
@@ -80,18 +84,17 @@ BlazeComponent.extendComponent({
     const cardContainerScrollTop = $cardContainer.scrollTop();
 
     let topOffset = false;
-    if(cardViewStartTop !== 100){
+    if (cardViewStartTop !== 100) {
       topOffset = cardViewStartTop - 100;
     }
-    if(topOffset !== false) {
+    if (topOffset !== false) {
       bodyBoardComponent.scrollTop(cardContainerScrollTop + topOffset);
     }
-
   },
 
   presentParentTask() {
     let result = this.currentBoard.presentParentTask;
-    if ((result === null) || (result === undefined)) {
+    if (result === null || result === undefined) {
       result = 'no-parent';
     }
     return result;
@@ -116,7 +119,13 @@ BlazeComponent.extendComponent({
   onRendered() {
     if (!Utils.isMiniScreen()) {
       Meteor.setTimeout(() => {
-        $('.card-details').mCustomScrollbar({theme:'minimal-dark', setWidth: false, setLeft: 0, scrollbarPosition: 'outside', mouseWheel: true });
+        $('.card-details').mCustomScrollbar({
+          theme: 'minimal-dark',
+          setWidth: false,
+          setLeft: 0,
+          scrollbarPosition: 'outside',
+          mouseWheel: true,
+        });
         this.scrollParentContainer();
       }, 500);
     }
@@ -212,7 +221,7 @@ BlazeComponent.extendComponent({
   },
 
   onDestroyed() {
-    const parentComponent =  this.parentComponent().parentComponent();
+    const parentComponent = this.parentComponent().parentComponent();
     //on mobile view parent is Board, not board body.
     if (parentComponent === null) return;
     parentComponent.showOverlay.set(false);
@@ -228,56 +237,64 @@ BlazeComponent.extendComponent({
       },
     };
 
-    return [{
-      ...events,
-      'click .js-close-card-details' () {
-        Utils.goBoardId(this.data().boardId);
-      },
-      'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
-      'submit .js-card-description' (evt) {
-        evt.preventDefault();
-        const description = this.currentComponent().getValue();
-        this.data().setDescription(description);
-      },
-      'submit .js-card-details-title' (evt) {
-        evt.preventDefault();
-        const title = this.currentComponent().getValue().trim();
-        if (title) {
-          this.data().setTitle(title);
-        }
-      },
-      'submit .js-card-details-assigner'(evt) {
-        evt.preventDefault();
-        const assigner = this.currentComponent().getValue().trim();
-        if (assigner) {
-          this.data().setAssignedBy(assigner);
-        }
-      },
-      'submit .js-card-details-requester'(evt) {
-        evt.preventDefault();
-        const requester = this.currentComponent().getValue().trim();
-        if (requester) {
-          this.data().setRequestedBy(requester);
-        }
-      },
-      'click .js-member': Popup.open('cardMember'),
-      'click .js-add-members': Popup.open('cardMembers'),
-      'click .js-add-labels': Popup.open('cardLabels'),
-      'click .js-received-date': Popup.open('editCardReceivedDate'),
-      'click .js-start-date': Popup.open('editCardStartDate'),
-      'click .js-due-date': Popup.open('editCardDueDate'),
-      'click .js-end-date': Popup.open('editCardEndDate'),
-      'mouseenter .js-card-details' () {
-        const parentComponent =  this.parentComponent().parentComponent();
-        //on mobile view parent is Board, not BoardBody.
-        if (parentComponent === null) return;
-        parentComponent.showOverlay.set(true);
-        parentComponent.mouseHasEnterCardDetails = true;
-      },
-      'click #toggleButton'() {
-        Meteor.call('toggleSystemMessages');
+    return [
+      {
+        ...events,
+        'click .js-close-card-details'() {
+          Utils.goBoardId(this.data().boardId);
+        },
+        'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
+        'submit .js-card-description'(event) {
+          event.preventDefault();
+          const description = this.currentComponent().getValue();
+          this.data().setDescription(description);
+        },
+        'submit .js-card-details-title'(event) {
+          event.preventDefault();
+          const title = this.currentComponent()
+            .getValue()
+            .trim();
+          if (title) {
+            this.data().setTitle(title);
+          }
+        },
+        'submit .js-card-details-assigner'(event) {
+          event.preventDefault();
+          const assigner = this.currentComponent()
+            .getValue()
+            .trim();
+          if (assigner) {
+            this.data().setAssignedBy(assigner);
+          }
+        },
+        'submit .js-card-details-requester'(event) {
+          event.preventDefault();
+          const requester = this.currentComponent()
+            .getValue()
+            .trim();
+          if (requester) {
+            this.data().setRequestedBy(requester);
+          }
+        },
+        'click .js-member': Popup.open('cardMember'),
+        'click .js-add-members': Popup.open('cardMembers'),
+        'click .js-add-labels': Popup.open('cardLabels'),
+        'click .js-received-date': Popup.open('editCardReceivedDate'),
+        'click .js-start-date': Popup.open('editCardStartDate'),
+        'click .js-due-date': Popup.open('editCardDueDate'),
+        'click .js-end-date': Popup.open('editCardEndDate'),
+        'mouseenter .js-card-details'() {
+          const parentComponent = this.parentComponent().parentComponent();
+          //on mobile view parent is Board, not BoardBody.
+          if (parentComponent === null) return;
+          parentComponent.showOverlay.set(true);
+          parentComponent.mouseHasEnterCardDetails = true;
+        },
+        'click #toggleButton'() {
+          Meteor.call('toggleSystemMessages');
+        },
       },
-    }];
+    ];
   },
 }).register('cardDetails');
 
@@ -297,7 +314,9 @@ BlazeComponent.extendComponent({
   close(isReset = false) {
     if (this.isOpen.get() && !isReset) {
       const draft = this.getValue().trim();
-      if (draft !== Cards.findOne(Session.get('currentCard')).getDescription()) {
+      if (
+        draft !== Cards.findOne(Session.get('currentCard')).getDescription()
+      ) {
         UnsavedEdits.set(this._getUnsavedEditKey(), this.getValue());
       }
     }
@@ -311,12 +330,14 @@ BlazeComponent.extendComponent({
 
   events() {
     const parentEvents = InlinedForm.prototype.events()[0];
-    return [{
-      ...parentEvents,
-      'click .js-close-inlined-form': this.reset,
-    }];
+    return [
+      {
+        ...parentEvents,
+        'click .js-close-inlined-form': this.reset,
+      },
+    ];
   }
-}).register('inlinedCardDescription');
+}.register('inlinedCardDescription'));
 
 Template.cardDetailsActionsPopup.helpers({
   isWatching() {
@@ -324,7 +345,11 @@ Template.cardDetailsActionsPopup.helpers({
   },
 
   canModifyCard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 });
 
@@ -342,23 +367,31 @@ Template.cardDetailsActionsPopup.events({
   'click .js-copy-card': Popup.open('copyCard'),
   'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'),
   'click .js-set-card-color': Popup.open('setCardColor'),
-  'click .js-move-card-to-top' (evt) {
-    evt.preventDefault();
-    const minOrder = _.min(this.list().cards(this.swimlaneId).map((c) => c.sort));
+  'click .js-move-card-to-top'(event) {
+    event.preventDefault();
+    const minOrder = _.min(
+      this.list()
+        .cards(this.swimlaneId)
+        .map(c => c.sort),
+    );
     this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
   },
-  'click .js-move-card-to-bottom' (evt) {
-    evt.preventDefault();
-    const maxOrder = _.max(this.list().cards(this.swimlaneId).map((c) => c.sort));
+  'click .js-move-card-to-bottom'(event) {
+    event.preventDefault();
+    const maxOrder = _.max(
+      this.list()
+        .cards(this.swimlaneId)
+        .map(c => c.sort),
+    );
     this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
   },
-  'click .js-archive' (evt) {
-    evt.preventDefault();
+  'click .js-archive'(event) {
+    event.preventDefault();
     this.archive();
     Popup.close();
   },
   'click .js-more': Popup.open('cardMore'),
-  'click .js-toggle-watch-card' () {
+  'click .js-toggle-watch-card'() {
     const currentCard = this;
     const level = currentCard.findWatcher(Meteor.userId()) ? null : 'watching';
     Meteor.call('watch', 'card', currentCard._id, level, (err, ret) => {
@@ -367,15 +400,15 @@ Template.cardDetailsActionsPopup.events({
   },
 });
 
-Template.editCardTitleForm.onRendered(function () {
+Template.editCardTitleForm.onRendered(function() {
   autosize(this.$('.js-edit-card-title'));
 });
 
 Template.editCardTitleForm.events({
-  'keydown .js-edit-card-title' (evt) {
+  'keydown .js-edit-card-title'(event) {
     // If enter key was pressed, submit the data
     // Unless the shift key is also being pressed
-    if (evt.keyCode === 13 && !evt.shiftKey) {
+    if (event.keyCode === 13 && !event.shiftKey) {
       $('.js-submit-edit-card-title-form').click();
     }
   },
@@ -386,9 +419,9 @@ Template.editCardRequesterForm.onRendered(function() {
 });
 
 Template.editCardRequesterForm.events({
-  'keydown .js-edit-card-requester'(evt) {
+  'keydown .js-edit-card-requester'(event) {
     // If enter key was pressed, submit the data
-    if (evt.keyCode === 13) {
+    if (event.keyCode === 13) {
       $('.js-submit-edit-card-requester-form').click();
     }
   },
@@ -399,16 +432,16 @@ Template.editCardAssignerForm.onRendered(function() {
 });
 
 Template.editCardAssignerForm.events({
-  'keydown .js-edit-card-assigner'(evt) {
+  'keydown .js-edit-card-assigner'(event) {
     // If enter key was pressed, submit the data
-    if (evt.keyCode === 13) {
+    if (event.keyCode === 13) {
       $('.js-submit-edit-card-assigner-form').click();
     }
   },
 });
 
 Template.moveCardPopup.events({
-  'click .js-done' () {
+  'click .js-done'() {
     // XXX We should *not* get the currentCard from the global state, but
     // instead from a “component” state.
     const card = Cards.findOne(Session.get('currentCard'));
@@ -429,13 +462,16 @@ BlazeComponent.extendComponent({
   },
 
   boards() {
-    const boards = Boards.find({
-      archived: false,
-      'members.userId': Meteor.userId(),
-      _id: {$ne: Meteor.user().getTemplatesBoardId()},
-    }, {
-      sort: ['title'],
-    });
+    const boards = Boards.find(
+      {
+        archived: false,
+        'members.userId': Meteor.userId(),
+        _id: { $ne: Meteor.user().getTemplatesBoardId() },
+      },
+      {
+        sort: ['title'],
+      },
+    );
     return boards;
   },
 
@@ -450,12 +486,14 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'change .js-select-boards'(evt) {
-        this.selectedBoardId.set($(evt.currentTarget).val());
-        subManager.subscribe('board', this.selectedBoardId.get(), false);
+    return [
+      {
+        'change .js-select-boards'(event) {
+          this.selectedBoardId.set($(event.currentTarget).val());
+          subManager.subscribe('board', this.selectedBoardId.get(), false);
+        },
       },
-    }];
+    ];
   },
 }).register('boardsAndLists');
 
@@ -471,7 +509,9 @@ Template.copyCardPopup.events({
     const textarea = $('#copy-card-title');
     const title = textarea.val().trim();
     // insert new card to the bottom of new list
-    card.sort = Lists.findOne(card.listId).cards().count();
+    card.sort = Lists.findOne(card.listId)
+      .cards()
+      .count();
 
     if (title) {
       card.title = title;
@@ -489,7 +529,7 @@ Template.copyCardPopup.events({
 });
 
 Template.copyChecklistToManyCardsPopup.events({
-  'click .js-done' () {
+  'click .js-done'() {
     const card = Cards.findOne(Session.get('currentCard'));
     const oldId = card._id;
     card._id = null;
@@ -502,11 +542,13 @@ Template.copyChecklistToManyCardsPopup.events({
     const textarea = $('#copy-card-title');
     const titleEntry = textarea.val().trim();
     // insert new card to the bottom of new list
-    card.sort = Lists.findOne(card.listId).cards().count();
+    card.sort = Lists.findOne(card.listId)
+      .cards()
+      .count();
 
     if (titleEntry) {
       const titleList = JSON.parse(titleEntry);
-      for (let i = 0; i < titleList.length; i++){
+      for (let i = 0; i < titleList.length; i++) {
         const obj = titleList[i];
         card.title = obj.title;
         card.description = obj.description;
@@ -519,12 +561,12 @@ Template.copyChecklistToManyCardsPopup.events({
         Filter.addException(_id);
 
         // copy checklists
-        Checklists.find({cardId: oldId}).forEach((ch) => {
+        Checklists.find({ cardId: oldId }).forEach(ch => {
           ch.copy(_id);
         });
 
         // copy subtasks
-        cursor = Cards.find({parentId: oldId});
+        cursor = Cards.find({ parentId: oldId });
         cursor.forEach(function() {
           'use strict';
           const subtask = arguments[0];
@@ -534,7 +576,7 @@ Template.copyChecklistToManyCardsPopup.events({
         });
 
         // copy card comments
-        CardComments.find({cardId: oldId}).forEach((cmt) => {
+        CardComments.find({ cardId: oldId }).forEach(cmt => {
           cmt.copy(_id);
         });
       }
@@ -550,7 +592,7 @@ BlazeComponent.extendComponent({
   },
 
   colors() {
-    return cardColors.map((color) => ({ color, name: '' }));
+    return cardColors.map(color => ({ color, name: '' }));
   },
 
   isSelected(color) {
@@ -561,19 +603,21 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-palette-color'() {
-        this.currentColor.set(this.currentData().color);
-      },
-      'click .js-submit' () {
-        this.currentCard.setColor(this.currentColor.get());
-        Popup.close();
+    return [
+      {
+        'click .js-palette-color'() {
+          this.currentColor.set(this.currentData().color);
+        },
+        'click .js-submit'() {
+          this.currentCard.setColor(this.currentColor.get());
+          Popup.close();
+        },
+        'click .js-remove-color'() {
+          this.currentCard.setColor(null);
+          Popup.close();
+        },
       },
-      'click .js-remove-color'() {
-        this.currentCard.setColor(null);
-        Popup.close();
-      },
-    }];
+    ];
   },
 }).register('setCardColorPopup');
 
@@ -592,15 +636,18 @@ BlazeComponent.extendComponent({
   },
 
   boards() {
-    const boards = Boards.find({
-      archived: false,
-      'members.userId': Meteor.userId(),
-      _id: {
-        $ne: Meteor.user().getTemplatesBoardId(),
+    const boards = Boards.find(
+      {
+        archived: false,
+        'members.userId': Meteor.userId(),
+        _id: {
+          $ne: Meteor.user().getTemplatesBoardId(),
+        },
       },
-    }, {
-      sort: ['title'],
-    });
+      {
+        sort: ['title'],
+      },
+    );
     return boards;
   },
 
@@ -609,7 +656,7 @@ BlazeComponent.extendComponent({
     if (this.parentBoard.get()) {
       return Cards.find({
         boardId: this.parentBoard.get(),
-        _id: {$ne: currentId},
+        _id: { $ne: currentId },
       });
     } else {
       return [];
@@ -642,63 +689,69 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-copy-card-link-to-clipboard' () {
-        // Clipboard code from:
-        // https://stackoverflow.com/questions/6300213/copy-selected-text-to-the-clipboard-without-using-flash-must-be-cross-browser
-        const StringToCopyElement = document.getElementById('cardURL');
-        StringToCopyElement.select();
-        if (document.execCommand('copy')) {
-          StringToCopyElement.blur();
-        } else {
-          document.getElementById('cardURL').selectionStart = 0;
-          document.getElementById('cardURL').selectionEnd = 999;
-          document.execCommand('copy');
-          if (window.getSelection) {
-            if (window.getSelection().empty) { // Chrome
-              window.getSelection().empty();
-            } else if (window.getSelection().removeAllRanges) { // Firefox
-              window.getSelection().removeAllRanges();
+    return [
+      {
+        'click .js-copy-card-link-to-clipboard'() {
+          // Clipboard code from:
+          // https://stackoverflow.com/questions/6300213/copy-selected-text-to-the-clipboard-without-using-flash-must-be-cross-browser
+          const StringToCopyElement = document.getElementById('cardURL');
+          StringToCopyElement.select();
+          if (document.execCommand('copy')) {
+            StringToCopyElement.blur();
+          } else {
+            document.getElementById('cardURL').selectionStart = 0;
+            document.getElementById('cardURL').selectionEnd = 999;
+            document.execCommand('copy');
+            if (window.getSelection) {
+              if (window.getSelection().empty) {
+                // Chrome
+                window.getSelection().empty();
+              } else if (window.getSelection().removeAllRanges) {
+                // Firefox
+                window.getSelection().removeAllRanges();
+              }
+            } else if (document.selection) {
+              // IE?
+              document.selection.empty();
             }
-          } else if (document.selection) { // IE?
-            document.selection.empty();
           }
-        }
-      },
-      'click .js-delete': Popup.afterConfirm('cardDelete', function () {
-        Popup.close();
-        Cards.remove(this._id);
-        Utils.goBoardId(this.boardId);
-      }),
-      'change .js-field-parent-board'(evt) {
-        const selection = $(evt.currentTarget).val();
-        const list = $('.js-field-parent-card');
-        if (selection === 'none') {
-          this.parentBoard.set(null);
-        } else {
-          subManager.subscribe('board', $(evt.currentTarget).val(), false);
-          this.parentBoard.set(selection);
-          list.prop('disabled', false);
-        }
-        this.setParentCardId(null);
-      },
-      'change .js-field-parent-card'(evt) {
-        const selection = $(evt.currentTarget).val();
-        this.setParentCardId(selection);
+        },
+        'click .js-delete': Popup.afterConfirm('cardDelete', function() {
+          Popup.close();
+          Cards.remove(this._id);
+          Utils.goBoardId(this.boardId);
+        }),
+        'change .js-field-parent-board'(event) {
+          const selection = $(event.currentTarget).val();
+          const list = $('.js-field-parent-card');
+          if (selection === 'none') {
+            this.parentBoard.set(null);
+          } else {
+            subManager.subscribe('board', $(event.currentTarget).val(), false);
+            this.parentBoard.set(selection);
+            list.prop('disabled', false);
+          }
+          this.setParentCardId(null);
+        },
+        'change .js-field-parent-card'(event) {
+          const selection = $(event.currentTarget).val();
+          this.setParentCardId(selection);
+        },
       },
-    }];
+    ];
   },
 }).register('cardMorePopup');
 
-
 // Close the card details pane by pressing escape
-EscapeActions.register('detailsPane',
+EscapeActions.register(
+  'detailsPane',
   () => {
     Utils.goBoardId(Session.get('currentBoard'));
   },
   () => {
     return !Session.equals('currentCard', null);
-  }, {
+  },
+  {
     noClickEscapeOn: '.js-card-details,.board-sidebar,#header',
-  }
+  },
 );

+ 37 - 25
client/components/cards/cardTime.js

@@ -20,29 +20,31 @@ BlazeComponent.extendComponent({
     this.card.setSpentTime(null);
   },
   events() {
-    return [{
-      //TODO : need checking this portion
-      'submit .edit-time'(evt) {
-        evt.preventDefault();
+    return [
+      {
+        //TODO : need checking this portion
+        'submit .edit-time'(evt) {
+          evt.preventDefault();
 
-        const spentTime = parseFloat(evt.target.time.value);
-        const isOvertime = this.card.getIsOvertime();
+          const spentTime = parseFloat(evt.target.time.value);
+          const isOvertime = this.card.getIsOvertime();
 
-        if (spentTime >= 0) {
-          this.storeTime(spentTime, isOvertime);
+          if (spentTime >= 0) {
+            this.storeTime(spentTime, isOvertime);
+            Popup.close();
+          } else {
+            this.error.set('invalid-time');
+            evt.target.time.focus();
+          }
+        },
+        'click .js-delete-time'(evt) {
+          evt.preventDefault();
+          this.deleteTime();
           Popup.close();
-        } else {
-          this.error.set('invalid-time');
-          evt.target.time.focus();
-        }
+        },
+        'click a.js-toggle-overtime': this.toggleOvertime,
       },
-      'click .js-delete-time'(evt) {
-        evt.preventDefault();
-        this.deleteTime();
-        Popup.close();
-      },
-      'click a.js-toggle-overtime': this.toggleOvertime,
-    }];
+    ];
   },
 }).register('editCardSpentTimePopup');
 
@@ -56,23 +58,33 @@ BlazeComponent.extendComponent({
   },
   showTitle() {
     if (this.data().getIsOvertime()) {
-      return `${TAPi18n.__('overtime')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
+      return `${TAPi18n.__(
+        'overtime',
+      )} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
     } else {
-      return `${TAPi18n.__('card-spent')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
+      return `${TAPi18n.__(
+        'card-spent',
+      )} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
     }
   },
   showTime() {
     return this.data().getSpentTime();
   },
   events() {
-    return [{
-      'click .js-edit-time': Popup.open('editCardSpentTime'),
-    }];
+    return [
+      {
+        'click .js-edit-time': Popup.open('editCardSpentTime'),
+      },
+    ];
   },
 }).register('cardSpentTime');
 
 Template.timeBadge.helpers({
   canModifyCard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 });

+ 45 - 25
client/components/cards/checklists.js

@@ -64,20 +64,22 @@ BlazeComponent.extendComponent({
   },
 
   canModifyCard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 }).register('checklistDetail');
 
 BlazeComponent.extendComponent({
-
   addChecklist(event) {
     event.preventDefault();
     const textarea = this.find('textarea.js-add-checklist-item');
     const title = textarea.value.trim();
     let cardId = this.currentData().cardId;
     const card = Cards.findOne(cardId);
-    if (card.isLinked())
-      cardId = card.linkedId;
+    if (card.isLinked()) cardId = card.linkedId;
 
     if (title) {
       Checklists.insert({
@@ -86,7 +88,9 @@ BlazeComponent.extendComponent({
         sort: card.checklists().count(),
       });
       setTimeout(() => {
-        this.$('.add-checklist-item').last().click();
+        this.$('.add-checklist-item')
+          .last()
+          .click();
       }, 100);
     }
     textarea.value = '';
@@ -113,7 +117,11 @@ BlazeComponent.extendComponent({
   },
 
   canModifyCard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 
   deleteChecklist() {
@@ -167,37 +175,43 @@ BlazeComponent.extendComponent({
   events() {
     const events = {
       'click .toggle-delete-checklist-dialog'(event) {
-        if($(event.target).hasClass('js-delete-checklist')){
+        if ($(event.target).hasClass('js-delete-checklist')) {
           this.checklistToDelete = this.currentData().checklist; //Store data context
         }
         this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());
       },
     };
 
-    return [{
-      ...events,
-      'submit .js-add-checklist': this.addChecklist,
-      'submit .js-edit-checklist-title': this.editChecklist,
-      'submit .js-add-checklist-item': this.addChecklistItem,
-      'submit .js-edit-checklist-item': this.editChecklistItem,
-      'click .js-delete-checklist-item': this.deleteItem,
-      'click .confirm-checklist-delete': this.deleteChecklist,
-      keydown: this.pressKey,
-    }];
+    return [
+      {
+        ...events,
+        'submit .js-add-checklist': this.addChecklist,
+        'submit .js-edit-checklist-title': this.editChecklist,
+        'submit .js-add-checklist-item': this.addChecklistItem,
+        'submit .js-edit-checklist-item': this.editChecklistItem,
+        'click .js-delete-checklist-item': this.deleteItem,
+        'click .confirm-checklist-delete': this.deleteChecklist,
+        keydown: this.pressKey,
+      },
+    ];
   },
 }).register('checklists');
 
 Template.checklistDeleteDialog.onCreated(() => {
   const $cardDetails = this.$('.card-details');
-  this.scrollState = { position: $cardDetails.scrollTop(), //save current scroll position
+  this.scrollState = {
+    position: $cardDetails.scrollTop(), //save current scroll position
     top: false, //required for smooth scroll animation
   };
   //Callback's purpose is to only prevent scrolling after animation is complete
-  $cardDetails.animate({ scrollTop: 0 }, 500, () => { this.scrollState.top = true; });
+  $cardDetails.animate({ scrollTop: 0 }, 500, () => {
+    this.scrollState.top = true;
+  });
 
   //Prevent scrolling while dialog is open
   $cardDetails.on('scroll', () => {
-    if(this.scrollState.top) { //If it's already in position, keep it there. Otherwise let animation scroll
+    if (this.scrollState.top) {
+      //If it's already in position, keep it there. Otherwise let animation scroll
       $cardDetails.scrollTop(0);
     }
   });
@@ -206,12 +220,16 @@ Template.checklistDeleteDialog.onCreated(() => {
 Template.checklistDeleteDialog.onDestroyed(() => {
   const $cardDetails = this.$('.card-details');
   $cardDetails.off('scroll'); //Reactivate scrolling
-  $cardDetails.animate( { scrollTop: this.scrollState.position });
+  $cardDetails.animate({ scrollTop: this.scrollState.position });
 });
 
 Template.checklistItemDetail.helpers({
   canModifyCard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 });
 
@@ -224,8 +242,10 @@ BlazeComponent.extendComponent({
     }
   },
   events() {
-    return [{
-      'click .js-checklist-item .check-box': this.toggleItem,
-    }];
+    return [
+      {
+        'click .js-checklist-item .check-box': this.toggleItem,
+      },
+    ];
   },
 }).register('checklistItemDetail');

+ 25 - 17
client/components/cards/labels.js

@@ -9,7 +9,7 @@ BlazeComponent.extendComponent({
   },
 
   labels() {
-    return labelColors.map((color) => ({ color, name: '' }));
+    return labelColors.map(color => ({ color, name: '' }));
   },
 
   isSelected(color) {
@@ -17,11 +17,13 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-palette-color'() {
-        this.currentColor.set(this.currentData().color);
+    return [
+      {
+        'click .js-palette-color'() {
+          this.currentColor.set(this.currentData().color);
+        },
       },
-    }];
+    ];
   },
 }).register('formLabel');
 
@@ -38,19 +40,19 @@ Template.createLabelPopup.helpers({
 });
 
 Template.cardLabelsPopup.events({
-  'click .js-select-label'(evt) {
+  'click .js-select-label'(event) {
     const card = Cards.findOne(Session.get('currentCard'));
     const labelId = this._id;
     card.toggleLabel(labelId);
-    evt.preventDefault();
+    event.preventDefault();
   },
   'click .js-edit-label': Popup.open('editLabel'),
   'click .js-add-label': Popup.open('createLabel'),
 });
 
 Template.formLabel.events({
-  'click .js-palette-color'(evt) {
-    const $this = $(evt.currentTarget);
+  'click .js-palette-color'(event) {
+    const $this = $(event.currentTarget);
 
     // hide selected ll colors
     $('.js-palette-select').addClass('hide');
@@ -62,11 +64,14 @@ Template.formLabel.events({
 
 Template.createLabelPopup.events({
   // Create the new label
-  'submit .create-label'(evt, tpl) {
-    evt.preventDefault();
+  'submit .create-label'(event, templateInstance) {
+    event.preventDefault();
     const board = Boards.findOne(Session.get('currentBoard'));
-    const name = tpl.$('#labelName').val().trim();
-    const color = Blaze.getData(tpl.find('.fa-check')).color;
+    const name = templateInstance
+      .$('#labelName')
+      .val()
+      .trim();
+    const color = Blaze.getData(templateInstance.find('.fa-check')).color;
     board.addLabel(name, color);
     Popup.back();
   },
@@ -78,11 +83,14 @@ Template.editLabelPopup.events({
     board.removeLabel(this._id);
     Popup.back(2);
   }),
-  'submit .edit-label'(evt, tpl) {
-    evt.preventDefault();
+  'submit .edit-label'(event, templateInstance) {
+    event.preventDefault();
     const board = Boards.findOne(Session.get('currentBoard'));
-    const name = tpl.$('#labelName').val().trim();
-    const color = Blaze.getData(tpl.find('.fa-check')).color;
+    const name = templateInstance
+      .$('#labelName')
+      .val()
+      .trim();
+    const color = Blaze.getData(templateInstance.find('.fa-check')).color;
     board.editLabel(this._id, name, color);
     Popup.back();
   },

+ 8 - 7
client/components/cards/minicard.js

@@ -8,13 +8,14 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-linked-link' () {
-        if (this.data().isLinkedCard())
-          Utils.goCardId(this.data().linkedId);
-        else if (this.data().isLinkedBoard())
-          Utils.goBoardId(this.data().linkedId);
+    return [
+      {
+        'click .js-linked-link'() {
+          if (this.data().isLinkedCard()) Utils.goCardId(this.data().linkedId);
+          else if (this.data().isLinkedBoard())
+            Utils.goBoardId(this.data().linkedId);
+        },
       },
-    }];
+    ];
   },
 }).register('minicard');

+ 37 - 19
client/components/cards/subtasks.js

@@ -1,11 +1,14 @@
 BlazeComponent.extendComponent({
   canModifyCard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 }).register('subtaskDetail');
 
 BlazeComponent.extendComponent({
-
   addSubtask(event) {
     event.preventDefault();
     const textarea = this.find('textarea.js-add-subtask-item');
@@ -38,9 +41,10 @@ BlazeComponent.extendComponent({
       // See https://github.com/wekan/wekan/issues/80
       Filter.addException(_id);
 
-
       setTimeout(() => {
-        this.$('.add-subtask-item').last().click();
+        this.$('.add-subtask-item')
+          .last()
+          .click();
       }, 100);
     }
     textarea.value = '';
@@ -48,7 +52,11 @@ BlazeComponent.extendComponent({
   },
 
   canModifyCard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 
   deleteSubtask() {
@@ -85,13 +93,13 @@ BlazeComponent.extendComponent({
   events() {
     const events = {
       'click .toggle-delete-subtask-dialog'(event) {
-        if($(event.target).hasClass('js-delete-subtask')){
+        if ($(event.target).hasClass('js-delete-subtask')) {
           this.subtaskToDelete = this.currentData().subtask; //Store data context
         }
         this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());
       },
       'click .js-view-subtask'(event) {
-        if($(event.target).hasClass('js-view-subtask')){
+        if ($(event.target).hasClass('js-view-subtask')) {
           const subtask = this.currentData().subtask;
           const board = subtask.board();
           FlowRouter.go('card', {
@@ -103,27 +111,33 @@ BlazeComponent.extendComponent({
       },
     };
 
-    return [{
-      ...events,
-      'submit .js-add-subtask': this.addSubtask,
-      'submit .js-edit-subtask-title': this.editSubtask,
-      'click .confirm-subtask-delete': this.deleteSubtask,
-      keydown: this.pressKey,
-    }];
+    return [
+      {
+        ...events,
+        'submit .js-add-subtask': this.addSubtask,
+        'submit .js-edit-subtask-title': this.editSubtask,
+        'click .confirm-subtask-delete': this.deleteSubtask,
+        keydown: this.pressKey,
+      },
+    ];
   },
 }).register('subtasks');
 
 Template.subtaskDeleteDialog.onCreated(() => {
   const $cardDetails = this.$('.card-details');
-  this.scrollState = { position: $cardDetails.scrollTop(), //save current scroll position
+  this.scrollState = {
+    position: $cardDetails.scrollTop(), //save current scroll position
     top: false, //required for smooth scroll animation
   };
   //Callback's purpose is to only prevent scrolling after animation is complete
-  $cardDetails.animate({ scrollTop: 0 }, 500, () => { this.scrollState.top = true; });
+  $cardDetails.animate({ scrollTop: 0 }, 500, () => {
+    this.scrollState.top = true;
+  });
 
   //Prevent scrolling while dialog is open
   $cardDetails.on('scroll', () => {
-    if(this.scrollState.top) { //If it's already in position, keep it there. Otherwise let animation scroll
+    if (this.scrollState.top) {
+      //If it's already in position, keep it there. Otherwise let animation scroll
       $cardDetails.scrollTop(0);
     }
   });
@@ -132,12 +146,16 @@ Template.subtaskDeleteDialog.onCreated(() => {
 Template.subtaskDeleteDialog.onDestroyed(() => {
   const $cardDetails = this.$('.card-details');
   $cardDetails.off('scroll'); //Reactivate scrolling
-  $cardDetails.animate( { scrollTop: this.scrollState.position });
+  $cardDetails.animate({ scrollTop: this.scrollState.position });
 });
 
 Template.subtaskItemDetail.helpers({
   canModifyCard() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 });
 

+ 47 - 38
client/components/import/import.js

@@ -56,7 +56,7 @@ BlazeComponent.extendComponent({
     const membersMapping = this.membersToMap.get();
     if (membersMapping) {
       const mappingById = {};
-      membersMapping.forEach((member) => {
+      membersMapping.forEach(member => {
         if (member.wekanId) {
           mappingById[member.id] = member.wekanId;
         }
@@ -64,7 +64,8 @@ BlazeComponent.extendComponent({
       additionalData.membersMapping = mappingById;
     }
     this.membersToMap.set([]);
-    Meteor.call('importBoard',
+    Meteor.call(
+      'importBoard',
       this.importedData.get(),
       additionalData,
       this.importSource,
@@ -76,7 +77,7 @@ BlazeComponent.extendComponent({
           Session.set('fromBoard', null);
           Utils.goBoardId(res);
         }
-      }
+      },
     );
   },
 
@@ -84,12 +85,12 @@ BlazeComponent.extendComponent({
     const importSource = Session.get('importSource');
     let membersToMap;
     switch (importSource) {
-    case 'trello':
-      membersToMap = trelloMembersMapper.getMembersToMap(dataObject);
-      break;
-    case 'wekan':
-      membersToMap = wekanMembersMapper.getMembersToMap(dataObject);
-      break;
+      case 'trello':
+        membersToMap = trelloMembersMapper.getMembersToMap(dataObject);
+        break;
+      case 'wekan':
+        membersToMap = wekanMembersMapper.getMembersToMap(dataObject);
+        break;
     }
     return membersToMap;
   },
@@ -109,22 +110,26 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      submit(evt) {
-        return this.parentComponent().importData(evt);
+    return [
+      {
+        submit(evt) {
+          return this.parentComponent().importData(evt);
+        },
       },
-    }];
+    ];
   },
 }).register('importTextarea');
 
 BlazeComponent.extendComponent({
   onCreated() {
     this.autorun(() => {
-      this.parentComponent().membersToMap.get().forEach(({ wekanId }) => {
-        if (wekanId) {
-          this.subscribe('user-miniprofile', wekanId);
-        }
-      });
+      this.parentComponent()
+        .membersToMap.get()
+        .forEach(({ wekanId }) => {
+          if (wekanId) {
+            this.subscribe('user-miniprofile', wekanId);
+          }
+        });
     });
   },
 
@@ -149,23 +154,23 @@ BlazeComponent.extendComponent({
   _setPropertyForMember(property, value, memberId, unset = false) {
     const listOfMembers = this.members();
     let finder = null;
-    if(memberId) {
-      finder = (member) => member.id === memberId;
+    if (memberId) {
+      finder = member => member.id === memberId;
     } else {
-      finder = (member) => member.selected;
+      finder = member => member.selected;
     }
-    listOfMembers.forEach((member) => {
-      if(finder(member)) {
-        if(value !== null) {
+    listOfMembers.forEach(member => {
+      if (finder(member)) {
+        if (value !== null) {
           member[property] = value;
         } else {
           delete member[property];
         }
-        if(!unset) {
+        if (!unset) {
           // we shortcut if we don't care about unsetting the others
           return false;
         }
-      } else if(unset) {
+      } else if (unset) {
         delete member[property];
       }
       return true;
@@ -186,9 +191,9 @@ BlazeComponent.extendComponent({
     const allMembers = this.members();
     let finder = null;
     if (memberId) {
-      finder = (user) => user.id === memberId;
+      finder = user => user.id === memberId;
     } else {
-      finder = (user) => user.selected;
+      finder = user => user.selected;
     }
     return allMembers.find(finder);
   },
@@ -197,7 +202,7 @@ BlazeComponent.extendComponent({
     return this._setPropertyForMember('wekanId', wekanId, null);
   },
 
-  unmapMember(memberId){
+  unmapMember(memberId) {
     return this._setPropertyForMember('wekanId', null, memberId);
   },
 
@@ -208,7 +213,7 @@ BlazeComponent.extendComponent({
 
   onMapMember(evt) {
     const memberToMap = this.currentData();
-    if(memberToMap.wekan) {
+    if (memberToMap.wekan) {
       // todo xxx ask for confirmation?
       this.unmapMember(memberToMap.id);
     } else {
@@ -218,10 +223,12 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'submit': this.onSubmit,
-      'click .js-select-member': this.onMapMember,
-    }];
+    return [
+      {
+        submit: this.onSubmit,
+        'click .js-select-member': this.onMapMember,
+      },
+    ];
   },
 }).register('importMapMembers');
 
@@ -230,14 +237,16 @@ BlazeComponent.extendComponent({
     this.find('.js-map-member input').focus();
   },
 
-  onSelectUser(){
+  onSelectUser() {
     Popup.getOpenerComponent().mapSelectedMember(this.currentData()._id);
     Popup.back();
   },
 
   events() {
-    return [{
-      'click .js-select-import': this.onSelectUser,
-    }];
+    return [
+      {
+        'click .js-select-import': this.onSelectUser,
+      },
+    ];
   },
 }).register('importMapMembersAddPopup');

+ 1 - 1
client/components/import/trelloMembersMapper.js

@@ -4,7 +4,7 @@ export function getMembersToMap(data) {
   // imported member
   const membersToMap = data.members;
   // auto-map based on username
-  membersToMap.forEach((importedMember) => {
+  membersToMap.forEach(importedMember => {
     const wekanUser = Users.findOne({ username: importedMember.username });
     if (wekanUser) {
       importedMember.wekanId = wekanUser._id;

+ 2 - 2
client/components/import/wekanMembersMapper.js

@@ -5,10 +5,10 @@ export function getMembersToMap(data) {
   const membersToMap = data.members;
   const users = data.users;
   // auto-map based on username
-  membersToMap.forEach((importedMember) => {
+  membersToMap.forEach(importedMember => {
     importedMember.id = importedMember.userId;
     delete importedMember.userId;
-    const user = users.filter((user) => {
+    const user = users.filter(user => {
       return user._id === importedMember.id;
     })[0];
     if (user.profile && user.profile.fullname) {

+ 32 - 10
client/components/lists/list.js

@@ -21,14 +21,18 @@ BlazeComponent.extendComponent({
     const boardComponent = this.parentComponent().parentComponent();
 
     function userIsMember() {
-      return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+      return (
+        Meteor.user() &&
+        Meteor.user().isBoardMember() &&
+        !Meteor.user().isCommentOnly()
+      );
     }
 
     const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)';
     const $cards = this.$('.js-minicards');
 
-    if(window.matchMedia('(max-width: 1199px)').matches) {
-      $( '.js-minicards' ).sortable({
+    if (window.matchMedia('(max-width: 1199px)').matches) {
+      $('.js-minicards').sortable({
         handle: '.handle',
       });
     }
@@ -42,10 +46,16 @@ BlazeComponent.extendComponent({
         if (MultiSelection.isActive()) {
           const andNOthers = $cards.find('.js-minicard.is-checked').length - 1;
           if (andNOthers > 0) {
-            helper.append($(Blaze.toHTML(HTML.DIV(
-              { 'class': 'and-n-other' },
-              TAPi18n.__('and-n-other-card', { count: andNOthers })
-            ))));
+            helper.append(
+              $(
+                Blaze.toHTML(
+                  HTML.DIV(
+                    { class: 'and-n-other' },
+                    TAPi18n.__('and-n-other-card', { count: andNOthers }),
+                  ),
+                ),
+              ),
+            );
           }
         }
         return helper;
@@ -70,9 +80,16 @@ BlazeComponent.extendComponent({
         const currentBoard = Boards.findOne(Session.get('currentBoard'));
         let swimlaneId = '';
         const boardView = (Meteor.user().profile || {}).boardView;
-        if (boardView === 'board-view-swimlanes' || currentBoard.isTemplatesBoard())
+        if (
+          boardView === 'board-view-swimlanes' ||
+          currentBoard.isTemplatesBoard()
+        )
           swimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0))._id;
-        else if ((boardView === 'board-view-lists') || (boardView === 'board-view-cal') || !boardView)
+        else if (
+          boardView === 'board-view-lists' ||
+          boardView === 'board-view-cal' ||
+          !boardView
+        )
           swimlaneId = currentBoard.getDefaultSwimline()._id;
 
         // Normally the jquery-ui sortable library moves the dragged DOM element
@@ -86,7 +103,12 @@ BlazeComponent.extendComponent({
 
         if (MultiSelection.isActive()) {
           Cards.find(MultiSelection.getMongoSelector()).forEach((card, i) => {
-            card.move(currentBoard._id, swimlaneId, listId, sortIndex.base + i * sortIndex.increment);
+            card.move(
+              currentBoard._id,
+              swimlaneId,
+              listId,
+              sortIndex.base + i * sortIndex.increment,
+            );
           });
         } else {
           const cardDomElement = ui.item.get(0);

+ 317 - 229
client/components/lists/listBody.js

@@ -16,7 +16,7 @@ BlazeComponent.extendComponent({
     options.position = options.position || 'top';
 
     const forms = this.childComponents('inlinedForm');
-    let form = forms.find((component) => {
+    let form = forms.find(component => {
       return component.data().position === options.position;
     });
     if (!form && forms.length > 0) {
@@ -52,11 +52,12 @@ BlazeComponent.extendComponent({
     let cardType = 'cardType-card';
     if (title) {
       if (board.isTemplatesBoard()) {
-        swimlaneId = this.parentComponent().parentComponent().data()._id; // Always swimlanes view
+        swimlaneId = this.parentComponent()
+          .parentComponent()
+          .data()._id; // Always swimlanes view
         const swimlane = Swimlanes.findOne(swimlaneId);
         // If this is the card templates swimlane, insert a card template
-        if (swimlane.isCardTemplatesSwimlane())
-          cardType = 'template-card';
+        if (swimlane.isCardTemplatesSwimlane()) cardType = 'template-card';
         // If this is the board templates swimlane, insert a board template and a linked card
         else if (swimlane.isBoardTemplatesSwimlane()) {
           linkedId = Boards.insert({
@@ -71,8 +72,14 @@ BlazeComponent.extendComponent({
           cardType = 'cardType-linkedBoard';
         }
       } else if (boardView === 'board-view-swimlanes')
-        swimlaneId = this.parentComponent().parentComponent().data()._id;
-      else if ((boardView === 'board-view-lists') || (boardView === 'board-view-cal') || !boardView)
+        swimlaneId = this.parentComponent()
+          .parentComponent()
+          .data()._id;
+      else if (
+        boardView === 'board-view-lists' ||
+        boardView === 'board-view-cal' ||
+        !boardView
+      )
         swimlaneId = board.getDefaultSwimline()._id;
 
       const _id = Cards.insert({
@@ -91,7 +98,9 @@ BlazeComponent.extendComponent({
       // if the displayed card count is less than the total cards in the list,
       // we need to increment the displayed card count to prevent the spinner
       // to appear
-      const cardCount = this.data().cards(this.idOrNull(swimlaneId)).count();
+      const cardCount = this.data()
+        .cards(this.idOrNull(swimlaneId))
+        .count();
       if (this.cardlimit.get() < cardCount) {
         this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
       }
@@ -149,8 +158,12 @@ BlazeComponent.extendComponent({
 
   idOrNull(swimlaneId) {
     const currentUser = Meteor.user();
-    if ((currentUser.profile || {}).boardView === 'board-view-swimlanes' ||
-        this.data().board().isTemplatesBoard())
+    if (
+      (currentUser.profile || {}).boardView === 'board-view-swimlanes' ||
+      this.data()
+        .board()
+        .isTemplatesBoard()
+    )
       return swimlaneId;
     return undefined;
   },
@@ -161,8 +174,7 @@ BlazeComponent.extendComponent({
       listId: this.currentData()._id,
       archived: false,
     };
-    if (swimlaneId)
-      selector.swimlaneId = swimlaneId;
+    if (swimlaneId) selector.swimlaneId = swimlaneId;
     return Cards.find(Filter.mongoSelector(selector), {
       sort: ['sort'],
       limit,
@@ -175,21 +187,32 @@ BlazeComponent.extendComponent({
   },
 
   canSeeAddCard() {
-    return !this.reachedWipLimit() && Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      !this.reachedWipLimit() &&
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 
   reachedWipLimit() {
     const list = Template.currentData();
-    return !list.getWipLimit('soft') && list.getWipLimit('enabled') && list.getWipLimit('value') <= list.cards().count();
+    return (
+      !list.getWipLimit('soft') &&
+      list.getWipLimit('enabled') &&
+      list.getWipLimit('value') <= list.cards().count()
+    );
   },
 
   events() {
-    return [{
-      'click .js-minicard': this.clickOnMiniCard,
-      'click .js-toggle-multi-selection': this.toggleMultiSelection,
-      'click .open-minicard-composer': this.scrollToBottom,
-      submit: this.addCard,
-    }];
+    return [
+      {
+        'click .js-minicard': this.clickOnMiniCard,
+        'click .js-toggle-multi-selection': this.toggleMultiSelection,
+        'click .open-minicard-composer': this.scrollToBottom,
+        submit: this.addCard,
+      },
+    ];
   },
 }).register('listBody');
 
@@ -212,10 +235,15 @@ BlazeComponent.extendComponent({
 
     const currentBoardId = Session.get('currentBoard');
     arr = [];
-    _.forEach(Boards.findOne(currentBoardId).customFields().fetch(), function(field){
-      if(field.automaticallyOnCard)
-        arr.push({_id: field._id, value: null});
-    });
+    _.forEach(
+      Boards.findOne(currentBoardId)
+        .customFields()
+        .fetch(),
+      function(field) {
+        if (field.automaticallyOnCard)
+          arr.push({ _id: field._id, value: null });
+      },
+    );
     this.customFields.set(arr);
   },
 
@@ -227,7 +255,7 @@ BlazeComponent.extendComponent({
 
   getLabels() {
     const currentBoardId = Session.get('currentBoard');
-    return Boards.findOne(currentBoardId).labels.filter((label) => {
+    return Boards.findOne(currentBoardId).labels.filter(label => {
       return this.labels.get().indexOf(label._id) > -1;
     });
   },
@@ -257,18 +285,20 @@ BlazeComponent.extendComponent({
       }
 
       BlazeComponent.getComponentForElement(nextList).openForm({
-        position:this.data().position,
+        position: this.data().position,
       });
     }
   },
 
   events() {
-    return [{
-      keydown: this.pressKey,
-      'click .js-link': Popup.open('linkCard'),
-      'click .js-search': Popup.open('searchElement'),
-      'click .js-card-template': Popup.open('searchElement'),
-    }];
+    return [
+      {
+        keydown: this.pressKey,
+        'click .js-link': Popup.open('linkCard'),
+        'click .js-search': Popup.open('searchElement'),
+        'click .js-card-template': Popup.open('searchElement'),
+      },
+    ];
   },
 
   onRendered() {
@@ -277,66 +307,75 @@ BlazeComponent.extendComponent({
 
     autosize($textarea);
 
-    $textarea.escapeableTextComplete([
-      // User mentions
-      {
-        match: /\B@([\w.]*)$/,
-        search(term, callback) {
-          const currentBoard = Boards.findOne(Session.get('currentBoard'));
-          callback($.map(currentBoard.activeMembers(), (member) => {
-            const user = Users.findOne(member.userId);
-            return user.username.indexOf(term) === 0 ? user : null;
-          }));
-        },
-        template(user) {
-          return user.username;
-        },
-        replace(user) {
-          toggleValueInReactiveArray(editor.members, user._id);
-          return '';
+    $textarea.escapeableTextComplete(
+      [
+        // User mentions
+        {
+          match: /\B@([\w.]*)$/,
+          search(term, callback) {
+            const currentBoard = Boards.findOne(Session.get('currentBoard'));
+            callback(
+              $.map(currentBoard.activeMembers(), member => {
+                const user = Users.findOne(member.userId);
+                return user.username.indexOf(term) === 0 ? user : null;
+              }),
+            );
+          },
+          template(user) {
+            return user.username;
+          },
+          replace(user) {
+            toggleValueInReactiveArray(editor.members, user._id);
+            return '';
+          },
+          index: 1,
         },
-        index: 1,
-      },
 
-      // Labels
-      {
-        match: /\B#(\w*)$/,
-        search(term, callback) {
-          const currentBoard = Boards.findOne(Session.get('currentBoard'));
-          callback($.map(currentBoard.labels, (label) => {
-            if (label.name.indexOf(term) > -1 ||
-              label.color.indexOf(term) > -1) {
-              return label;
-            }
-            return null;
-          }));
-        },
-        template(label) {
-          return Blaze.toHTMLWithData(Template.autocompleteLabelLine, {
-            hasNoName: !label.name,
-            colorName: label.color,
-            labelName: label.name || label.color,
-          });
+        // Labels
+        {
+          match: /\B#(\w*)$/,
+          search(term, callback) {
+            const currentBoard = Boards.findOne(Session.get('currentBoard'));
+            callback(
+              $.map(currentBoard.labels, label => {
+                if (
+                  label.name.indexOf(term) > -1 ||
+                  label.color.indexOf(term) > -1
+                ) {
+                  return label;
+                }
+                return null;
+              }),
+            );
+          },
+          template(label) {
+            return Blaze.toHTMLWithData(Template.autocompleteLabelLine, {
+              hasNoName: !label.name,
+              colorName: label.color,
+              labelName: label.name || label.color,
+            });
+          },
+          replace(label) {
+            toggleValueInReactiveArray(editor.labels, label._id);
+            return '';
+          },
+          index: 1,
         },
-        replace(label) {
-          toggleValueInReactiveArray(editor.labels, label._id);
-          return '';
+      ],
+      {
+        // When the autocomplete menu is shown we want both a press of both `Tab`
+        // or `Enter` to validation the auto-completion. We also need to stop the
+        // event propagation to prevent the card from submitting (on `Enter`) or
+        // going on the next column (on `Tab`).
+        onKeydown(evt, commands) {
+          if (evt.keyCode === 9 || evt.keyCode === 13) {
+            evt.stopPropagation();
+            return commands.KEY_ENTER;
+          }
+          return null;
         },
-        index: 1,
       },
-    ], {
-      // When the autocomplete menu is shown we want both a press of both `Tab`
-      // or `Enter` to validation the auto-completion. We also need to stop the
-      // event propagation to prevent the card from submitting (on `Enter`) or
-      // going on the next column (on `Tab`).
-      onKeydown(evt, commands) {
-        if (evt.keyCode === 9 || evt.keyCode === 13) {
-          evt.stopPropagation();
-          return commands.KEY_ENTER;
-        }
-        return null;
-      },
-    });
+    );
   },
 }).register('addCardForm');
 
@@ -354,24 +393,29 @@ BlazeComponent.extendComponent({
     const list = $(Popup._getTopStack().openerElement).closest('.js-list');
     this.listId = Blaze.getData(list[0])._id;
     // Swimlane where to insert card
-    const swimlane = $(Popup._getTopStack().openerElement).closest('.js-swimlane');
+    const swimlane = $(Popup._getTopStack().openerElement).closest(
+      '.js-swimlane',
+    );
     this.swimlaneId = '';
     const boardView = (Meteor.user().profile || {}).boardView;
     if (boardView === 'board-view-swimlanes')
       this.swimlaneId = Blaze.getData(swimlane[0])._id;
     else if (boardView === 'board-view-lists' || !boardView)
-      this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
+      this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
   },
 
   boards() {
-    const boards = Boards.find({
-      archived: false,
-      'members.userId': Meteor.userId(),
-      _id: {$ne: Session.get('currentBoard')},
-      type: 'board',
-    }, {
-      sort: ['title'],
-    });
+    const boards = Boards.find(
+      {
+        archived: false,
+        'members.userId': Meteor.userId(),
+        _id: { $ne: Session.get('currentBoard') },
+        type: 'board',
+      },
+      {
+        sort: ['title'],
+      },
+    );
     return boards;
   },
 
@@ -379,7 +423,7 @@ BlazeComponent.extendComponent({
     if (!this.selectedBoardId.get()) {
       return [];
     }
-    const swimlanes = Swimlanes.find({boardId: this.selectedBoardId.get()});
+    const swimlanes = Swimlanes.find({ boardId: this.selectedBoardId.get() });
     if (swimlanes.count())
       this.selectedSwimlaneId.set(swimlanes.fetch()[0]._id);
     return swimlanes;
@@ -389,9 +433,8 @@ BlazeComponent.extendComponent({
     if (!this.selectedBoardId.get()) {
       return [];
     }
-    const lists = Lists.find({boardId: this.selectedBoardId.get()});
-    if (lists.count())
-      this.selectedListId.set(lists.fetch()[0]._id);
+    const lists = Lists.find({ boardId: this.selectedBoardId.get() });
+    if (lists.count()) this.selectedListId.set(lists.fetch()[0]._id);
     return lists;
   },
 
@@ -399,73 +442,84 @@ BlazeComponent.extendComponent({
     if (!this.board) {
       return [];
     }
-    const ownCardsIds = this.board.cards().map((card) => { return card.linkedId || card._id; });
+    const ownCardsIds = this.board.cards().map(card => {
+      return card.linkedId || card._id;
+    });
     return Cards.find({
       boardId: this.selectedBoardId.get(),
       swimlaneId: this.selectedSwimlaneId.get(),
       listId: this.selectedListId.get(),
       archived: false,
-      linkedId: {$nin: ownCardsIds},
-      _id: {$nin: ownCardsIds},
-      type: {$nin: ['template-card']},
+      linkedId: { $nin: ownCardsIds },
+      _id: { $nin: ownCardsIds },
+      type: { $nin: ['template-card'] },
     });
   },
 
   events() {
-    return [{
-      'change .js-select-boards'(evt) {
-        subManager.subscribe('board', $(evt.currentTarget).val(), false);
-        this.selectedBoardId.set($(evt.currentTarget).val());
-      },
-      'change .js-select-swimlanes'(evt) {
-        this.selectedSwimlaneId.set($(evt.currentTarget).val());
-      },
-      'change .js-select-lists'(evt) {
-        this.selectedListId.set($(evt.currentTarget).val());
-      },
-      'click .js-done' (evt) {
-        // LINK CARD
-        evt.stopPropagation();
-        evt.preventDefault();
-        const linkedId = $('.js-select-cards option:selected').val();
-        if (!linkedId) {
+    return [
+      {
+        'change .js-select-boards'(evt) {
+          subManager.subscribe('board', $(evt.currentTarget).val(), false);
+          this.selectedBoardId.set($(evt.currentTarget).val());
+        },
+        'change .js-select-swimlanes'(evt) {
+          this.selectedSwimlaneId.set($(evt.currentTarget).val());
+        },
+        'change .js-select-lists'(evt) {
+          this.selectedListId.set($(evt.currentTarget).val());
+        },
+        'click .js-done'(evt) {
+          // LINK CARD
+          evt.stopPropagation();
+          evt.preventDefault();
+          const linkedId = $('.js-select-cards option:selected').val();
+          if (!linkedId) {
+            Popup.close();
+            return;
+          }
+          const _id = Cards.insert({
+            title: $('.js-select-cards option:selected').text(), //dummy
+            listId: this.listId,
+            swimlaneId: this.swimlaneId,
+            boardId: this.boardId,
+            sort: Lists.findOne(this.listId)
+              .cards()
+              .count(),
+            type: 'cardType-linkedCard',
+            linkedId,
+          });
+          Filter.addException(_id);
           Popup.close();
-          return;
-        }
-        const _id = Cards.insert({
-          title: $('.js-select-cards option:selected').text(), //dummy
-          listId: this.listId,
-          swimlaneId: this.swimlaneId,
-          boardId: this.boardId,
-          sort: Lists.findOne(this.listId).cards().count(),
-          type: 'cardType-linkedCard',
-          linkedId,
-        });
-        Filter.addException(_id);
-        Popup.close();
-      },
-      'click .js-link-board' (evt) {
-        //LINK BOARD
-        evt.stopPropagation();
-        evt.preventDefault();
-        const impBoardId = $('.js-select-boards option:selected').val();
-        if (!impBoardId || Cards.findOne({linkedId: impBoardId, archived: false})) {
+        },
+        'click .js-link-board'(evt) {
+          //LINK BOARD
+          evt.stopPropagation();
+          evt.preventDefault();
+          const impBoardId = $('.js-select-boards option:selected').val();
+          if (
+            !impBoardId ||
+            Cards.findOne({ linkedId: impBoardId, archived: false })
+          ) {
+            Popup.close();
+            return;
+          }
+          const _id = Cards.insert({
+            title: $('.js-select-boards option:selected').text(), //dummy
+            listId: this.listId,
+            swimlaneId: this.swimlaneId,
+            boardId: this.boardId,
+            sort: Lists.findOne(this.listId)
+              .cards()
+              .count(),
+            type: 'cardType-linkedBoard',
+            linkedId: impBoardId,
+          });
+          Filter.addException(_id);
           Popup.close();
-          return;
-        }
-        const _id = Cards.insert({
-          title: $('.js-select-boards option:selected').text(), //dummy
-          listId: this.listId,
-          swimlaneId: this.swimlaneId,
-          boardId: this.boardId,
-          sort: Lists.findOne(this.listId).cards().count(),
-          type: 'cardType-linkedBoard',
-          linkedId: impBoardId,
-        });
-        Filter.addException(_id);
-        Popup.close();
+        },
       },
-    }];
+    ];
   },
 }).register('linkCardPopup');
 
@@ -475,11 +529,20 @@ BlazeComponent.extendComponent({
   },
 
   onCreated() {
-    this.isCardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-card-template');
-    this.isListTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-list-template');
-    this.isSwimlaneTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-open-add-swimlane-menu');
-    this.isBoardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-add-board');
-    this.isTemplateSearch = this.isCardTemplateSearch ||
+    this.isCardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
+      'js-card-template',
+    );
+    this.isListTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
+      'js-list-template',
+    );
+    this.isSwimlaneTemplateSearch = $(
+      Popup._getTopStack().openerElement,
+    ).hasClass('js-open-add-swimlane-menu');
+    this.isBoardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
+      'js-add-board',
+    );
+    this.isTemplateSearch =
+      this.isCardTemplateSearch ||
       this.isListTemplateSearch ||
       this.isSwimlaneTemplateSearch ||
       this.isBoardTemplateSearch;
@@ -491,7 +554,12 @@ BlazeComponent.extendComponent({
       board = Boards.findOne({
         archived: false,
         'members.userId': Meteor.userId(),
-        _id: {$nin: [Session.get('currentBoard'), (Meteor.user().profile || {}).templatesBoardId]},
+        _id: {
+          $nin: [
+            Session.get('currentBoard'),
+            (Meteor.user().profile || {}).templatesBoardId,
+          ],
+        },
       });
     }
     if (!board) {
@@ -509,11 +577,12 @@ BlazeComponent.extendComponent({
       subManager.subscribe('board', this.boardId, false);
       this.swimlaneId = '';
       // Swimlane where to insert card
-      const swimlane = $(Popup._getTopStack().openerElement).parents('.js-swimlane');
+      const swimlane = $(Popup._getTopStack().openerElement).parents(
+        '.js-swimlane',
+      );
       if ((Meteor.user().profile || {}).boardView === 'board-view-swimlanes')
         this.swimlaneId = Blaze.getData(swimlane[0])._id;
-      else
-        this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
+      else this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
       // List where to insert card
       const list = $(Popup._getTopStack().openerElement).closest('.js-list');
       this.listId = Blaze.getData(list[0])._id;
@@ -522,14 +591,17 @@ BlazeComponent.extendComponent({
   },
 
   boards() {
-    const boards = Boards.find({
-      archived: false,
-      'members.userId': Meteor.userId(),
-      _id: {$ne: Session.get('currentBoard')},
-      type: 'board',
-    }, {
-      sort: ['title'],
-    });
+    const boards = Boards.find(
+      {
+        archived: false,
+        'members.userId': Meteor.userId(),
+        _id: { $ne: Session.get('currentBoard') },
+        type: 'board',
+      },
+      {
+        sort: ['title'],
+      },
+    );
     return boards;
   },
 
@@ -546,7 +618,7 @@ BlazeComponent.extendComponent({
       return board.searchSwimlanes(this.term.get());
     } else if (this.isBoardTemplateSearch) {
       const boards = board.searchBoards(this.term.get());
-      boards.forEach((board) => {
+      boards.forEach(board => {
         subManager.subscribe('board', board.linkedId, false);
       });
       return boards;
@@ -556,60 +628,69 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'change .js-select-boards'(evt) {
-        subManager.subscribe('board', $(evt.currentTarget).val(), false);
-        this.selectedBoardId.set($(evt.currentTarget).val());
-      },
-      'submit .js-search-term-form'(evt) {
-        evt.preventDefault();
-        this.term.set(evt.target.searchTerm.value);
-      },
-      'click .js-minicard'(evt) {
-        // 0. Common
-        const title = $('.js-element-title').val().trim();
-        if (!title)
-          return;
-        const element = Blaze.getData(evt.currentTarget);
-        element.title = title;
-        let _id = '';
-        if (!this.isTemplateSearch || this.isCardTemplateSearch) {
-          // Card insertion
-          // 1. Common
-          element.sort = Lists.findOne(this.listId).cards().count();
-          // 1.A From template
-          if (this.isTemplateSearch) {
-            element.type = 'cardType-card';
-            element.linkedId = '';
-            _id = element.copy(this.boardId, this.swimlaneId, this.listId);
-            // 1.B Linked card
-          } else {
-            delete element._id;
-            element.type = 'cardType-linkedCard';
-            element.linkedId = element.linkedId || element._id;
-            _id = Cards.insert(element);
+    return [
+      {
+        'change .js-select-boards'(evt) {
+          subManager.subscribe('board', $(evt.currentTarget).val(), false);
+          this.selectedBoardId.set($(evt.currentTarget).val());
+        },
+        'submit .js-search-term-form'(evt) {
+          evt.preventDefault();
+          this.term.set(evt.target.searchTerm.value);
+        },
+        'click .js-minicard'(evt) {
+          // 0. Common
+          const title = $('.js-element-title')
+            .val()
+            .trim();
+          if (!title) return;
+          const element = Blaze.getData(evt.currentTarget);
+          element.title = title;
+          let _id = '';
+          if (!this.isTemplateSearch || this.isCardTemplateSearch) {
+            // Card insertion
+            // 1. Common
+            element.sort = Lists.findOne(this.listId)
+              .cards()
+              .count();
+            // 1.A From template
+            if (this.isTemplateSearch) {
+              element.type = 'cardType-card';
+              element.linkedId = '';
+              _id = element.copy(this.boardId, this.swimlaneId, this.listId);
+              // 1.B Linked card
+            } else {
+              delete element._id;
+              element.type = 'cardType-linkedCard';
+              element.linkedId = element.linkedId || element._id;
+              _id = Cards.insert(element);
+            }
+            Filter.addException(_id);
+            // List insertion
+          } else if (this.isListTemplateSearch) {
+            element.sort = Swimlanes.findOne(this.swimlaneId)
+              .lists()
+              .count();
+            element.type = 'list';
+            _id = element.copy(this.boardId, this.swimlaneId);
+          } else if (this.isSwimlaneTemplateSearch) {
+            element.sort = Boards.findOne(this.boardId)
+              .swimlanes()
+              .count();
+            element.type = 'swimlalne';
+            _id = element.copy(this.boardId);
+          } else if (this.isBoardTemplateSearch) {
+            board = Boards.findOne(element.linkedId);
+            board.sort = Boards.find({ archived: false }).count();
+            board.type = 'board';
+            board.title = element.title;
+            delete board.slug;
+            _id = board.copy();
           }
-          Filter.addException(_id);
-          // List insertion
-        } else if (this.isListTemplateSearch) {
-          element.sort = Swimlanes.findOne(this.swimlaneId).lists().count();
-          element.type = 'list';
-          _id = element.copy(this.boardId, this.swimlaneId);
-        } else if (this.isSwimlaneTemplateSearch) {
-          element.sort = Boards.findOne(this.boardId).swimlanes().count();
-          element.type = 'swimlalne';
-          _id = element.copy(this.boardId);
-        } else if (this.isBoardTemplateSearch) {
-          board = Boards.findOne(element.linkedId);
-          board.sort = Boards.find({archived: false}).count();
-          board.type = 'board';
-          board.title = element.title;
-          delete board.slug;
-          _id = board.copy();
-        }
-        Popup.close();
+          Popup.close();
+        },
       },
-    }];
+    ];
   },
 }).register('searchElementPopup');
 
@@ -622,15 +703,23 @@ BlazeComponent.extendComponent({
 
     const boardView = (Meteor.user().profile || {}).boardView;
     if (boardView === 'board-view-swimlanes')
-      this.swimlaneId = this.parentComponent().parentComponent().parentComponent().data()._id;
+      this.swimlaneId = this.parentComponent()
+        .parentComponent()
+        .parentComponent()
+        .data()._id;
   },
 
   onRendered() {
     this.spinner = this.find('.sk-spinner-list');
     this.container = this.$(this.spinner).parents('.js-perfect-scrollbar')[0];
 
-    $(this.container).on(`scroll.spinner_${this.swimlaneId}_${this.listId}`, () => this.updateList());
-    $(window).on(`resize.spinner_${this.swimlaneId}_${this.listId}`, () => this.updateList());
+    $(this.container).on(
+      `scroll.spinner_${this.swimlaneId}_${this.listId}`,
+      () => this.updateList(),
+    );
+    $(window).on(`resize.spinner_${this.swimlaneId}_${this.listId}`, () =>
+      this.updateList(),
+    );
 
     this.updateList();
   },
@@ -660,5 +749,4 @@ BlazeComponent.extendComponent({
 
     return bottomViewPosition > threshold;
   },
-
 }).register('spinnerList');

+ 87 - 55
client/components/lists/listHeader.js

@@ -6,12 +6,18 @@ Meteor.startup(() => {
 BlazeComponent.extendComponent({
   canSeeAddCard() {
     const list = Template.currentData();
-    return !list.getWipLimit('enabled') || list.getWipLimit('soft') || !this.reachedWipLimit();
-  },
-
-  editTitle(evt) {
-    evt.preventDefault();
-    const newTitle = this.childComponents('inlinedForm')[0].getValue().trim();
+    return (
+      !list.getWipLimit('enabled') ||
+      list.getWipLimit('soft') ||
+      !this.reachedWipLimit()
+    );
+  },
+
+  editTitle(event) {
+    event.preventDefault();
+    const newTitle = this.childComponents('inlinedForm')[0]
+      .getValue()
+      .trim();
     const list = this.currentData();
     if (newTitle) {
       list.rename(newTitle.trim());
@@ -32,14 +38,19 @@ BlazeComponent.extendComponent({
     let swimlaneId = '';
     const boardView = (Meteor.user().profile || {}).boardView;
     if (boardView === 'board-view-swimlanes')
-      swimlaneId = this.parentComponent().parentComponent().data()._id;
+      swimlaneId = this.parentComponent()
+        .parentComponent()
+        .data()._id;
 
     return list.cards(swimlaneId).count();
   },
 
   reachedWipLimit() {
     const list = Template.currentData();
-    return list.getWipLimit('enabled') && list.getWipLimit('value') <= list.cards().count();
+    return (
+      list.getWipLimit('enabled') &&
+      list.getWipLimit('value') <= list.cards().count()
+    );
   },
 
   showCardsCountForList(count) {
@@ -48,20 +59,24 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-open-list-menu': Popup.open('listAction'),
-      'click .js-add-card' (evt) {
-        const listDom = $(evt.target).parents(`#js-list-${this.currentData()._id}`)[0];
-        const listComponent = BlazeComponent.getComponentForElement(listDom);
-        listComponent.openForm({
-          position: 'top',
-        });
+    return [
+      {
+        'click .js-open-list-menu': Popup.open('listAction'),
+        'click .js-add-card'(event) {
+          const listDom = $(event.target).parents(
+            `#js-list-${this.currentData()._id}`,
+          )[0];
+          const listComponent = BlazeComponent.getComponentForElement(listDom);
+          listComponent.openForm({
+            position: 'top',
+          });
+        },
+        'click .js-unselect-list'() {
+          Session.set('currentList', null);
+        },
+        submit: this.editTitle,
       },
-      'click .js-unselect-list'() {
-        Session.set('currentList', null);
-      },
-      submit: this.editTitle,
-    }];
+    ];
   },
 }).register('listHeader');
 
@@ -76,22 +91,22 @@ Template.listActionPopup.helpers({
 });
 
 Template.listActionPopup.events({
-  'click .js-list-subscribe' () {},
+  'click .js-list-subscribe'() {},
   'click .js-set-color-list': Popup.open('setListColor'),
-  'click .js-select-cards' () {
-    const cardIds = this.allCards().map((card) => card._id);
+  'click .js-select-cards'() {
+    const cardIds = this.allCards().map(card => card._id);
     MultiSelection.add(cardIds);
     Popup.close();
   },
-  'click .js-toggle-watch-list' () {
+  'click .js-toggle-watch-list'() {
     const currentList = this;
     const level = currentList.findWatcher(Meteor.userId()) ? null : 'watching';
     Meteor.call('watch', 'list', currentList._id, level, (err, ret) => {
       if (!err && ret) Popup.close();
     });
   },
-  'click .js-close-list' (evt) {
-    evt.preventDefault();
+  'click .js-close-list'(event) {
+    event.preventDefault();
     this.archive();
     Popup.close();
   },
@@ -102,10 +117,17 @@ Template.listActionPopup.events({
 BlazeComponent.extendComponent({
   applyWipLimit() {
     const list = Template.currentData();
-    const limit = parseInt(Template.instance().$('.wip-limit-value').val(), 10);
-
-    if(limit < list.cards().count() && !list.getWipLimit('soft')){
-      Template.instance().$('.wip-limit-error').click();
+    const limit = parseInt(
+      Template.instance()
+        .$('.wip-limit-value')
+        .val(),
+      10,
+    );
+
+    if (limit < list.cards().count() && !list.getWipLimit('soft')) {
+      Template.instance()
+        .$('.wip-limit-error')
+        .click();
     } else {
       Meteor.call('applyWipLimit', list._id, limit);
       Popup.back();
@@ -115,7 +137,10 @@ BlazeComponent.extendComponent({
   enableSoftLimit() {
     const list = Template.currentData();
 
-    if(list.getWipLimit('soft') && list.getWipLimit('value') < list.cards().count()){
+    if (
+      list.getWipLimit('soft') &&
+      list.getWipLimit('value') < list.cards().count()
+    ) {
       list.setWipLimit(list.cards().count());
     }
     Meteor.call('enableSoftLimit', Template.currentData()._id);
@@ -124,7 +149,10 @@ BlazeComponent.extendComponent({
   enableWipLimit() {
     const list = Template.currentData();
     // Prevent user from using previously stored wipLimit.value if it is less than the current number of cards in the list
-    if(!list.getWipLimit('enabled') && list.getWipLimit('value') < list.cards().count()){
+    if (
+      !list.getWipLimit('enabled') &&
+      list.getWipLimit('value') < list.cards().count()
+    ) {
       list.setWipLimit(list.cards().count());
     }
     Meteor.call('enableWipLimit', list._id);
@@ -138,24 +166,26 @@ BlazeComponent.extendComponent({
     return Template.currentData().getWipLimit('enabled');
   },
 
-  wipLimitValue(){
+  wipLimitValue() {
     return Template.currentData().getWipLimit('value');
   },
 
   events() {
-    return [{
-      'click .js-enable-wip-limit': this.enableWipLimit,
-      'click .wip-limit-apply': this.applyWipLimit,
-      'click .wip-limit-error': Popup.open('wipLimitError'),
-      'click .materialCheckBox': this.enableSoftLimit,
-    }];
+    return [
+      {
+        'click .js-enable-wip-limit': this.enableWipLimit,
+        'click .wip-limit-apply': this.applyWipLimit,
+        'click .wip-limit-error': Popup.open('wipLimitError'),
+        'click .materialCheckBox': this.enableSoftLimit,
+      },
+    ];
   },
 }).register('setWipLimitPopup');
 
 Template.listMorePopup.events({
-  'click .js-delete': Popup.afterConfirm('listDelete', function () {
+  'click .js-delete': Popup.afterConfirm('listDelete', function() {
     Popup.close();
-    this.allCards().map((card) => Cards.remove(card._id));
+    this.allCards().map(card => Cards.remove(card._id));
     Lists.remove(this._id);
     Utils.goBoardId(this.boardId);
   }),
@@ -168,7 +198,7 @@ BlazeComponent.extendComponent({
   },
 
   colors() {
-    return listsColors.map((color) => ({ color, name: '' }));
+    return listsColors.map(color => ({ color, name: '' }));
   },
 
   isSelected(color) {
@@ -176,18 +206,20 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-palette-color'() {
-        this.currentColor.set(this.currentData().color);
-      },
-      'click .js-submit' () {
-        this.currentList.setColor(this.currentColor.get());
-        Popup.close();
-      },
-      'click .js-remove-color'() {
-        this.currentList.setColor(null);
-        Popup.close();
+    return [
+      {
+        'click .js-palette-color'() {
+          this.currentColor.set(this.currentData().color);
+        },
+        'click .js-submit'() {
+          this.currentList.setColor(this.currentColor.get());
+          Popup.close();
+        },
+        'click .js-remove-color'() {
+          this.currentList.setColor(null);
+          Popup.close();
+        },
       },
-    }];
+    ];
   },
 }).register('setListColorPopup');

+ 60 - 51
client/components/main/editor.js

@@ -9,10 +9,15 @@ Template.editor.onRendered(() => {
       match: /\B@([\w.]*)$/,
       search(term, callback) {
         const currentBoard = Boards.findOne(Session.get('currentBoard'));
-        callback(currentBoard.activeMembers().map((member) => {
-          const username = Users.findOne(member.userId).username;
-          return username.includes(term) ? username : null;
-        }).filter(Boolean));
+        callback(
+          currentBoard
+            .activeMembers()
+            .map(member => {
+              const username = Users.findOne(member.userId).username;
+              return username.includes(term) ? username : null;
+            })
+            .filter(Boolean),
+        );
       },
       template(value) {
         return value;
@@ -33,69 +38,73 @@ import sanitizeXss from 'xss';
 // compiled version to most users -- who don't need to edit.
 // In the meantime, all the transformation are done on the client using the
 // Blaze API.
-const at = HTML.CharRef({html: '&commat;', str: '@'});
-Blaze.Template.registerHelper('mentions', new Template('mentions', function() {
-  const view = this;
-  let content = Blaze.toHTML(view.templateContentBlock);
-  const currentBoard = Boards.findOne(Session.get('currentBoard'));
-  if (!currentBoard)
-    return HTML.Raw(sanitizeXss(content));
-  const knowedUsers = currentBoard.members.map((member) => {
-    const u = Users.findOne(member.userId);
-    if(u){
-      member.username = u.username;
-    }
-    return member;
-  });
-  const mentionRegex = /\B@([\w.]*)/gi;
+const at = HTML.CharRef({ html: '&commat;', str: '@' });
+Blaze.Template.registerHelper(
+  'mentions',
+  new Template('mentions', function() {
+    const view = this;
+    let content = Blaze.toHTML(view.templateContentBlock);
+    const currentBoard = Boards.findOne(Session.get('currentBoard'));
+    if (!currentBoard) return HTML.Raw(sanitizeXss(content));
+    const knowedUsers = currentBoard.members.map(member => {
+      const u = Users.findOne(member.userId);
+      if (u) {
+        member.username = u.username;
+      }
+      return member;
+    });
+    const mentionRegex = /\B@([\w.]*)/gi;
 
-  let currentMention;
-  while ((currentMention = mentionRegex.exec(content)) !== null) {
-    const [fullMention, username] = currentMention;
-    const knowedUser = _.findWhere(knowedUsers, { username });
-    if (!knowedUser) {
-      continue;
-    }
+    let currentMention;
+    while ((currentMention = mentionRegex.exec(content)) !== null) {
+      const [fullMention, username] = currentMention;
+      const knowedUser = _.findWhere(knowedUsers, { username });
+      if (!knowedUser) {
+        continue;
+      }
 
-    const linkValue = [' ', at, knowedUser.username];
-    let linkClass = 'atMention js-open-member';
-    if (knowedUser.userId === Meteor.userId()) {
-      linkClass += ' me';
-    }
-    const link = HTML.A({
-      'class': linkClass,
-      // XXX Hack. Since we stringify this render function result below with
-      // `Blaze.toHTML` we can't rely on blaze data contexts to pass the
-      // `userId` to the popup as usual, and we need to store it in the DOM
-      // using a data attribute.
-      'data-userId': knowedUser.userId,
-    }, linkValue);
+      const linkValue = [' ', at, knowedUser.username];
+      let linkClass = 'atMention js-open-member';
+      if (knowedUser.userId === Meteor.userId()) {
+        linkClass += ' me';
+      }
+      const link = HTML.A(
+        {
+          class: linkClass,
+          // XXX Hack. Since we stringify this render function result below with
+          // `Blaze.toHTML` we can't rely on blaze data contexts to pass the
+          // `userId` to the popup as usual, and we need to store it in the DOM
+          // using a data attribute.
+          'data-userId': knowedUser.userId,
+        },
+        linkValue,
+      );
 
-    content = content.replace(fullMention, Blaze.toHTML(link));
-  }
+      content = content.replace(fullMention, Blaze.toHTML(link));
+    }
 
-  return HTML.Raw(sanitizeXss(content));
-}));
+    return HTML.Raw(sanitizeXss(content));
+  }),
+);
 
 Template.viewer.events({
   // Viewer sometimes have click-able wrapper around them (for instance to edit
   // the corresponding text). Clicking a link shouldn't fire these actions, stop
   // we stop these event at the viewer component level.
-  'click a'(evt, tpl) {
-    evt.stopPropagation();
+  'click a'(event, templateInstance) {
+    event.stopPropagation();
 
     // XXX We hijack the build-in browser action because we currently don't have
     // `_blank` attributes in viewer links, and the transformer function is
     // handled by a third party package that we can't configure easily. Fix that
     // by using directly `_blank` attribute in the rendered HTML.
-    evt.preventDefault();
+    event.preventDefault();
 
-    const userId = evt.currentTarget.dataset.userid;
+    const userId = event.currentTarget.dataset.userid;
     if (userId) {
-      Popup.open('member').call({ userId }, evt, tpl);
-    }
-    else {
-      const href = evt.currentTarget.href;
+      Popup.open('member').call({ userId }, event, templateInstance);
+    } else {
+      const href = event.currentTarget.href;
       if (href) {
         window.open(href, '_blank');
       }

+ 2 - 2
client/components/main/header.js

@@ -20,13 +20,13 @@ Template.header.helpers({
   },
 
   hasAnnouncement() {
-    const announcements =  Announcements.findOne();
+    const announcements = Announcements.findOne();
     return announcements && announcements.enabled;
   },
 
   announcement() {
     $('.announcement').show();
-    const announcements =  Announcements.findOne();
+    const announcements = Announcements.findOne();
     return announcements && announcements.body;
   },
 });

+ 40 - 32
client/components/main/layouts.js

@@ -1,6 +1,6 @@
 BlazeLayout.setRoot('body');
 
-const i18nTagToT9n = (i18nTag) => {
+const i18nTagToT9n = i18nTag => {
   // t9n/i18n tags are same now, see: https://github.com/softwarerero/meteor-accounts-t9n/pull/129
   // but we keep this conversion function here, to be aware that that they are different system.
   return i18nTag;
@@ -21,20 +21,23 @@ const validator = {
 };
 
 Template.userFormsLayout.onCreated(function() {
-  const instance = this;
-  instance.currentSetting = new ReactiveVar();
-  instance.isLoading = new ReactiveVar(false);
+  const templateInstance = this;
+  templateInstance.currentSetting = new ReactiveVar();
+  templateInstance.isLoading = new ReactiveVar(false);
 
   Meteor.subscribe('setting', {
     onReady() {
-      instance.currentSetting.set(Settings.findOne());
+      templateInstance.currentSetting.set(Settings.findOne());
       return this.stop();
     },
   });
 });
 
 Template.userFormsLayout.onRendered(() => {
-  AccountsTemplates.state.form.keys = new Proxy(AccountsTemplates.state.form.keys, validator);
+  AccountsTemplates.state.form.keys = new Proxy(
+    AccountsTemplates.state.form.keys,
+    validator,
+  );
 
   const i18nTag = navigator.language;
   if (i18nTag) {
@@ -89,18 +92,17 @@ Template.userFormsLayout.helpers({
 });
 
 Template.userFormsLayout.events({
-  'change .js-userform-set-language'(evt) {
-    const i18nTag = $(evt.currentTarget).val();
+  'change .js-userform-set-language'(event) {
+    const i18nTag = $(event.currentTarget).val();
     T9n.setLanguage(i18nTagToT9n(i18nTag));
-    evt.preventDefault();
+    event.preventDefault();
   },
-  'click #at-btn'(event, instance) {
+  'click #at-btn'(event, templateInstance) {
     if (FlowRouter.getRouteName() === 'atSignIn') {
-      instance.isLoading.set(true);
-      authentication(event, instance)
-        .then(() => {
-          instance.isLoading.set(false);
-        });
+      templateInstance.isLoading.set(true);
+      authentication(event, templateInstance).then(() => {
+        templateInstance.isLoading.set(false);
+      });
     }
   },
 });
@@ -111,13 +113,16 @@ Template.defaultLayout.events({
   },
 });
 
-async function authentication(event, instance) {
+async function authentication(event, templateInstance) {
   const match = $('#at-field-username_and_email').val();
   const password = $('#at-field-password').val();
 
   if (!match || !password) return undefined;
 
-  const result = await getAuthenticationMethod(instance.currentSetting.get(), match);
+  const result = await getAuthenticationMethod(
+    templateInstance.currentSetting.get(),
+    match,
+  );
 
   if (result === 'password') return undefined;
 
@@ -126,26 +131,29 @@ async function authentication(event, instance) {
   event.stopImmediatePropagation();
 
   switch (result) {
-  case 'ldap':
-    return new Promise((resolve) => {
-      Meteor.loginWithLDAP(match, password, function() {
-        resolve(FlowRouter.go('/'));
+    case 'ldap':
+      return new Promise(resolve => {
+        Meteor.loginWithLDAP(match, password, function() {
+          resolve(FlowRouter.go('/'));
+        });
       });
-    });
 
-  case 'cas':
-    return new Promise((resolve) => {
-      Meteor.loginWithCas(match, password, function() {
-        resolve(FlowRouter.go('/'));
+    case 'cas':
+      return new Promise(resolve => {
+        Meteor.loginWithCas(match, password, function() {
+          resolve(FlowRouter.go('/'));
+        });
       });
-    });
 
-  default:
-    return undefined;
+    default:
+      return undefined;
   }
 }
 
-function getAuthenticationMethod({displayAuthenticationMethod, defaultAuthenticationMethod}, match) {
+function getAuthenticationMethod(
+  { displayAuthenticationMethod, defaultAuthenticationMethod },
+  match,
+) {
   if (displayAuthenticationMethod) {
     return $('.select-authentication').val();
   }
@@ -153,7 +161,7 @@ function getAuthenticationMethod({displayAuthenticationMethod, defaultAuthentica
 }
 
 function getUserAuthenticationMethod(defaultAuthenticationMethod, match) {
-  return new Promise((resolve) => {
+  return new Promise(resolve => {
     try {
       Meteor.subscribe('user-authenticationMethod', match, {
         onReady() {
@@ -166,7 +174,7 @@ function getUserAuthenticationMethod(defaultAuthenticationMethod, match) {
           resolve(authenticationMethod);
         },
       });
-    } catch(error) {
+    } catch (error) {
       resolve(defaultAuthenticationMethod);
     }
   });

+ 11 - 9
client/components/mixins/infiniteScrolling.js

@@ -18,15 +18,17 @@ Mixins.InfiniteScrolling = BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      scroll(evt) {
-        const domElement = evt.currentTarget;
-        let altitude = domElement.scrollTop + domElement.offsetHeight;
-        altitude += peakAnticipation;
-        if (altitude >= this.callFirstWith(null, 'getNextPeak')) {
-          this.mixinParent().callFirstWith(null, 'reachNextPeak');
-        }
+    return [
+      {
+        scroll(evt) {
+          const domElement = evt.currentTarget;
+          let altitude = domElement.scrollTop + domElement.offsetHeight;
+          altitude += peakAnticipation;
+          if (altitude >= this.callFirstWith(null, 'getNextPeak')) {
+            this.mixinParent().callFirstWith(null, 'reachNextPeak');
+          }
+        },
       },
-    }];
+    ];
   },
 });

+ 136 - 139
client/components/rules/actions/boardActions.js

@@ -1,68 +1,23 @@
 BlazeComponent.extendComponent({
-  onCreated() {
-
-  },
+  onCreated() {},
 
   events() {
-    return [{
-      'click .js-create-card-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const cardName = this.find('#card-name').value;
-        const listName = this.find('#list-name').value;
-        const swimlaneName = this.find('#swimlane-name2').value;
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const triggerId = Triggers.insert(trigger);
-        const actionId = Actions.insert({
-          actionType: 'createCard',
-          swimlaneName,
-          cardName,
-          listName,
-          boardId,
-          desc,
-        });
-        Rules.insert({
-          title: ruleName,
-          triggerId,
-          actionId,
-          boardId,
-        });
-
-      },
-      'click .js-add-swimlane-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const swimlaneName = this.find('#swimlane-name').value;
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const triggerId = Triggers.insert(trigger);
-        const actionId = Actions.insert({
-          actionType: 'addSwimlane',
-          swimlaneName,
-          boardId,
-          desc,
-        });
-        Rules.insert({
-          title: ruleName,
-          triggerId,
-          actionId,
-          boardId,
-        });
-
-      },
-      'click .js-add-spec-move-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const actionSelected = this.find('#move-spec-action').value;
-        const listTitle = this.find('#listName').value;
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-        if (actionSelected === 'top') {
+    return [
+      {
+        'click .js-create-card-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const cardName = this.find('#card-name').value;
+          const listName = this.find('#list-name').value;
+          const swimlaneName = this.find('#swimlane-name2').value;
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
           const triggerId = Triggers.insert(trigger);
           const actionId = Actions.insert({
-            actionType: 'moveCardToTop',
-            listTitle,
+            actionType: 'createCard',
+            swimlaneName,
+            cardName,
+            listName,
             boardId,
             desc,
           });
@@ -72,12 +27,17 @@ BlazeComponent.extendComponent({
             actionId,
             boardId,
           });
-        }
-        if (actionSelected === 'bottom') {
+        },
+        'click .js-add-swimlane-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const swimlaneName = this.find('#swimlane-name').value;
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
           const triggerId = Triggers.insert(trigger);
           const actionId = Actions.insert({
-            actionType: 'moveCardToBottom',
-            listTitle,
+            actionType: 'addSwimlane',
+            swimlaneName,
             boardId,
             desc,
           });
@@ -87,82 +47,119 @@ BlazeComponent.extendComponent({
             actionId,
             boardId,
           });
-        }
+        },
+        'click .js-add-spec-move-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const actionSelected = this.find('#move-spec-action').value;
+          const listTitle = this.find('#listName').value;
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
+          if (actionSelected === 'top') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'moveCardToTop',
+              listTitle,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+          if (actionSelected === 'bottom') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'moveCardToBottom',
+              listTitle,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+        },
+        'click .js-add-gen-move-action'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const boardId = Session.get('currentBoard');
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const actionSelected = this.find('#move-gen-action').value;
+          if (actionSelected === 'top') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'moveCardToTop',
+              listTitle: '*',
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+          if (actionSelected === 'bottom') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'moveCardToBottom',
+              listTitle: '*',
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+        },
+        'click .js-add-arch-action'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const boardId = Session.get('currentBoard');
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const actionSelected = this.find('#arch-action').value;
+          if (actionSelected === 'archive') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'archive',
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+          if (actionSelected === 'unarchive') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'unarchive',
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+        },
       },
-      'click .js-add-gen-move-action' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const boardId = Session.get('currentBoard');
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const actionSelected = this.find('#move-gen-action').value;
-        if (actionSelected === 'top') {
-          const triggerId = Triggers.insert(trigger);
-          const actionId = Actions.insert({
-            actionType: 'moveCardToTop',
-            'listTitle': '*',
-            boardId,
-            desc,
-          });
-          Rules.insert({
-            title: ruleName,
-            triggerId,
-            actionId,
-            boardId,
-          });
-        }
-        if (actionSelected === 'bottom') {
-          const triggerId = Triggers.insert(trigger);
-          const actionId = Actions.insert({
-            actionType: 'moveCardToBottom',
-            'listTitle': '*',
-            boardId,
-            desc,
-          });
-          Rules.insert({
-            title: ruleName,
-            triggerId,
-            actionId,
-            boardId,
-          });
-        }
-      },
-      'click .js-add-arch-action' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const boardId = Session.get('currentBoard');
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const actionSelected = this.find('#arch-action').value;
-        if (actionSelected === 'archive') {
-          const triggerId = Triggers.insert(trigger);
-          const actionId = Actions.insert({
-            actionType: 'archive',
-            boardId,
-            desc,
-          });
-          Rules.insert({
-            title: ruleName,
-            triggerId,
-            actionId,
-            boardId,
-          });
-        }
-        if (actionSelected === 'unarchive') {
-          const triggerId = Triggers.insert(trigger);
-          const actionId = Actions.insert({
-            actionType: 'unarchive',
-            boardId,
-            desc,
-          });
-          Rules.insert({
-            title: ruleName,
-            triggerId,
-            actionId,
-            boardId,
-          });
-        }
-      },
-    }];
+    ];
   },
-
 }).register('boardActions');
 /* eslint-no-undef */

+ 144 - 141
client/components/rules/actions/cardActions.js

@@ -14,7 +14,7 @@ BlazeComponent.extendComponent({
   },
 
   cardColorButtonText() {
-    return `color-${ this.cardColorButtonValue.get() }`;
+    return `color-${this.cardColorButtonValue.get()}`;
   },
 
   labels() {
@@ -28,106 +28,143 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-set-date-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const triggerId = Triggers.insert(trigger);
-        const actionSelected = this.find('#setdate-action').value;
-        const dateFieldSelected = this.find('#setdate-datefield').value;
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-
-        const actionId = Actions.insert({
-          actionType: actionSelected,
-          dateField: dateFieldSelected,
-          boardId,
-          desc,
-        });
-
-        Rules.insert({
-          title: ruleName,
-          triggerId,
-          actionId,
-          boardId,
-          desc,
-        });
-      },
-
-      'click .js-remove-datevalue-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const triggerId = Triggers.insert(trigger);
-        const dateFieldSelected = this.find('#setdate-removedatefieldvalue').value;
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-
-        const actionId = Actions.insert({
-          actionType: 'removeDate',
-          dateField: dateFieldSelected,
-          boardId,
-          desc,
-        });
-
-        Rules.insert({
-          title: ruleName,
-          triggerId,
-          actionId,
-          boardId,
-          desc,
-        });
-      },
-      'click .js-add-label-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const actionSelected = this.find('#label-action').value;
-        const labelId = this.find('#label-id').value;
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-        if (actionSelected === 'add') {
+    return [
+      {
+        'click .js-set-date-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
           const triggerId = Triggers.insert(trigger);
+          const actionSelected = this.find('#setdate-action').value;
+          const dateFieldSelected = this.find('#setdate-datefield').value;
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
+
           const actionId = Actions.insert({
-            actionType: 'addLabel',
-            labelId,
+            actionType: actionSelected,
+            dateField: dateFieldSelected,
             boardId,
             desc,
           });
+
           Rules.insert({
             title: ruleName,
             triggerId,
             actionId,
             boardId,
+            desc,
           });
-        }
-        if (actionSelected === 'remove') {
+        },
+
+        'click .js-remove-datevalue-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
           const triggerId = Triggers.insert(trigger);
+          const dateFieldSelected = this.find('#setdate-removedatefieldvalue')
+            .value;
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
+
           const actionId = Actions.insert({
-            actionType: 'removeLabel',
-            labelId,
+            actionType: 'removeDate',
+            dateField: dateFieldSelected,
             boardId,
             desc,
           });
+
           Rules.insert({
             title: ruleName,
             triggerId,
             actionId,
             boardId,
+            desc,
           });
-        }
-
-      },
-      'click .js-add-member-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const actionSelected = this.find('#member-action').value;
-        const username = this.find('#member-name').value;
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-        if (actionSelected === 'add') {
+        },
+        'click .js-add-label-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const actionSelected = this.find('#label-action').value;
+          const labelId = this.find('#label-id').value;
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
+          if (actionSelected === 'add') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'addLabel',
+              labelId,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+          if (actionSelected === 'remove') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'removeLabel',
+              labelId,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+        },
+        'click .js-add-member-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const actionSelected = this.find('#member-action').value;
+          const username = this.find('#member-name').value;
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
+          if (actionSelected === 'add') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'addMember',
+              username,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+              desc,
+            });
+          }
+          if (actionSelected === 'remove') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'removeMember',
+              username,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+        },
+        'click .js-add-removeall-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
           const triggerId = Triggers.insert(trigger);
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const boardId = Session.get('currentBoard');
           const actionId = Actions.insert({
-            actionType: 'addMember',
-            username,
+            actionType: 'removeMember',
+            username: '*',
             boardId,
             desc,
           });
@@ -136,14 +173,26 @@ BlazeComponent.extendComponent({
             triggerId,
             actionId,
             boardId,
-            desc,
           });
-        }
-        if (actionSelected === 'remove') {
+        },
+        'click .js-show-color-palette'(event) {
+          const funct = Popup.open('setCardActionsColor');
+          const colorButton = this.find('#color-action');
+          if (colorButton.value === '') {
+            colorButton.value = 'green';
+          }
+          funct.call(this, event);
+        },
+        'click .js-set-color-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const selectedColor = this.cardColorButtonValue.get();
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
           const triggerId = Triggers.insert(trigger);
           const actionId = Actions.insert({
-            actionType: 'removeMember',
-            username,
+            actionType: 'setColor',
+            selectedColor,
             boardId,
             desc,
           });
@@ -153,58 +202,10 @@ BlazeComponent.extendComponent({
             actionId,
             boardId,
           });
-        }
-      },
-      'click .js-add-removeall-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const triggerId = Triggers.insert(trigger);
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const boardId = Session.get('currentBoard');
-        const actionId = Actions.insert({
-          actionType: 'removeMember',
-          'username': '*',
-          boardId,
-          desc,
-        });
-        Rules.insert({
-          title: ruleName,
-          triggerId,
-          actionId,
-          boardId,
-        });
-      },
-      'click .js-show-color-palette'(event){
-        const funct = Popup.open('setCardActionsColor');
-        const colorButton = this.find('#color-action');
-        if (colorButton.value === '') {
-          colorButton.value = 'green';
-        }
-        funct.call(this, event);
+        },
       },
-      'click .js-set-color-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const selectedColor = this.cardColorButtonValue.get();
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const triggerId = Triggers.insert(trigger);
-        const actionId = Actions.insert({
-          actionType: 'setColor',
-          selectedColor,
-          boardId,
-          desc,
-        });
-        Rules.insert({
-          title: ruleName,
-          triggerId,
-          actionId,
-          boardId,
-        });
-      },
-    }];
+    ];
   },
-
 }).register('cardActions');
 
 BlazeComponent.extendComponent({
@@ -215,7 +216,7 @@ BlazeComponent.extendComponent({
   },
 
   colors() {
-    return cardColors.map((color) => ({ color, name: '' }));
+    return cardColors.map(color => ({ color, name: '' }));
   },
 
   isSelected(color) {
@@ -223,14 +224,16 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-palette-color'() {
-        this.currentColor.set(this.currentData().color);
-      },
-      'click .js-submit' () {
-        this.colorButtonValue.set(this.currentColor.get());
-        Popup.close();
+    return [
+      {
+        'click .js-palette-color'() {
+          this.currentColor.set(this.currentData().color);
+        },
+        'click .js-submit'() {
+          this.colorButtonValue.set(this.currentColor.get());
+          Popup.close();
+        },
       },
-    }];
+    ];
   },
 }).register('setCardActionsColorPopup');

+ 130 - 131
client/components/rules/actions/checklistActions.js

@@ -3,137 +3,20 @@ BlazeComponent.extendComponent({
     this.subscribe('allRules');
   },
   events() {
-    return [{
-      'click .js-add-checklist-items-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const checklistName = this.find('#checklist-name-3').value;
-        const checklistItems = this.find('#checklist-items').value;
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const triggerId = Triggers.insert(trigger);
-        const actionId = Actions.insert({
-          actionType: 'addChecklistWithItems',
-          checklistName,
-          checklistItems,
-          boardId,
-          desc,
-        });
-        Rules.insert({
-          title: ruleName,
-          triggerId,
-          actionId,
-          boardId,
-        });
-
-      },
-      'click .js-add-checklist-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const actionSelected = this.find('#check-action').value;
-        const checklistName = this.find('#checklist-name').value;
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-        if (actionSelected === 'add') {
-          const triggerId = Triggers.insert(trigger);
-          const actionId = Actions.insert({
-            actionType: 'addChecklist',
-            checklistName,
-            boardId,
-            desc,
-          });
-          Rules.insert({
-            title: ruleName,
-            triggerId,
-            actionId,
-            boardId,
-          });
-        }
-        if (actionSelected === 'remove') {
-          const triggerId = Triggers.insert(trigger);
-          const actionId = Actions.insert({
-            actionType: 'removeChecklist',
-            checklistName,
-            boardId,
-            desc,
-          });
-          Rules.insert({
-            title: ruleName,
-            triggerId,
-            actionId,
-            boardId,
-          });
-        }
-
-      },
-      'click .js-add-checkall-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const actionSelected = this.find('#checkall-action').value;
-        const checklistName = this.find('#checklist-name2').value;
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-        if (actionSelected === 'check') {
-          const triggerId = Triggers.insert(trigger);
-          const actionId = Actions.insert({
-            actionType: 'checkAll',
-            checklistName,
-            boardId,
-            desc,
-          });
-          Rules.insert({
-            title: ruleName,
-            triggerId,
-            actionId,
-            boardId,
-          });
-        }
-        if (actionSelected === 'uncheck') {
-          const triggerId = Triggers.insert(trigger);
-          const actionId = Actions.insert({
-            actionType: 'uncheckAll',
-            checklistName,
-            boardId,
-            desc,
-          });
-          Rules.insert({
-            title: ruleName,
-            triggerId,
-            actionId,
-            boardId,
-          });
-        }
-      },
-      'click .js-add-check-item-action' (event) {
-        const ruleName = this.data().ruleName.get();
-        const trigger = this.data().triggerVar.get();
-        const checkItemName = this.find('#checkitem-name');
-        const checklistName = this.find('#checklist-name3');
-        const actionSelected = this.find('#check-item-action').value;
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-        if (actionSelected === 'check') {
-          const triggerId = Triggers.insert(trigger);
-          const actionId = Actions.insert({
-            actionType: 'checkItem',
-            checklistName,
-            checkItemName,
-            boardId,
-            desc,
-          });
-          Rules.insert({
-            title: ruleName,
-            triggerId,
-            actionId,
-            boardId,
-          });
-        }
-        if (actionSelected === 'uncheck') {
+    return [
+      {
+        'click .js-add-checklist-items-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const checklistName = this.find('#checklist-name-3').value;
+          const checklistItems = this.find('#checklist-items').value;
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
           const triggerId = Triggers.insert(trigger);
           const actionId = Actions.insert({
-            actionType: 'uncheckItem',
+            actionType: 'addChecklistWithItems',
             checklistName,
-            checkItemName,
+            checklistItems,
             boardId,
             desc,
           });
@@ -143,9 +26,125 @@ BlazeComponent.extendComponent({
             actionId,
             boardId,
           });
-        }
+        },
+        'click .js-add-checklist-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const actionSelected = this.find('#check-action').value;
+          const checklistName = this.find('#checklist-name').value;
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
+          if (actionSelected === 'add') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'addChecklist',
+              checklistName,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+          if (actionSelected === 'remove') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'removeChecklist',
+              checklistName,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+        },
+        'click .js-add-checkall-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const actionSelected = this.find('#checkall-action').value;
+          const checklistName = this.find('#checklist-name2').value;
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
+          if (actionSelected === 'check') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'checkAll',
+              checklistName,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+          if (actionSelected === 'uncheck') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'uncheckAll',
+              checklistName,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+        },
+        'click .js-add-check-item-action'(event) {
+          const ruleName = this.data().ruleName.get();
+          const trigger = this.data().triggerVar.get();
+          const checkItemName = this.find('#checkitem-name');
+          const checklistName = this.find('#checklist-name3');
+          const actionSelected = this.find('#check-item-action').value;
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
+          if (actionSelected === 'check') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'checkItem',
+              checklistName,
+              checkItemName,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+          if (actionSelected === 'uncheck') {
+            const triggerId = Triggers.insert(trigger);
+            const actionId = Actions.insert({
+              actionType: 'uncheckItem',
+              checklistName,
+              checkItemName,
+              boardId,
+              desc,
+            });
+            Rules.insert({
+              title: ruleName,
+              triggerId,
+              actionId,
+              boardId,
+            });
+          }
+        },
       },
-    }];
+    ];
   },
-
 }).register('checklistActions');

+ 28 - 29
client/components/rules/actions/mailActions.js

@@ -1,35 +1,34 @@
 BlazeComponent.extendComponent({
-  onCreated() {
-
-  },
+  onCreated() {},
 
   events() {
-    return [{
-      'click .js-mail-action' (event) {
-        const emailTo = this.find('#email-to').value;
-        const emailSubject = this.find('#email-subject').value;
-        const emailMsg = this.find('#email-msg').value;
-        const trigger = this.data().triggerVar.get();
-        const ruleName = this.data().ruleName.get();
-        const triggerId = Triggers.insert(trigger);
-        const boardId = Session.get('currentBoard');
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const actionId = Actions.insert({
-          actionType: 'sendEmail',
-          emailTo,
-          emailSubject,
-          emailMsg,
-          boardId,
-          desc,
-        });
-        Rules.insert({
-          title: ruleName,
-          triggerId,
-          actionId,
-          boardId,
-        });
+    return [
+      {
+        'click .js-mail-action'(event) {
+          const emailTo = this.find('#email-to').value;
+          const emailSubject = this.find('#email-subject').value;
+          const emailMsg = this.find('#email-msg').value;
+          const trigger = this.data().triggerVar.get();
+          const ruleName = this.data().ruleName.get();
+          const triggerId = Triggers.insert(trigger);
+          const boardId = Session.get('currentBoard');
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const actionId = Actions.insert({
+            actionType: 'sendEmail',
+            emailTo,
+            emailSubject,
+            emailMsg,
+            boardId,
+            desc,
+          });
+          Rules.insert({
+            title: ruleName,
+            triggerId,
+            actionId,
+            boardId,
+          });
+        },
       },
-    }];
+    ];
   },
-
 }).register('mailActions');

+ 0 - 2
client/components/rules/ruleDetails.js

@@ -3,7 +3,6 @@ BlazeComponent.extendComponent({
     this.subscribe('allRules');
     this.subscribe('allTriggers');
     this.subscribe('allActions');
-
   },
 
   trigger() {
@@ -34,5 +33,4 @@ BlazeComponent.extendComponent({
   events() {
     return [{}];
   },
-
 }).register('ruleDetails');

+ 15 - 13
client/components/rules/rulesActions.js

@@ -40,19 +40,21 @@ BlazeComponent.extendComponent({
     // console.log(this.data());
   },
   events() {
-    return [{
-      'click .js-set-board-actions'(){
-        this.setBoardActions();
+    return [
+      {
+        'click .js-set-board-actions'() {
+          this.setBoardActions();
+        },
+        'click .js-set-card-actions'() {
+          this.setCardActions();
+        },
+        'click .js-set-mail-actions'() {
+          this.setMailActions();
+        },
+        'click .js-set-checklist-actions'() {
+          this.setChecklistActions();
+        },
       },
-      'click .js-set-card-actions'() {
-        this.setCardActions();
-      },
-      'click .js-set-mail-actions'() {
-        this.setMailActions();
-      },
-      'click .js-set-checklist-actions'() {
-        this.setChecklistActions();
-      },
-    }];
+    ];
   },
 }).register('rulesActions');

+ 70 - 66
client/components/rules/rulesMain.js

@@ -9,12 +9,12 @@ BlazeComponent.extendComponent({
   setTrigger() {
     this.rulesCurrentTab.set('trigger');
   },
-  sanitizeObject(obj){
-    Object.keys(obj).forEach((key) => {
-      if(obj[key] === '' || obj[key] === undefined){
+  sanitizeObject(obj) {
+    Object.keys(obj).forEach(key => {
+      if (obj[key] === '' || obj[key] === undefined) {
         obj[key] = '*';
-      }}
-    );
+      }
+    });
   },
   setRulesList() {
     this.rulesCurrentTab.set('rulesList');
@@ -29,69 +29,73 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-delete-rule' () {
-        const rule = this.currentData();
-        Rules.remove(rule._id);
-        Actions.remove(rule.actionId);
-        Triggers.remove(rule.triggerId);
-
-      },
-      'click .js-goto-trigger' (event) {
-        event.preventDefault();
-        const ruleTitle = this.find('#ruleTitle').value;
-        if(ruleTitle !== undefined && ruleTitle !== ''){
-          this.find('#ruleTitle').value = '';
-          this.ruleName.set(ruleTitle);
-          this.setTrigger();
-        }
-      },
-      'click .js-goto-action' (event) {
-        event.preventDefault();
-        // Add user to the trigger
-        const username = $(event.currentTarget.offsetParent).find('.user-name').val();
-        let trigger = this.triggerVar.get();
-        trigger.userId = '*';
-        if(username !== undefined ){
-          const userFound = Users.findOne({username});
-          if(userFound !== undefined){
-            trigger.userId = userFound._id;
-            this.triggerVar.set(trigger);
+    return [
+      {
+        'click .js-delete-rule'() {
+          const rule = this.currentData();
+          Rules.remove(rule._id);
+          Actions.remove(rule.actionId);
+          Triggers.remove(rule.triggerId);
+        },
+        'click .js-goto-trigger'(event) {
+          event.preventDefault();
+          const ruleTitle = this.find('#ruleTitle').value;
+          if (ruleTitle !== undefined && ruleTitle !== '') {
+            this.find('#ruleTitle').value = '';
+            this.ruleName.set(ruleTitle);
+            this.setTrigger();
           }
-        }
-        // Sanitize trigger
-        trigger = this.triggerVar.get();
-        this.sanitizeObject(trigger);
-        this.triggerVar.set(trigger);
-        this.setAction();
-      },
-      'click .js-show-user-field' (event) {
-        event.preventDefault();
-        $(event.currentTarget.offsetParent).find('.user-details').removeClass('hide-element');
-      },
-      'click .js-goto-rules' (event) {
-        event.preventDefault();
-        this.setRulesList();
-      },
-      'click .js-goback' (event) {
-        event.preventDefault();
-        if(this.rulesCurrentTab.get() === 'trigger' || this.rulesCurrentTab.get() === 'ruleDetails' ){
+        },
+        'click .js-goto-action'(event) {
+          event.preventDefault();
+          // Add user to the trigger
+          const username = $(event.currentTarget.offsetParent)
+            .find('.user-name')
+            .val();
+          let trigger = this.triggerVar.get();
+          trigger.userId = '*';
+          if (username !== undefined) {
+            const userFound = Users.findOne({ username });
+            if (userFound !== undefined) {
+              trigger.userId = userFound._id;
+              this.triggerVar.set(trigger);
+            }
+          }
+          // Sanitize trigger
+          trigger = this.triggerVar.get();
+          this.sanitizeObject(trigger);
+          this.triggerVar.set(trigger);
+          this.setAction();
+        },
+        'click .js-show-user-field'(event) {
+          event.preventDefault();
+          $(event.currentTarget.offsetParent)
+            .find('.user-details')
+            .removeClass('hide-element');
+        },
+        'click .js-goto-rules'(event) {
+          event.preventDefault();
           this.setRulesList();
-        }
-        if(this.rulesCurrentTab.get() === 'action'){
-          this.setTrigger();
-        }
-      },
-      'click .js-goto-details' (event) {
-        event.preventDefault();
-        const rule = this.currentData();
-        this.ruleId.set(rule._id);
-        this.setRuleDetails();
+        },
+        'click .js-goback'(event) {
+          event.preventDefault();
+          if (
+            this.rulesCurrentTab.get() === 'trigger' ||
+            this.rulesCurrentTab.get() === 'ruleDetails'
+          ) {
+            this.setRulesList();
+          }
+          if (this.rulesCurrentTab.get() === 'action') {
+            this.setTrigger();
+          }
+        },
+        'click .js-goto-details'(event) {
+          event.preventDefault();
+          const rule = this.currentData();
+          this.ruleId.set(rule._id);
+          this.setRuleDetails();
+        },
       },
-
-    }];
+    ];
   },
-
 }).register('rulesMain');
-
-

+ 12 - 10
client/components/rules/rulesTriggers.js

@@ -38,16 +38,18 @@ BlazeComponent.extendComponent({
     // console.log(this.data());
   },
   events() {
-    return [{
-      'click .js-set-board-triggers' () {
-        this.setBoardTriggers();
+    return [
+      {
+        'click .js-set-board-triggers'() {
+          this.setBoardTriggers();
+        },
+        'click .js-set-card-triggers'() {
+          this.setCardTriggers();
+        },
+        'click .js-set-checklist-triggers'() {
+          this.setChecklistTriggers();
+        },
       },
-      'click .js-set-card-triggers' () {
-        this.setCardTriggers();
-      },
-      'click .js-set-checklist-triggers' () {
-        this.setChecklistTriggers();
-      },
-    }];
+    ];
   },
 }).register('rulesTriggers');

+ 92 - 90
client/components/rules/triggers/boardTriggers.js

@@ -4,116 +4,118 @@ BlazeComponent.extendComponent({
     this.currentPopupTriggerId = 'def';
     this.cardTitleFilters = {};
   },
-  setNameFilter(name){
-    this.cardTitleFilters[this.currentPopupTriggerId] =  name;
+  setNameFilter(name) {
+    this.cardTitleFilters[this.currentPopupTriggerId] = name;
   },
 
   events() {
-    return [{
-      'click .js-open-card-title-popup'(event){
-        const funct = Popup.open('boardCardTitle');
-        const divId = $(event.currentTarget.parentNode.parentNode).attr('id');
-        //console.log('current popup');
-        //console.log(this.currentPopupTriggerId);
-        this.currentPopupTriggerId = divId;
-        funct.call(this, event);
-      },
-      'click .js-add-create-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const datas = this.data();
-        const listName = this.find('#create-list-name').value;
-        const swimlaneName = this.find('#create-swimlane-name').value;
-        const boardId = Session.get('currentBoard');
-        const divId = $(event.currentTarget.parentNode).attr('id');
-        const cardTitle = this.cardTitleFilters[divId];
-        // move to generic funciont
-        datas.triggerVar.set({
-          activityType: 'createCard',
-          boardId,
-          cardTitle,
-          swimlaneName,
-          listName,
-          desc,
-        });
-      },
-      'click .js-add-moved-trigger' (event) {
-        const datas = this.data();
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const swimlaneName = this.find('#create-swimlane-name-2').value;
-        const actionSelected = this.find('#move-action').value;
-        const listName = this.find('#move-list-name').value;
-        const boardId = Session.get('currentBoard');
-        const divId = $(event.currentTarget.parentNode).attr('id');
-        const cardTitle = this.cardTitleFilters[divId];
-        if (actionSelected === 'moved-to') {
+    return [
+      {
+        'click .js-open-card-title-popup'(event) {
+          const funct = Popup.open('boardCardTitle');
+          const divId = $(event.currentTarget.parentNode.parentNode).attr('id');
+          //console.log('current popup');
+          //console.log(this.currentPopupTriggerId);
+          this.currentPopupTriggerId = divId;
+          funct.call(this, event);
+        },
+        'click .js-add-create-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const datas = this.data();
+          const listName = this.find('#create-list-name').value;
+          const swimlaneName = this.find('#create-swimlane-name').value;
+          const boardId = Session.get('currentBoard');
+          const divId = $(event.currentTarget.parentNode).attr('id');
+          const cardTitle = this.cardTitleFilters[divId];
+          // move to generic funciont
           datas.triggerVar.set({
-            activityType: 'moveCard',
-            boardId,
-            listName,
-            cardTitle,
-            swimlaneName,
-            'oldListName': '*',
-            desc,
-          });
-        }
-        if (actionSelected === 'moved-from') {
-          datas.triggerVar.set({
-            activityType: 'moveCard',
+            activityType: 'createCard',
             boardId,
             cardTitle,
             swimlaneName,
-            'listName': '*',
-            'oldListName': listName,
+            listName,
             desc,
           });
-        }
-      },
-      'click .js-add-gen-moved-trigger' (event){
-        const datas = this.data();
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const boardId = Session.get('currentBoard');
+        },
+        'click .js-add-moved-trigger'(event) {
+          const datas = this.data();
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const swimlaneName = this.find('#create-swimlane-name-2').value;
+          const actionSelected = this.find('#move-action').value;
+          const listName = this.find('#move-list-name').value;
+          const boardId = Session.get('currentBoard');
+          const divId = $(event.currentTarget.parentNode).attr('id');
+          const cardTitle = this.cardTitleFilters[divId];
+          if (actionSelected === 'moved-to') {
+            datas.triggerVar.set({
+              activityType: 'moveCard',
+              boardId,
+              listName,
+              cardTitle,
+              swimlaneName,
+              oldListName: '*',
+              desc,
+            });
+          }
+          if (actionSelected === 'moved-from') {
+            datas.triggerVar.set({
+              activityType: 'moveCard',
+              boardId,
+              cardTitle,
+              swimlaneName,
+              listName: '*',
+              oldListName: listName,
+              desc,
+            });
+          }
+        },
+        'click .js-add-gen-moved-trigger'(event) {
+          const datas = this.data();
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const boardId = Session.get('currentBoard');
 
-        datas.triggerVar.set({
-          'activityType': 'moveCard',
-          boardId,
-          'swimlaneName': '*',
-          'listName':'*',
-          'oldListName': '*',
-          desc,
-        });
-      },
-      'click .js-add-arc-trigger' (event) {
-        const datas = this.data();
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const actionSelected = this.find('#arch-action').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'archived') {
           datas.triggerVar.set({
-            activityType: 'archivedCard',
-            boardId,
-            desc,
-          });
-        }
-        if (actionSelected === 'unarchived') {
-          datas.triggerVar.set({
-            activityType: 'restoredCard',
+            activityType: 'moveCard',
             boardId,
+            swimlaneName: '*',
+            listName: '*',
+            oldListName: '*',
             desc,
           });
-        }
+        },
+        'click .js-add-arc-trigger'(event) {
+          const datas = this.data();
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const actionSelected = this.find('#arch-action').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'archived') {
+            datas.triggerVar.set({
+              activityType: 'archivedCard',
+              boardId,
+              desc,
+            });
+          }
+          if (actionSelected === 'unarchived') {
+            datas.triggerVar.set({
+              activityType: 'restoredCard',
+              boardId,
+              desc,
+            });
+          }
+        },
       },
-
-    }];
+    ];
   },
-
 }).register('boardTriggers');
 
-
 Template.boardCardTitlePopup.events({
-  submit(evt, tpl) {
-    const title = tpl.$('.js-card-filter-name').val().trim();
+  submit(event, templateInstance) {
+    const title = templateInstance
+      .$('.js-card-filter-name')
+      .val()
+      .trim();
     Popup.getOpenerComponent().setNameFilter(title);
-    evt.preventDefault();
+    event.preventDefault();
     Popup.close();
   },
 });

+ 114 - 112
client/components/rules/triggers/cardTriggers.js

@@ -7,7 +7,7 @@ BlazeComponent.extendComponent({
     for (let i = 0; i < labels.length; i++) {
       if (labels[i].name === '' || labels[i].name === undefined) {
         labels[i].name = labels[i].color;
-        labels[i].translatedname = `${TAPi18n.__(`color-${  labels[i].color}`)}`;
+        labels[i].translatedname = `${TAPi18n.__(`color-${labels[i].color}`)}`;
       } else {
         labels[i].translatedname = labels[i].name;
       }
@@ -15,117 +15,119 @@ BlazeComponent.extendComponent({
     return labels;
   },
   events() {
-    return [{
-      'click .js-add-gen-label-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const datas = this.data();
-        const actionSelected = this.find('#label-action').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'added') {
-          datas.triggerVar.set({
-            activityType: 'addedLabel',
-            boardId,
-            'labelId': '*',
-            desc,
-          });
-        }
-        if (actionSelected === 'removed') {
-          datas.triggerVar.set({
-            activityType: 'removedLabel',
-            boardId,
-            'labelId': '*',
-            desc,
-          });
-        }
+    return [
+      {
+        'click .js-add-gen-label-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const datas = this.data();
+          const actionSelected = this.find('#label-action').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'added') {
+            datas.triggerVar.set({
+              activityType: 'addedLabel',
+              boardId,
+              labelId: '*',
+              desc,
+            });
+          }
+          if (actionSelected === 'removed') {
+            datas.triggerVar.set({
+              activityType: 'removedLabel',
+              boardId,
+              labelId: '*',
+              desc,
+            });
+          }
+        },
+        'click .js-add-spec-label-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const datas = this.data();
+          const actionSelected = this.find('#spec-label-action').value;
+          const labelId = this.find('#spec-label').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'added') {
+            datas.triggerVar.set({
+              activityType: 'addedLabel',
+              boardId,
+              labelId,
+              desc,
+            });
+          }
+          if (actionSelected === 'removed') {
+            datas.triggerVar.set({
+              activityType: 'removedLabel',
+              boardId,
+              labelId,
+              desc,
+            });
+          }
+        },
+        'click .js-add-gen-member-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const datas = this.data();
+          const actionSelected = this.find('#gen-member-action').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'added') {
+            datas.triggerVar.set({
+              activityType: 'joinMember',
+              boardId,
+              username: '*',
+              desc,
+            });
+          }
+          if (actionSelected === 'removed') {
+            datas.triggerVar.set({
+              activityType: 'unjoinMember',
+              boardId,
+              username: '*',
+              desc,
+            });
+          }
+        },
+        'click .js-add-spec-member-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const datas = this.data();
+          const actionSelected = this.find('#spec-member-action').value;
+          const username = this.find('#spec-member').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'added') {
+            datas.triggerVar.set({
+              activityType: 'joinMember',
+              boardId,
+              username,
+              desc,
+            });
+          }
+          if (actionSelected === 'removed') {
+            datas.triggerVar.set({
+              activityType: 'unjoinMember',
+              boardId,
+              username,
+              desc,
+            });
+          }
+        },
+        'click .js-add-attachment-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const datas = this.data();
+          const actionSelected = this.find('#attach-action').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'added') {
+            datas.triggerVar.set({
+              activityType: 'addAttachment',
+              boardId,
+              desc,
+            });
+          }
+          if (actionSelected === 'removed') {
+            datas.triggerVar.set({
+              activityType: 'deleteAttachment',
+              boardId,
+              desc,
+            });
+          }
+        },
       },
-      'click .js-add-spec-label-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const datas = this.data();
-        const actionSelected = this.find('#spec-label-action').value;
-        const labelId = this.find('#spec-label').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'added') {
-          datas.triggerVar.set({
-            activityType: 'addedLabel',
-            boardId,
-            labelId,
-            desc,
-          });
-        }
-        if (actionSelected === 'removed') {
-          datas.triggerVar.set({
-            activityType: 'removedLabel',
-            boardId,
-            labelId,
-            desc,
-          });
-        }
-      },
-      'click .js-add-gen-member-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const datas = this.data();
-        const actionSelected = this.find('#gen-member-action').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'added') {
-          datas.triggerVar.set({
-            activityType: 'joinMember',
-            boardId,
-            'username': '*',
-            desc,
-          });
-        }
-        if (actionSelected === 'removed') {
-          datas.triggerVar.set({
-            activityType: 'unjoinMember',
-            boardId,
-            'username': '*',
-            desc,
-          });
-        }
-      },
-      'click .js-add-spec-member-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const datas = this.data();
-        const actionSelected = this.find('#spec-member-action').value;
-        const username = this.find('#spec-member').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'added') {
-          datas.triggerVar.set({
-            activityType: 'joinMember',
-            boardId,
-            username,
-            desc,
-          });
-        }
-        if (actionSelected === 'removed') {
-          datas.triggerVar.set({
-            activityType: 'unjoinMember',
-            boardId,
-            username,
-            desc,
-          });
-        }
-      },
-      'click .js-add-attachment-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const datas = this.data();
-        const actionSelected = this.find('#attach-action').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'added') {
-          datas.triggerVar.set({
-            activityType: 'addAttachment',
-            boardId,
-            desc,
-          });
-        }
-        if (actionSelected === 'removed') {
-          datas.triggerVar.set({
-            activityType: 'deleteAttachment',
-            boardId,
-            desc,
-          });
-        }
-      },
-    }];
+    ];
   },
 }).register('cardTriggers');

+ 138 - 137
client/components/rules/triggers/checklistTriggers.js

@@ -3,144 +3,145 @@ BlazeComponent.extendComponent({
     this.subscribe('allRules');
   },
   events() {
-    return [{
-      'click .js-add-gen-check-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const datas = this.data();
-        const actionSelected = this.find('#gen-check-action').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'created') {
-          datas.triggerVar.set({
-            activityType: 'addChecklist',
-            boardId,
-            'checklistName': '*',
-            desc,
-          });
-        }
-        if (actionSelected === 'removed') {
-          datas.triggerVar.set({
-            activityType: 'removeChecklist',
-            boardId,
-            'checklistName': '*',
-            desc,
-          });
-        }
-      },
-      'click .js-add-spec-check-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const datas = this.data();
-        const actionSelected = this.find('#spec-check-action').value;
-        const checklistId = this.find('#check-name').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'created') {
-          datas.triggerVar.set({
-            activityType: 'addChecklist',
-            boardId,
-            'checklistName': checklistId,
-            desc,
-          });
-        }
-        if (actionSelected === 'removed') {
-          datas.triggerVar.set({
-            activityType: 'removeChecklist',
-            boardId,
-            'checklistName': checklistId,
-            desc,
-          });
-        }
-      },
-      'click .js-add-gen-comp-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
+    return [
+      {
+        'click .js-add-gen-check-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const datas = this.data();
+          const actionSelected = this.find('#gen-check-action').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'created') {
+            datas.triggerVar.set({
+              activityType: 'addChecklist',
+              boardId,
+              checklistName: '*',
+              desc,
+            });
+          }
+          if (actionSelected === 'removed') {
+            datas.triggerVar.set({
+              activityType: 'removeChecklist',
+              boardId,
+              checklistName: '*',
+              desc,
+            });
+          }
+        },
+        'click .js-add-spec-check-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const datas = this.data();
+          const actionSelected = this.find('#spec-check-action').value;
+          const checklistId = this.find('#check-name').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'created') {
+            datas.triggerVar.set({
+              activityType: 'addChecklist',
+              boardId,
+              checklistName: checklistId,
+              desc,
+            });
+          }
+          if (actionSelected === 'removed') {
+            datas.triggerVar.set({
+              activityType: 'removeChecklist',
+              boardId,
+              checklistName: checklistId,
+              desc,
+            });
+          }
+        },
+        'click .js-add-gen-comp-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
 
-        const datas = this.data();
-        const actionSelected = this.find('#gen-comp-check-action').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'completed') {
-          datas.triggerVar.set({
-            activityType: 'completeChecklist',
-            boardId,
-            'checklistName': '*',
-            desc,
-          });
-        }
-        if (actionSelected === 'uncompleted') {
-          datas.triggerVar.set({
-            activityType: 'uncompleteChecklist',
-            boardId,
-            'checklistName': '*',
-            desc,
-          });
-        }
-      },
-      'click .js-add-spec-comp-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const datas = this.data();
-        const actionSelected = this.find('#spec-comp-check-action').value;
-        const checklistId = this.find('#spec-comp-check-name').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'completed') {
-          datas.triggerVar.set({
-            activityType: 'completeChecklist',
-            boardId,
-            'checklistName': checklistId,
-            desc,
-          });
-        }
-        if (actionSelected === 'uncompleted') {
-          datas.triggerVar.set({
-            activityType: 'uncompleteChecklist',
-            boardId,
-            'checklistName': checklistId,
-            desc,
-          });
-        }
+          const datas = this.data();
+          const actionSelected = this.find('#gen-comp-check-action').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'completed') {
+            datas.triggerVar.set({
+              activityType: 'completeChecklist',
+              boardId,
+              checklistName: '*',
+              desc,
+            });
+          }
+          if (actionSelected === 'uncompleted') {
+            datas.triggerVar.set({
+              activityType: 'uncompleteChecklist',
+              boardId,
+              checklistName: '*',
+              desc,
+            });
+          }
+        },
+        'click .js-add-spec-comp-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const datas = this.data();
+          const actionSelected = this.find('#spec-comp-check-action').value;
+          const checklistId = this.find('#spec-comp-check-name').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'completed') {
+            datas.triggerVar.set({
+              activityType: 'completeChecklist',
+              boardId,
+              checklistName: checklistId,
+              desc,
+            });
+          }
+          if (actionSelected === 'uncompleted') {
+            datas.triggerVar.set({
+              activityType: 'uncompleteChecklist',
+              boardId,
+              checklistName: checklistId,
+              desc,
+            });
+          }
+        },
+        'click .js-add-gen-check-item-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const datas = this.data();
+          const actionSelected = this.find('#check-item-gen-action').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'checked') {
+            datas.triggerVar.set({
+              activityType: 'checkedItem',
+              boardId,
+              checklistItemName: '*',
+              desc,
+            });
+          }
+          if (actionSelected === 'unchecked') {
+            datas.triggerVar.set({
+              activityType: 'uncheckedItem',
+              boardId,
+              checklistItemName: '*',
+              desc,
+            });
+          }
+        },
+        'click .js-add-spec-check-item-trigger'(event) {
+          const desc = Utils.getTriggerActionDesc(event, this);
+          const datas = this.data();
+          const actionSelected = this.find('#check-item-spec-action').value;
+          const checklistItemId = this.find('#check-item-name').value;
+          const boardId = Session.get('currentBoard');
+          if (actionSelected === 'checked') {
+            datas.triggerVar.set({
+              activityType: 'checkedItem',
+              boardId,
+              checklistItemName: checklistItemId,
+              desc,
+            });
+          }
+          if (actionSelected === 'unchecked') {
+            datas.triggerVar.set({
+              activityType: 'uncheckedItem',
+              boardId,
+              checklistItemName: checklistItemId,
+              desc,
+            });
+          }
+        },
       },
-      'click .js-add-gen-check-item-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const datas = this.data();
-        const actionSelected = this.find('#check-item-gen-action').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'checked') {
-          datas.triggerVar.set({
-            activityType: 'checkedItem',
-            boardId,
-            'checklistItemName': '*',
-            desc,
-          });
-        }
-        if (actionSelected === 'unchecked') {
-          datas.triggerVar.set({
-            activityType: 'uncheckedItem',
-            boardId,
-            'checklistItemName': '*',
-            desc,
-          });
-        }
-      },
-      'click .js-add-spec-check-item-trigger' (event) {
-        const desc = Utils.getTriggerActionDesc(event, this);
-        const datas = this.data();
-        const actionSelected = this.find('#check-item-spec-action').value;
-        const checklistItemId = this.find('#check-item-name').value;
-        const boardId = Session.get('currentBoard');
-        if (actionSelected === 'checked') {
-          datas.triggerVar.set({
-            activityType: 'checkedItem',
-            boardId,
-            'checklistItemName': checklistItemId,
-            desc,
-          });
-        }
-        if (actionSelected === 'unchecked') {
-          datas.triggerVar.set({
-            activityType: 'uncheckedItem',
-            boardId,
-            'checklistItemName': checklistItemId,
-            desc,
-          });
-        }
-      },
-    }];
+    ];
   },
-
 }).register('checklistTriggers');

+ 7 - 3
client/components/settings/connectionMethod.js

@@ -6,9 +6,11 @@ Template.connectionMethod.onCreated(function() {
       // TODO : add a management of different languages
       // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
       this.authenticationMethods.set([
-        {value: 'password'},
+        { value: 'password' },
         // Gets only the authentication methods availables
-        ...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})),
+        ...Object.entries(result)
+          .filter(e => e[1])
+          .map(e => ({ value: e[0] })),
       ]);
     }
 
@@ -24,7 +26,9 @@ Template.connectionMethod.onCreated(function() {
 
 Template.connectionMethod.onRendered(() => {
   // Moves the select boxe in the first place of the at-pwd-form div
-  $('.at-form-authentication').detach().prependTo('.at-pwd-form');
+  $('.at-form-authentication')
+    .detach()
+    .prependTo('.at-pwd-form');
 });
 
 Template.connectionMethod.helpers({

+ 69 - 51
client/components/settings/peopleBody.js

@@ -29,19 +29,23 @@ BlazeComponent.extendComponent({
     });
   },
   events() {
-    return [{
-      'click #searchButton'() {
-        this.filterPeople();
-      },
-      'keydown #searchInput'(event) {
-        if (event.keyCode === 13 && !event.shiftKey) {
+    return [
+      {
+        'click #searchButton'() {
           this.filterPeople();
-        }
+        },
+        'keydown #searchInput'(event) {
+          if (event.keyCode === 13 && !event.shiftKey) {
+            this.filterPeople();
+          }
+        },
       },
-    }];
+    ];
   },
   filterPeople() {
-    const value = $('#searchInput').first().val();
+    const value = $('#searchInput')
+      .first()
+      .val();
     if (value === '') {
       this.findUsersOptions.set({});
     } else {
@@ -79,7 +83,7 @@ BlazeComponent.extendComponent({
   },
   peopleList() {
     const users = Users.find(this.findUsersOptions.get(), {
-      fields: {_id: true},
+      fields: { _id: true },
     });
     this.number.set(users.count());
     return users;
@@ -105,9 +109,11 @@ Template.editUserPopup.onCreated(function() {
       // TODO : add a management of different languages
       // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
       this.authenticationMethods.set([
-        {value: 'password'},
+        { value: 'password' },
         // Gets only the authentication methods availables
-        ...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})),
+        ...Object.entries(result)
+          .filter(e => e[1])
+          .map(e => ({ value: e[0] })),
       ]);
     }
   });
@@ -136,69 +142,79 @@ Template.editUserPopup.helpers({
 });
 
 BlazeComponent.extendComponent({
-  onCreated() {
-  },
+  onCreated() {},
   user() {
     return Users.findOne(this.userId);
   },
   events() {
-    return [{
-      'click a.edit-user': Popup.open('editUser'),
-    }];
+    return [
+      {
+        'click a.edit-user': Popup.open('editUser'),
+      },
+    ];
   },
 }).register('peopleRow');
 
 Template.editUserPopup.events({
-  submit(evt, tpl) {
-    evt.preventDefault();
+  submit(event, templateInstance) {
+    event.preventDefault();
     const user = Users.findOne(this.userId);
-    const fullname = tpl.find('.js-profile-fullname').value.trim();
-    const username = tpl.find('.js-profile-username').value.trim();
-    const password = tpl.find('.js-profile-password').value;
-    const isAdmin = tpl.find('.js-profile-isadmin').value.trim();
-    const isActive = tpl.find('.js-profile-isactive').value.trim();
-    const email = tpl.find('.js-profile-email').value.trim();
-    const authentication = tpl.find('.js-authenticationMethod').value.trim();
+    const fullname = templateInstance.find('.js-profile-fullname').value.trim();
+    const username = templateInstance.find('.js-profile-username').value.trim();
+    const password = templateInstance.find('.js-profile-password').value;
+    const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
+    const isActive = templateInstance.find('.js-profile-isactive').value.trim();
+    const email = templateInstance.find('.js-profile-email').value.trim();
+    const authentication = templateInstance
+      .find('.js-authenticationMethod')
+      .value.trim();
 
     const isChangePassword = password.length > 0;
     const isChangeUserName = username !== user.username;
-    const isChangeEmail = email.toLowerCase() !== user.emails[0].address.toLowerCase();
+    const isChangeEmail =
+      email.toLowerCase() !== user.emails[0].address.toLowerCase();
 
     Users.update(this.userId, {
       $set: {
         'profile.fullname': fullname,
-        'isAdmin': isAdmin === 'true',
-        'loginDisabled': isActive === 'true',
-        'authenticationMethod': authentication,
+        isAdmin: isAdmin === 'true',
+        loginDisabled: isActive === 'true',
+        authenticationMethod: authentication,
       },
     });
 
-    if(isChangePassword){
+    if (isChangePassword) {
       Meteor.call('setPassword', password, this.userId);
     }
 
     if (isChangeUserName && isChangeEmail) {
-      Meteor.call('setUsernameAndEmail', username, email.toLowerCase(), this.userId, function (error) {
-        const usernameMessageElement = tpl.$('.username-taken');
-        const emailMessageElement = tpl.$('.email-taken');
-        if (error) {
-          const errorElement = error.error;
-          if (errorElement === 'username-already-taken') {
-            usernameMessageElement.show();
-            emailMessageElement.hide();
-          } else if (errorElement === 'email-already-taken') {
+      Meteor.call(
+        'setUsernameAndEmail',
+        username,
+        email.toLowerCase(),
+        this.userId,
+        function(error) {
+          const usernameMessageElement = templateInstance.$('.username-taken');
+          const emailMessageElement = templateInstance.$('.email-taken');
+          if (error) {
+            const errorElement = error.error;
+            if (errorElement === 'username-already-taken') {
+              usernameMessageElement.show();
+              emailMessageElement.hide();
+            } else if (errorElement === 'email-already-taken') {
+              usernameMessageElement.hide();
+              emailMessageElement.show();
+            }
+          } else {
             usernameMessageElement.hide();
-            emailMessageElement.show();
+            emailMessageElement.hide();
+            Popup.close();
           }
-        } else {
-          usernameMessageElement.hide();
-          emailMessageElement.hide();
-          Popup.close();
-        }
-      });
+        },
+      );
     } else if (isChangeUserName) {
-      Meteor.call('setUsername', username, this.userId, function (error) {
-        const usernameMessageElement = tpl.$('.username-taken');
+      Meteor.call('setUsername', username, this.userId, function(error) {
+        const usernameMessageElement = templateInstance.$('.username-taken');
         if (error) {
           const errorElement = error.error;
           if (errorElement === 'username-already-taken') {
@@ -210,8 +226,10 @@ Template.editUserPopup.events({
         }
       });
     } else if (isChangeEmail) {
-      Meteor.call('setEmail', email.toLowerCase(), this.userId, function (error) {
-        const emailMessageElement = tpl.$('.email-taken');
+      Meteor.call('setEmail', email.toLowerCase(), this.userId, function(
+        error,
+      ) {
+        const emailMessageElement = templateInstance.$('.email-taken');
         if (error) {
           const errorElement = error.error;
           if (errorElement === 'email-already-taken') {

+ 90 - 53
client/components/settings/settingBody.js

@@ -25,7 +25,9 @@ BlazeComponent.extendComponent({
   checkField(selector) {
     const value = $(selector).val();
     if (!value || value.trim() === '') {
-      $(selector).parents('li.smtp-form').addClass('has-error');
+      $(selector)
+        .parents('li.smtp-form')
+        .addClass('has-error');
       throw Error('blank field');
     } else {
       return value;
@@ -37,18 +39,23 @@ BlazeComponent.extendComponent({
   },
 
   boards() {
-    return Boards.find({
-      archived: false,
-      'members.userId': Meteor.userId(),
-      'members.isAdmin': true,
-    }, {
-      sort: ['title'],
-    });
+    return Boards.find(
+      {
+        archived: false,
+        'members.userId': Meteor.userId(),
+        'members.isAdmin': true,
+      },
+      {
+        sort: ['title'],
+      },
+    );
   },
   toggleRegistration() {
     this.setLoading(true);
     const registrationClosed = this.currentSetting().disableRegistration;
-    Settings.update(Settings.findOne()._id, {$set: {disableRegistration: !registrationClosed}});
+    Settings.update(Settings.findOne()._id, {
+      $set: { disableRegistration: !registrationClosed },
+    });
     this.setLoading(false);
     if (registrationClosed) {
       $('.invite-people').slideUp();
@@ -90,13 +97,19 @@ BlazeComponent.extendComponent({
   },
 
   inviteThroughEmail() {
-    const emails = $('#email-to-invite').val().toLowerCase().trim().split('\n').join(',').split(',');
+    const emails = $('#email-to-invite')
+      .val()
+      .toLowerCase()
+      .trim()
+      .split('\n')
+      .join(',')
+      .split(',');
     const boardsToInvite = [];
-    $('.js-toggle-board-choose .materialCheckBox.is-checked').each(function () {
+    $('.js-toggle-board-choose .materialCheckBox.is-checked').each(function() {
       boardsToInvite.push($(this).data('id'));
     });
     const validEmails = [];
-    emails.forEach((email) => {
+    emails.forEach(email => {
       if (email && SimpleSchema.RegEx.Email.test(email.trim())) {
         validEmails.push(email.trim());
       }
@@ -119,14 +132,22 @@ BlazeComponent.extendComponent({
     try {
       const host = this.checkField('#mail-server-host');
       const port = this.checkField('#mail-server-port');
-      const username = $('#mail-server-username').val().trim();
-      const password = $('#mail-server-password').val().trim();
+      const username = $('#mail-server-username')
+        .val()
+        .trim();
+      const password = $('#mail-server-password')
+        .val()
+        .trim();
       const from = this.checkField('#mail-server-from');
       const tls = $('#mail-server-tls.is-checked').length > 0;
       Settings.update(Settings.findOne()._id, {
         $set: {
-          'mailServer.host': host, 'mailServer.port': port, 'mailServer.username': username,
-          'mailServer.password': password, 'mailServer.enableTLS': tls, 'mailServer.from': from,
+          'mailServer.host': host,
+          'mailServer.port': port,
+          'mailServer.username': username,
+          'mailServer.password': password,
+          'mailServer.enableTLS': tls,
+          'mailServer.from': from,
         },
       });
     } catch (e) {
@@ -134,19 +155,25 @@ BlazeComponent.extendComponent({
     } finally {
       this.setLoading(false);
     }
-
   },
 
   saveLayout() {
     this.setLoading(true);
     $('li').removeClass('has-error');
 
-    const productName = $('#product-name').val().trim();
-    const hideLogoChange = ($('input[name=hideLogo]:checked').val() === 'true');
-    const displayAuthenticationMethod = ($('input[name=displayAuthenticationMethod]:checked').val() === 'true');
+    const productName = $('#product-name')
+      .val()
+      .trim();
+    const hideLogoChange = $('input[name=hideLogo]:checked').val() === 'true';
+    const displayAuthenticationMethod =
+      $('input[name=displayAuthenticationMethod]:checked').val() === 'true';
     const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
-    const customHTMLafterBodyStart = $('#customHTMLafterBodyStart').val().trim();
-    const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd').val().trim();
+    const customHTMLafterBodyStart = $('#customHTMLafterBodyStart')
+      .val()
+      .trim();
+    const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd')
+      .val()
+      .trim();
 
     try {
       Settings.update(Settings.findOne()._id, {
@@ -166,7 +193,6 @@ BlazeComponent.extendComponent({
     }
 
     DocHead.setTitle(productName);
-
   },
 
   sendSMTPTestEmail() {
@@ -183,31 +209,35 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click a.js-toggle-registration': this.toggleRegistration,
-      'click a.js-toggle-tls': this.toggleTLS,
-      'click a.js-setting-menu': this.switchMenu,
-      'click a.js-toggle-board-choose': this.checkBoard,
-      'click button.js-email-invite': this.inviteThroughEmail,
-      'click button.js-save': this.saveMailServerInfo,
-      'click button.js-send-smtp-test-email': this.sendSMTPTestEmail,
-      'click a.js-toggle-hide-logo': this.toggleHideLogo,
-      'click button.js-save-layout': this.saveLayout,
-      'click a.js-toggle-display-authentication-method': this.toggleDisplayAuthenticationMethod,
-    }];
+    return [
+      {
+        'click a.js-toggle-registration': this.toggleRegistration,
+        'click a.js-toggle-tls': this.toggleTLS,
+        'click a.js-setting-menu': this.switchMenu,
+        'click a.js-toggle-board-choose': this.checkBoard,
+        'click button.js-email-invite': this.inviteThroughEmail,
+        'click button.js-save': this.saveMailServerInfo,
+        'click button.js-send-smtp-test-email': this.sendSMTPTestEmail,
+        'click a.js-toggle-hide-logo': this.toggleHideLogo,
+        'click button.js-save-layout': this.saveLayout,
+        'click a.js-toggle-display-authentication-method': this
+          .toggleDisplayAuthenticationMethod,
+      },
+    ];
   },
 }).register('setting');
 
 BlazeComponent.extendComponent({
-
   saveAccountsChange() {
-    const allowEmailChange = ($('input[name=allowEmailChange]:checked').val() === 'true');
-    const allowUserNameChange = ($('input[name=allowUserNameChange]:checked').val() === 'true');
+    const allowEmailChange =
+      $('input[name=allowEmailChange]:checked').val() === 'true';
+    const allowUserNameChange =
+      $('input[name=allowUserNameChange]:checked').val() === 'true';
     AccountSettings.update('accounts-allowEmailChange', {
-      $set: {'booleanValue': allowEmailChange},
+      $set: { booleanValue: allowEmailChange },
     });
     AccountSettings.update('accounts-allowUserNameChange', {
-      $set: {'booleanValue': allowUserNameChange},
+      $set: { booleanValue: allowUserNameChange },
     });
   },
 
@@ -219,9 +249,11 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click button.js-accounts-save': this.saveAccountsChange,
-    }];
+    return [
+      {
+        'click button.js-accounts-save': this.saveAccountsChange,
+      },
+    ];
   },
 }).register('accountSettings');
 
@@ -239,9 +271,11 @@ BlazeComponent.extendComponent({
   },
 
   saveMessage() {
-    const message = $('#admin-announcement').val().trim();
+    const message = $('#admin-announcement')
+      .val()
+      .trim();
     Announcements.update(Announcements.findOne()._id, {
-      $set: {'body': message},
+      $set: { body: message },
     });
   },
 
@@ -249,7 +283,7 @@ BlazeComponent.extendComponent({
     this.setLoading(true);
     const isActive = this.currentSetting().enabled;
     Announcements.update(Announcements.findOne()._id, {
-      $set: {'enabled': !isActive},
+      $set: { enabled: !isActive },
     });
     this.setLoading(false);
     if (isActive) {
@@ -260,14 +294,15 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click a.js-toggle-activemessage': this.toggleActive,
-      'click button.js-announcement-save': this.saveMessage,
-    }];
+    return [
+      {
+        'click a.js-toggle-activemessage': this.toggleActive,
+        'click button.js-announcement-save': this.saveMessage,
+      },
+    ];
   },
 }).register('announcementSettings');
 
-
 Template.selectAuthenticationMethod.onCreated(function() {
   this.authenticationMethods = new ReactiveVar([]);
 
@@ -276,9 +311,11 @@ Template.selectAuthenticationMethod.onCreated(function() {
       // TODO : add a management of different languages
       // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
       this.authenticationMethods.set([
-        {value: 'password'},
+        { value: 'password' },
         // Gets only the authentication methods availables
-        ...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})),
+        ...Object.entries(result)
+          .filter(e => e[1])
+          .map(e => ({ value: e[0] })),
       ]);
     }
   });

+ 192 - 131
client/components/sidebar/sidebar.js

@@ -91,29 +91,34 @@ BlazeComponent.extendComponent({
   },
 
   showTongueTitle() {
-    if (this.isOpen())
-      return `${TAPi18n.__('sidebar-close')}`;
-    else
-      return `${TAPi18n.__('sidebar-open')}`;
+    if (this.isOpen()) return `${TAPi18n.__('sidebar-close')}`;
+    else return `${TAPi18n.__('sidebar-open')}`;
   },
 
   events() {
-    return [{
-      'click .js-hide-sidebar': this.hide,
-      'click .js-toggle-sidebar': this.toggle,
-      'click .js-back-home': this.setView,
-      'click .js-shortcuts'() {
-        FlowRouter.go('shortcuts');
+    return [
+      {
+        'click .js-hide-sidebar': this.hide,
+        'click .js-toggle-sidebar': this.toggle,
+        'click .js-back-home': this.setView,
+        'click .js-shortcuts'() {
+          FlowRouter.go('shortcuts');
+        },
       },
-    }];
+    ];
   },
 }).register('sidebar');
 
 Blaze.registerHelper('Sidebar', () => Sidebar);
 
-EscapeActions.register('sidebarView',
-  () => { Sidebar.setView(defaultView); },
-  () => { return Sidebar && Sidebar.getView() !== defaultView; }
+EscapeActions.register(
+  'sidebarView',
+  () => {
+    Sidebar.setView(defaultView);
+  },
+  () => {
+    return Sidebar && Sidebar.getView() !== defaultView;
+  },
 );
 
 Template.memberPopup.helpers({
@@ -122,13 +127,13 @@ Template.memberPopup.helpers({
   },
   memberType() {
     const type = Users.findOne(this.userId).isBoardAdmin() ? 'admin' : 'normal';
-    if(type === 'normal'){
+    if (type === 'normal') {
       const currentBoard = Boards.findOne(Session.get('currentBoard'));
       const commentOnly = currentBoard.hasCommentOnly(this.userId);
       const noComments = currentBoard.hasNoComments(this.userId);
-      if(commentOnly){
+      if (commentOnly) {
         return TAPi18n.__('comment-only').toLowerCase();
-      } else if(noComments) {
+      } else if (noComments) {
         return TAPi18n.__('no-comments').toLowerCase();
       } else {
         return TAPi18n.__(type).toLowerCase();
@@ -197,7 +202,7 @@ Template.memberPopup.events({
   'click .js-remove-member': Popup.afterConfirm('removeMember', function() {
     const boardId = Session.get('currentBoard');
     const memberId = this.userId;
-    Cards.find({ boardId, members: memberId }).forEach((card) => {
+    Cards.find({ boardId, members: memberId }).forEach(card => {
       card.unassignMember(memberId);
     });
     Boards.findOne(boardId).removeMember(memberId);
@@ -274,38 +279,40 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'submit'(evt) {
-        evt.preventDefault();
-        const url = evt.target.url.value;
-        const boardId = Session.get('currentBoard');
-        let id = null;
-        let integration = null;
-        if (evt.target.id) {
-          id = evt.target.id.value;
-          integration = this.integration(id);
-          if (url) {
-            Integrations.update(integration._id, {
-              $set: {
-                url: `${url}`,
-              },
+    return [
+      {
+        submit(evt) {
+          evt.preventDefault();
+          const url = evt.target.url.value;
+          const boardId = Session.get('currentBoard');
+          let id = null;
+          let integration = null;
+          if (evt.target.id) {
+            id = evt.target.id.value;
+            integration = this.integration(id);
+            if (url) {
+              Integrations.update(integration._id, {
+                $set: {
+                  url: `${url}`,
+                },
+              });
+            } else {
+              Integrations.remove(integration._id);
+            }
+          } else if (url) {
+            Integrations.insert({
+              userId: Meteor.userId(),
+              enabled: true,
+              type: 'outgoing-webhooks',
+              url: `${url}`,
+              boardId: `${boardId}`,
+              activities: ['all'],
             });
-          } else {
-            Integrations.remove(integration._id);
           }
-        } else if (url) {
-          Integrations.insert({
-            userId: Meteor.userId(),
-            enabled: true,
-            type: 'outgoing-webhooks',
-            url: `${url}`,
-            boardId: `${boardId}`,
-            activities: ['all'],
-          });
-        }
-        Popup.close();
+          Popup.close();
+        },
       },
-    }];
+    ];
   },
 }).register('outgoingWebhooksPopup');
 
@@ -375,14 +382,16 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-select-background'(evt) {
-        const currentBoard = Boards.findOne(Session.get('currentBoard'));
-        const newColor = this.currentData().toString();
-        currentBoard.setColor(newColor);
-        evt.preventDefault();
+    return [
+      {
+        'click .js-select-background'(evt) {
+          const currentBoard = Boards.findOne(Session.get('currentBoard'));
+          const newColor = this.currentData().toString();
+          currentBoard.setColor(newColor);
+          evt.preventDefault();
+        },
       },
-    }];
+    ];
   },
 }).register('boardChangeColorPopup');
 
@@ -400,25 +409,34 @@ BlazeComponent.extendComponent({
   },
 
   isNullBoardSelected() {
-    return (this.currentBoard.subtasksDefaultBoardId === null) || (this.currentBoard.subtasksDefaultBoardId === undefined);
+    return (
+      this.currentBoard.subtasksDefaultBoardId === null ||
+      this.currentBoard.subtasksDefaultBoardId === undefined
+    );
   },
 
   boards() {
-    return Boards.find({
-      archived: false,
-      'members.userId': Meteor.userId(),
-    }, {
-      sort: ['title'],
-    });
+    return Boards.find(
+      {
+        archived: false,
+        'members.userId': Meteor.userId(),
+      },
+      {
+        sort: ['title'],
+      },
+    );
   },
 
   lists() {
-    return Lists.find({
-      boardId: this.currentBoard._id,
-      archived: false,
-    }, {
-      sort: ['title'],
-    });
+    return Lists.find(
+      {
+        boardId: this.currentBoard._id,
+        archived: false,
+      },
+      {
+        sort: ['title'],
+      },
+    );
   },
 
   hasLists() {
@@ -431,54 +449,74 @@ BlazeComponent.extendComponent({
 
   presentParentTask() {
     let result = this.currentBoard.presentParentTask;
-    if ((result === null) || (result === undefined)) {
+    if (result === null || result === undefined) {
       result = 'no-parent';
     }
     return result;
   },
 
   events() {
-    return [{
-      'click .js-field-has-subtasks'(evt) {
-        evt.preventDefault();
-        this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
-        this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
-        $('.js-field-has-subtasks .materialCheckBox').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
-        $('.js-field-has-subtasks').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
-        $('.js-field-deposit-board').prop('disabled', !this.currentBoard.allowsSubtasks);
-      },
-      'change .js-field-deposit-board'(evt) {
-        let value = evt.target.value;
-        if (value === 'null') {
-          value = null;
-        }
-        this.currentBoard.setSubtasksDefaultBoardId(value);
-        evt.preventDefault();
-      },
-      'change .js-field-deposit-list'(evt) {
-        this.currentBoard.setSubtasksDefaultListId(evt.target.value);
-        evt.preventDefault();
-      },
-      'click .js-field-show-parent-in-minicard'(evt) {
-        const value = evt.target.id || $(evt.target).parent()[0].id ||  $(evt.target).parent()[0].parent()[0].id;
-        const options = [
-          'prefix-with-full-path',
-          'prefix-with-parent',
-          'subtext-with-full-path',
-          'subtext-with-parent',
-          'no-parent'];
-        options.forEach(function(element) {
-          if (element !== value) {
-            $(`#${element} .materialCheckBox`).toggleClass('is-checked', false);
-            $(`#${element}`).toggleClass('is-checked', false);
+    return [
+      {
+        'click .js-field-has-subtasks'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
+          this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
+          $('.js-field-has-subtasks .materialCheckBox').toggleClass(
+            'is-checked',
+            this.currentBoard.allowsSubtasks,
+          );
+          $('.js-field-has-subtasks').toggleClass(
+            'is-checked',
+            this.currentBoard.allowsSubtasks,
+          );
+          $('.js-field-deposit-board').prop(
+            'disabled',
+            !this.currentBoard.allowsSubtasks,
+          );
+        },
+        'change .js-field-deposit-board'(evt) {
+          let value = evt.target.value;
+          if (value === 'null') {
+            value = null;
           }
-        });
-        $(`#${value} .materialCheckBox`).toggleClass('is-checked', true);
-        $(`#${value}`).toggleClass('is-checked', true);
-        this.currentBoard.setPresentParentTask(value);
-        evt.preventDefault();
+          this.currentBoard.setSubtasksDefaultBoardId(value);
+          evt.preventDefault();
+        },
+        'change .js-field-deposit-list'(evt) {
+          this.currentBoard.setSubtasksDefaultListId(evt.target.value);
+          evt.preventDefault();
+        },
+        'click .js-field-show-parent-in-minicard'(evt) {
+          const value =
+            evt.target.id ||
+            $(evt.target).parent()[0].id ||
+            $(evt.target)
+              .parent()[0]
+              .parent()[0].id;
+          const options = [
+            'prefix-with-full-path',
+            'prefix-with-parent',
+            'subtext-with-full-path',
+            'subtext-with-parent',
+            'no-parent',
+          ];
+          options.forEach(function(element) {
+            if (element !== value) {
+              $(`#${element} .materialCheckBox`).toggleClass(
+                'is-checked',
+                false,
+              );
+              $(`#${element}`).toggleClass('is-checked', false);
+            }
+          });
+          $(`#${value} .materialCheckBox`).toggleClass('is-checked', true);
+          $(`#${value}`).toggleClass('is-checked', true);
+          this.currentBoard.setPresentParentTask(value);
+          evt.preventDefault();
+        },
       },
-    }];
+    ];
   },
 }).register('boardSubtaskSettingsPopup');
 
@@ -528,35 +566,46 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'keyup input'() {
-        this.setError('');
-      },
-      'click .js-select-member'() {
-        const userId = this.currentData()._id;
-        const currentBoard = Boards.findOne(Session.get('currentBoard'));
-        if (!currentBoard.hasMember(userId)) {
-          this.inviteUser(userId);
-        }
-      },
-      'click .js-email-invite'() {
-        const idNameEmail = $('.js-search-member input').val();
-        if (idNameEmail.indexOf('@')<0 || this.isValidEmail(idNameEmail)) {
-          this.inviteUser(idNameEmail);
-        } else this.setError('email-invalid');
+    return [
+      {
+        'keyup input'() {
+          this.setError('');
+        },
+        'click .js-select-member'() {
+          const userId = this.currentData()._id;
+          const currentBoard = Boards.findOne(Session.get('currentBoard'));
+          if (!currentBoard.hasMember(userId)) {
+            this.inviteUser(userId);
+          }
+        },
+        'click .js-email-invite'() {
+          const idNameEmail = $('.js-search-member input').val();
+          if (idNameEmail.indexOf('@') < 0 || this.isValidEmail(idNameEmail)) {
+            this.inviteUser(idNameEmail);
+          } else this.setError('email-invalid');
+        },
       },
-    }];
+    ];
   },
 }).register('addMemberPopup');
 
 Template.changePermissionsPopup.events({
-  'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only'(event) {
+  'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only'(
+    event,
+  ) {
     const currentBoard = Boards.findOne(Session.get('currentBoard'));
     const memberId = this.userId;
     const isAdmin = $(event.currentTarget).hasClass('js-set-admin');
-    const isCommentOnly = $(event.currentTarget).hasClass('js-set-comment-only');
+    const isCommentOnly = $(event.currentTarget).hasClass(
+      'js-set-comment-only',
+    );
     const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments');
-    currentBoard.setMemberPermission(memberId, isAdmin, isNoComments, isCommentOnly);
+    currentBoard.setMemberPermission(
+      memberId,
+      isAdmin,
+      isNoComments,
+      isCommentOnly,
+    );
     Popup.back(1);
   },
 });
@@ -569,21 +618,33 @@ Template.changePermissionsPopup.helpers({
 
   isNormal() {
     const currentBoard = Boards.findOne(Session.get('currentBoard'));
-    return !currentBoard.hasAdmin(this.userId) && !currentBoard.hasNoComments(this.userId) && !currentBoard.hasCommentOnly(this.userId);
+    return (
+      !currentBoard.hasAdmin(this.userId) &&
+      !currentBoard.hasNoComments(this.userId) &&
+      !currentBoard.hasCommentOnly(this.userId)
+    );
   },
 
   isNoComments() {
     const currentBoard = Boards.findOne(Session.get('currentBoard'));
-    return !currentBoard.hasAdmin(this.userId) && currentBoard.hasNoComments(this.userId);
+    return (
+      !currentBoard.hasAdmin(this.userId) &&
+      currentBoard.hasNoComments(this.userId)
+    );
   },
 
   isCommentOnly() {
     const currentBoard = Boards.findOne(Session.get('currentBoard'));
-    return !currentBoard.hasAdmin(this.userId) && currentBoard.hasCommentOnly(this.userId);
+    return (
+      !currentBoard.hasAdmin(this.userId) &&
+      currentBoard.hasCommentOnly(this.userId)
+    );
   },
 
   isLastAdmin() {
     const currentBoard = Boards.findOne(Session.get('currentBoard'));
-    return currentBoard.hasAdmin(this.userId) && (currentBoard.activeAdmins() === 1);
+    return (
+      currentBoard.hasAdmin(this.userId) && currentBoard.activeAdmins() === 1
+    );
   },
 });

+ 70 - 63
client/components/sidebar/sidebarArchives.js

@@ -10,12 +10,11 @@ BlazeComponent.extendComponent({
     // unfortunatly, Blaze doesn't have this notion.
     this.autorun(() => {
       const currentBoardId = Session.get('currentBoard');
-      if (!currentBoardId)
-        return;
+      if (!currentBoardId) return;
       const handle = subManager.subscribe('board', currentBoardId, true);
       Tracker.nonreactive(() => {
         Tracker.autorun(() => {
-          this.isArchiveReady.set( handle.ready() );
+          this.isArchiveReady.set(handle.ready());
         });
       });
     });
@@ -59,74 +58,82 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-restore-card'() {
-        const card = this.currentData();
-        if(card.canBeRestored()){
-          card.restore();
-        }
-      },
-      'click .js-restore-all-cards'() {
-        this.archivedCards().forEach((card) => {
-          if(card.canBeRestored()){
+    return [
+      {
+        'click .js-restore-card'() {
+          const card = this.currentData();
+          if (card.canBeRestored()) {
             card.restore();
           }
-        });
-      },
+        },
+        'click .js-restore-all-cards'() {
+          this.archivedCards().forEach(card => {
+            if (card.canBeRestored()) {
+              card.restore();
+            }
+          });
+        },
 
-      'click .js-delete-card': Popup.afterConfirm('cardDelete', function() {
-        const cardId = this._id;
-        Cards.remove(cardId);
-        Popup.close();
-      }),
-      'click .js-delete-all-cards': Popup.afterConfirm('cardDelete', () => {
-        this.archivedCards().forEach((card) => {
-          Cards.remove(card._id);
-        });
-        Popup.close();
-      }),
+        'click .js-delete-card': Popup.afterConfirm('cardDelete', function() {
+          const cardId = this._id;
+          Cards.remove(cardId);
+          Popup.close();
+        }),
+        'click .js-delete-all-cards': Popup.afterConfirm('cardDelete', () => {
+          this.archivedCards().forEach(card => {
+            Cards.remove(card._id);
+          });
+          Popup.close();
+        }),
 
-      'click .js-restore-list'() {
-        const list = this.currentData();
-        list.restore();
-      },
-      'click .js-restore-all-lists'() {
-        this.archivedLists().forEach((list) => {
+        'click .js-restore-list'() {
+          const list = this.currentData();
           list.restore();
-        });
-      },
+        },
+        'click .js-restore-all-lists'() {
+          this.archivedLists().forEach(list => {
+            list.restore();
+          });
+        },
 
-      'click .js-delete-list': Popup.afterConfirm('listDelete', function() {
-        this.remove();
-        Popup.close();
-      }),
-      'click .js-delete-all-lists': Popup.afterConfirm('listDelete', () => {
-        this.archivedLists().forEach((list) => {
-          list.remove();
-        });
-        Popup.close();
-      }),
+        'click .js-delete-list': Popup.afterConfirm('listDelete', function() {
+          this.remove();
+          Popup.close();
+        }),
+        'click .js-delete-all-lists': Popup.afterConfirm('listDelete', () => {
+          this.archivedLists().forEach(list => {
+            list.remove();
+          });
+          Popup.close();
+        }),
 
-      'click .js-restore-swimlane'() {
-        const swimlane = this.currentData();
-        swimlane.restore();
-      },
-      'click .js-restore-all-swimlanes'() {
-        this.archivedSwimlanes().forEach((swimlane) => {
+        'click .js-restore-swimlane'() {
+          const swimlane = this.currentData();
           swimlane.restore();
-        });
-      },
+        },
+        'click .js-restore-all-swimlanes'() {
+          this.archivedSwimlanes().forEach(swimlane => {
+            swimlane.restore();
+          });
+        },
 
-      'click .js-delete-swimlane': Popup.afterConfirm('swimlaneDelete', function() {
-        this.remove();
-        Popup.close();
-      }),
-      'click .js-delete-all-swimlanes': Popup.afterConfirm('swimlaneDelete', () => {
-        this.archivedSwimlanes().forEach((swimlane) => {
-          swimlane.remove();
-        });
-        Popup.close();
-      }),
-    }];
+        'click .js-delete-swimlane': Popup.afterConfirm(
+          'swimlaneDelete',
+          function() {
+            this.remove();
+            Popup.close();
+          },
+        ),
+        'click .js-delete-all-swimlanes': Popup.afterConfirm(
+          'swimlaneDelete',
+          () => {
+            this.archivedSwimlanes().forEach(swimlane => {
+              swimlane.remove();
+            });
+            Popup.close();
+          },
+        ),
+      },
+    ];
   },
 }).register('archivesSidebar');

+ 115 - 100
client/components/sidebar/sidebarCustomFields.js

@@ -1,37 +1,43 @@
 BlazeComponent.extendComponent({
-
   customFields() {
     return CustomFields.find({
-      boardIds: {$in: [Session.get('currentBoard')]},
+      boardIds: { $in: [Session.get('currentBoard')] },
     });
   },
 
   events() {
-    return [{
-      'click .js-open-create-custom-field': Popup.open('createCustomField'),
-      'click .js-edit-custom-field': Popup.open('editCustomField'),
-    }];
+    return [
+      {
+        'click .js-open-create-custom-field': Popup.open('createCustomField'),
+        'click .js-edit-custom-field': Popup.open('editCustomField'),
+      },
+    ];
   },
-
 }).register('customFieldsSidebar');
 
 const CreateCustomFieldPopup = BlazeComponent.extendComponent({
-
   _types: ['text', 'number', 'date', 'dropdown'],
 
   onCreated() {
-    this.type = new ReactiveVar((this.data().type) ? this.data().type : this._types[0]);
-    this.dropdownItems = new ReactiveVar((this.data().settings && this.data().settings.dropdownItems) ? this.data().settings.dropdownItems : []);
+    this.type = new ReactiveVar(
+      this.data().type ? this.data().type : this._types[0],
+    );
+    this.dropdownItems = new ReactiveVar(
+      this.data().settings && this.data().settings.dropdownItems
+        ? this.data().settings.dropdownItems
+        : [],
+    );
   },
 
   types() {
     const currentType = this.data().type;
-    return this._types.
-      map((type) => {return {
+    return this._types.map(type => {
+      return {
         value: type,
         name: TAPi18n.__(`custom-field-${type}`),
         selected: type === currentType,
-      };});
+      };
+    });
   },
 
   isTypeNotSelected(type) {
@@ -40,113 +46,122 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
 
   getDropdownItems() {
     const items = this.dropdownItems.get();
-    Array.from(this.findAll('.js-field-settings-dropdown input')).forEach((el, index) => {
-      //console.log('each item!', index, el.value);
-      if (!items[index]) items[index] = {
-        _id: Random.id(6),
-      };
-      items[index].name = el.value.trim();
-    });
+    Array.from(this.findAll('.js-field-settings-dropdown input')).forEach(
+      (el, index) => {
+        //console.log('each item!', index, el.value);
+        if (!items[index])
+          items[index] = {
+            _id: Random.id(6),
+          };
+        items[index].name = el.value.trim();
+      },
+    );
     return items;
   },
 
   getSettings() {
     const settings = {};
     switch (this.type.get()) {
-    case 'dropdown': {
-      const dropdownItems = this.getDropdownItems().filter((item) => !!item.name.trim());
-      settings.dropdownItems = dropdownItems;
-      break;
-    }
+      case 'dropdown': {
+        const dropdownItems = this.getDropdownItems().filter(
+          item => !!item.name.trim(),
+        );
+        settings.dropdownItems = dropdownItems;
+        break;
+      }
     }
     return settings;
   },
 
   events() {
-    return [{
-      'change .js-field-type'(evt) {
-        const value = evt.target.value;
-        this.type.set(value);
-      },
-      'keydown .js-dropdown-item.last'(evt) {
-        if (evt.target.value.trim() && evt.keyCode === 13) {
-          const items = this.getDropdownItems();
-          this.dropdownItems.set(items);
-          evt.target.value = '';
-        }
+    return [
+      {
+        'change .js-field-type'(evt) {
+          const value = evt.target.value;
+          this.type.set(value);
+        },
+        'keydown .js-dropdown-item.last'(evt) {
+          if (evt.target.value.trim() && evt.keyCode === 13) {
+            const items = this.getDropdownItems();
+            this.dropdownItems.set(items);
+            evt.target.value = '';
+          }
+        },
+        'click .js-field-show-on-card'(evt) {
+          let $target = $(evt.target);
+          if (!$target.hasClass('js-field-show-on-card')) {
+            $target = $target.parent();
+          }
+          $target.find('.materialCheckBox').toggleClass('is-checked');
+          $target.toggleClass('is-checked');
+        },
+        'click .js-field-automatically-on-card'(evt) {
+          let $target = $(evt.target);
+          if (!$target.hasClass('js-field-automatically-on-card')) {
+            $target = $target.parent();
+          }
+          $target.find('.materialCheckBox').toggleClass('is-checked');
+          $target.toggleClass('is-checked');
+        },
+        'click .js-field-showLabel-on-card'(evt) {
+          let $target = $(evt.target);
+          if (!$target.hasClass('js-field-showLabel-on-card')) {
+            $target = $target.parent();
+          }
+          $target.find('.materialCheckBox').toggleClass('is-checked');
+          $target.toggleClass('is-checked');
+        },
+        'click .primary'(evt) {
+          evt.preventDefault();
+
+          const data = {
+            name: this.find('.js-field-name').value.trim(),
+            type: this.type.get(),
+            settings: this.getSettings(),
+            showOnCard: this.find('.js-field-show-on-card.is-checked') !== null,
+            showLabelOnMiniCard:
+              this.find('.js-field-showLabel-on-card.is-checked') !== null,
+            automaticallyOnCard:
+              this.find('.js-field-automatically-on-card.is-checked') !== null,
+          };
+
+          // insert or update
+          if (!this.data()._id) {
+            data.boardIds = [Session.get('currentBoard')];
+            CustomFields.insert(data);
+          } else {
+            CustomFields.update(this.data()._id, { $set: data });
+          }
+
+          Popup.back();
+        },
+        'click .js-delete-custom-field': Popup.afterConfirm(
+          'deleteCustomField',
+          function() {
+            const customField = CustomFields.findOne(this._id);
+            if (customField.boardIds.length > 1) {
+              CustomFields.update(customField._id, {
+                $pull: {
+                  boardIds: Session.get('currentBoard'),
+                },
+              });
+            } else {
+              CustomFields.remove(customField._id);
+            }
+            Popup.close();
+          },
+        ),
       },
-      'click .js-field-show-on-card'(evt) {
-        let $target = $(evt.target);
-        if(!$target.hasClass('js-field-show-on-card')){
-          $target = $target.parent();
-        }
-        $target.find('.materialCheckBox').toggleClass('is-checked');
-        $target.toggleClass('is-checked');
-      },
-      'click .js-field-automatically-on-card'(evt) {
-        let $target = $(evt.target);
-        if(!$target.hasClass('js-field-automatically-on-card')){
-          $target = $target.parent();
-        }
-        $target.find('.materialCheckBox').toggleClass('is-checked');
-        $target.toggleClass('is-checked');
-      },
-      'click .js-field-showLabel-on-card'(evt) {
-        let $target = $(evt.target);
-        if(!$target.hasClass('js-field-showLabel-on-card')){
-          $target = $target.parent();
-        }
-        $target.find('.materialCheckBox').toggleClass('is-checked');
-        $target.toggleClass('is-checked');
-      },
-      'click .primary'(evt) {
-        evt.preventDefault();
-
-        const data = {
-          name: this.find('.js-field-name').value.trim(),
-          type: this.type.get(),
-          settings: this.getSettings(),
-          showOnCard: this.find('.js-field-show-on-card.is-checked') !== null,
-          showLabelOnMiniCard: this.find('.js-field-showLabel-on-card.is-checked') !== null,
-          automaticallyOnCard: this.find('.js-field-automatically-on-card.is-checked') !== null,
-        };
-
-        // insert or update
-        if (!this.data()._id) {
-          data.boardIds = [Session.get('currentBoard')];
-          CustomFields.insert(data);
-        } else {
-          CustomFields.update(this.data()._id, {$set: data});
-        }
-
-        Popup.back();
-      },
-      'click .js-delete-custom-field': Popup.afterConfirm('deleteCustomField', function() {
-        const customField = CustomFields.findOne(this._id);
-        if (customField.boardIds.length > 1) {
-          CustomFields.update(customField._id, {
-            $pull: {
-              boardIds: Session.get('currentBoard'),
-            },
-          });
-        } else {
-          CustomFields.remove(customField._id);
-        }
-        Popup.close();
-      }),
-    }];
+    ];
   },
-
 });
 CreateCustomFieldPopup.register('createCustomFieldPopup');
 
 (class extends CreateCustomFieldPopup {
-
   template() {
     return 'createCustomFieldPopup';
   }
-
-}).register('editCustomFieldPopup');
+}.register('editCustomFieldPopup'));
 
 /*Template.deleteCustomFieldPopup.events({
   'submit'(evt) {

+ 78 - 76
client/components/sidebar/sidebarFilters.js

@@ -1,106 +1,108 @@
 BlazeComponent.extendComponent({
   events() {
-    return [{
-      'click .js-toggle-label-filter'(evt) {
-        evt.preventDefault();
-        Filter.labelIds.toggle(this.currentData()._id);
-        Filter.resetExceptions();
+    return [
+      {
+        'click .js-toggle-label-filter'(evt) {
+          evt.preventDefault();
+          Filter.labelIds.toggle(this.currentData()._id);
+          Filter.resetExceptions();
+        },
+        'click .js-toggle-member-filter'(evt) {
+          evt.preventDefault();
+          Filter.members.toggle(this.currentData()._id);
+          Filter.resetExceptions();
+        },
+        'click .js-toggle-custom-fields-filter'(evt) {
+          evt.preventDefault();
+          Filter.customFields.toggle(this.currentData()._id);
+          Filter.resetExceptions();
+        },
+        'change .js-field-advanced-filter'(evt) {
+          evt.preventDefault();
+          Filter.advanced.set(
+            this.find('.js-field-advanced-filter').value.trim(),
+          );
+          Filter.resetExceptions();
+        },
+        'click .js-clear-all'(evt) {
+          evt.preventDefault();
+          Filter.reset();
+        },
+        'click .js-filter-to-selection'(evt) {
+          evt.preventDefault();
+          const selectedCards = Cards.find(Filter.mongoSelector()).map(c => {
+            return c._id;
+          });
+          MultiSelection.add(selectedCards);
+        },
       },
-      'click .js-toggle-member-filter'(evt) {
-        evt.preventDefault();
-        Filter.members.toggle(this.currentData()._id);
-        Filter.resetExceptions();
-      },
-      'click .js-toggle-custom-fields-filter'(evt) {
-        evt.preventDefault();
-        Filter.customFields.toggle(this.currentData()._id);
-        Filter.resetExceptions();
-      },
-      'change .js-field-advanced-filter'(evt) {
-        evt.preventDefault();
-        Filter.advanced.set(this.find('.js-field-advanced-filter').value.trim());
-        Filter.resetExceptions();
-      },
-      'click .js-clear-all'(evt) {
-        evt.preventDefault();
-        Filter.reset();
-      },
-      'click .js-filter-to-selection'(evt) {
-        evt.preventDefault();
-        const selectedCards = Cards.find(Filter.mongoSelector()).map((c) => {
-          return c._id;
-        });
-        MultiSelection.add(selectedCards);
-      },
-    }];
+    ];
   },
 }).register('filterSidebar');
 
 function mutateSelectedCards(mutationName, ...args) {
-  Cards.find(MultiSelection.getMongoSelector()).forEach((card) => {
+  Cards.find(MultiSelection.getMongoSelector()).forEach(card => {
     card[mutationName](...args);
   });
 }
 
 BlazeComponent.extendComponent({
   mapSelection(kind, _id) {
-    return Cards.find(MultiSelection.getMongoSelector()).map((card) => {
+    return Cards.find(MultiSelection.getMongoSelector()).map(card => {
       const methodName = kind === 'label' ? 'hasLabel' : 'isAssigned';
       return card[methodName](_id);
     });
   },
 
   allSelectedElementHave(kind, _id) {
-    if (MultiSelection.isEmpty())
-      return false;
-    else
-      return _.every(this.mapSelection(kind, _id));
+    if (MultiSelection.isEmpty()) return false;
+    else return _.every(this.mapSelection(kind, _id));
   },
 
   someSelectedElementHave(kind, _id) {
-    if (MultiSelection.isEmpty())
-      return false;
-    else
-      return _.some(this.mapSelection(kind, _id));
+    if (MultiSelection.isEmpty()) return false;
+    else return _.some(this.mapSelection(kind, _id));
   },
 
   events() {
-    return [{
-      'click .js-toggle-label-multiselection'(evt) {
-        const labelId = this.currentData()._id;
-        const mappedSelection = this.mapSelection('label', labelId);
+    return [
+      {
+        'click .js-toggle-label-multiselection'(evt) {
+          const labelId = this.currentData()._id;
+          const mappedSelection = this.mapSelection('label', labelId);
 
-        if (_.every(mappedSelection)) {
-          mutateSelectedCards('removeLabel', labelId);
-        } else if (_.every(mappedSelection, (bool) => !bool)) {
-          mutateSelectedCards('addLabel', labelId);
-        } else {
-          const popup = Popup.open('disambiguateMultiLabel');
-          // XXX We need to have a better integration between the popup and the
-          // UI components systems.
-          popup.call(this.currentData(), evt);
-        }
-      },
-      'click .js-toggle-member-multiselection'(evt) {
-        const memberId = this.currentData()._id;
-        const mappedSelection = this.mapSelection('member', memberId);
-        if (_.every(mappedSelection)) {
-          mutateSelectedCards('unassignMember', memberId);
-        } else if (_.every(mappedSelection, (bool) => !bool)) {
-          mutateSelectedCards('assignMember', memberId);
-        } else {
-          const popup = Popup.open('disambiguateMultiMember');
-          // XXX We need to have a better integration between the popup and the
-          // UI components systems.
-          popup.call(this.currentData(), evt);
-        }
-      },
-      'click .js-move-selection': Popup.open('moveSelection'),
-      'click .js-archive-selection'() {
-        mutateSelectedCards('archive');
-        EscapeActions.executeUpTo('multiselection');
+          if (_.every(mappedSelection)) {
+            mutateSelectedCards('removeLabel', labelId);
+          } else if (_.every(mappedSelection, bool => !bool)) {
+            mutateSelectedCards('addLabel', labelId);
+          } else {
+            const popup = Popup.open('disambiguateMultiLabel');
+            // XXX We need to have a better integration between the popup and the
+            // UI components systems.
+            popup.call(this.currentData(), evt);
+          }
+        },
+        'click .js-toggle-member-multiselection'(evt) {
+          const memberId = this.currentData()._id;
+          const mappedSelection = this.mapSelection('member', memberId);
+          if (_.every(mappedSelection)) {
+            mutateSelectedCards('unassignMember', memberId);
+          } else if (_.every(mappedSelection, bool => !bool)) {
+            mutateSelectedCards('assignMember', memberId);
+          } else {
+            const popup = Popup.open('disambiguateMultiMember');
+            // XXX We need to have a better integration between the popup and the
+            // UI components systems.
+            popup.call(this.currentData(), evt);
+          }
+        },
+        'click .js-move-selection': Popup.open('moveSelection'),
+        'click .js-archive-selection'() {
+          mutateSelectedCards('archive');
+          EscapeActions.executeUpTo('multiselection');
+        },
       },
-    }];
+    ];
   },
 }).register('multiselectionSidebar');
 

+ 7 - 5
client/components/sidebar/sidebarSearches.js

@@ -9,11 +9,13 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'submit .js-search-term-form'(evt) {
-        evt.preventDefault();
-        this.term.set(evt.target.searchTerm.value);
+    return [
+      {
+        'submit .js-search-term-form'(evt) {
+          evt.preventDefault();
+          this.term.set(evt.target.searchTerm.value);
+        },
       },
-    }];
+    ];
   },
 }).register('searchSidebar');

+ 62 - 48
client/components/swimlanes/swimlaneHeader.js

@@ -6,9 +6,11 @@ Meteor.startup(() => {
 });
 
 BlazeComponent.extendComponent({
-  editTitle(evt) {
-    evt.preventDefault();
-    const newTitle = this.childComponents('inlinedForm')[0].getValue().trim();
+  editTitle(event) {
+    event.preventDefault();
+    const newTitle = this.childComponents('inlinedForm')[0]
+      .getValue()
+      .trim();
     const swimlane = this.currentData();
     if (newTitle) {
       swimlane.rename(newTitle.trim());
@@ -16,18 +18,20 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-open-swimlane-menu': Popup.open('swimlaneAction'),
-      'click .js-open-add-swimlane-menu': Popup.open('swimlaneAdd'),
-      submit: this.editTitle,
-    }];
+    return [
+      {
+        'click .js-open-swimlane-menu': Popup.open('swimlaneAction'),
+        'click .js-open-add-swimlane-menu': Popup.open('swimlaneAdd'),
+        submit: this.editTitle,
+      },
+    ];
   },
 }).register('swimlaneHeader');
 
 Template.swimlaneActionPopup.events({
   'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'),
-  'click .js-close-swimlane' (evt) {
-    evt.preventDefault();
+  'click .js-close-swimlane'(event) {
+    event.preventDefault();
     this.archive();
     Popup.close();
   },
@@ -39,34 +43,42 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      submit(evt) {
-        evt.preventDefault();
-        const currentBoard = Boards.findOne(Session.get('currentBoard'));
-        const nextSwimlane = currentBoard.nextSwimlane(this.currentSwimlane);
-        const titleInput = this.find('.swimlane-name-input');
-        const title = titleInput.value.trim();
-        const sortValue = calculateIndexData(this.currentSwimlane, nextSwimlane, 1);
-        const swimlaneType = (currentBoard.isTemplatesBoard())?'template-swimlane':'swimlane';
+    return [
+      {
+        submit(event) {
+          event.preventDefault();
+          const currentBoard = Boards.findOne(Session.get('currentBoard'));
+          const nextSwimlane = currentBoard.nextSwimlane(this.currentSwimlane);
+          const titleInput = this.find('.swimlane-name-input');
+          const title = titleInput.value.trim();
+          const sortValue = calculateIndexData(
+            this.currentSwimlane,
+            nextSwimlane,
+            1,
+          );
+          const swimlaneType = currentBoard.isTemplatesBoard()
+            ? 'template-swimlane'
+            : 'swimlane';
 
-        if (title) {
-          Swimlanes.insert({
-            title,
-            boardId: Session.get('currentBoard'),
-            sort: sortValue.base,
-            type: swimlaneType,
-          });
+          if (title) {
+            Swimlanes.insert({
+              title,
+              boardId: Session.get('currentBoard'),
+              sort: sortValue.base,
+              type: swimlaneType,
+            });
 
-          titleInput.value = '';
-          titleInput.focus();
-        }
-        // XXX ideally, we should move the popup to the newly
-        // created swimlane so a user can add more than one swimlane
-        // with a minimum of interactions
-        Popup.close();
+            titleInput.value = '';
+            titleInput.focus();
+          }
+          // XXX ideally, we should move the popup to the newly
+          // created swimlane so a user can add more than one swimlane
+          // with a minimum of interactions
+          Popup.close();
+        },
+        'click .js-swimlane-template': Popup.open('searchElement'),
       },
-      'click .js-swimlane-template': Popup.open('searchElement'),
-    }];
+    ];
   },
 }).register('swimlaneAddPopup');
 
@@ -77,7 +89,7 @@ BlazeComponent.extendComponent({
   },
 
   colors() {
-    return swimlaneColors.map((color) => ({ color, name: '' }));
+    return swimlaneColors.map(color => ({ color, name: '' }));
   },
 
   isSelected(color) {
@@ -85,18 +97,20 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-palette-color'() {
-        this.currentColor.set(this.currentData().color);
-      },
-      'click .js-submit' () {
-        this.currentSwimlane.setColor(this.currentColor.get());
-        Popup.close();
-      },
-      'click .js-remove-color'() {
-        this.currentSwimlane.setColor(null);
-        Popup.close();
+    return [
+      {
+        'click .js-palette-color'() {
+          this.currentColor.set(this.currentData().color);
+        },
+        'click .js-submit'() {
+          this.currentSwimlane.setColor(this.currentColor.get());
+          Popup.close();
+        },
+        'click .js-remove-color'() {
+          this.currentSwimlane.setColor(null);
+          Popup.close();
+        },
       },
-    }];
+    ];
   },
 }).register('setSwimlaneColorPopup');

+ 101 - 62
client/components/swimlanes/swimlanes.js

@@ -2,16 +2,27 @@ const { calculateIndex, enableClickOnTouch } = Utils;
 
 function currentListIsInThisSwimlane(swimlaneId) {
   const currentList = Lists.findOne(Session.get('currentList'));
-  return currentList && (currentList.swimlaneId === swimlaneId || currentList.swimlaneId === '');
+  return (
+    currentList &&
+    (currentList.swimlaneId === swimlaneId || currentList.swimlaneId === '')
+  );
 }
 
 function currentCardIsInThisList(listId, swimlaneId) {
   const currentCard = Cards.findOne(Session.get('currentCard'));
   const currentUser = Meteor.user();
-  if (currentUser && currentUser.profile && currentUser.profile.boardView === 'board-view-swimlanes')
-    return currentCard && currentCard.listId === listId && currentCard.swimlaneId === swimlaneId;
-  else // Default view: board-view-lists
-    return currentCard && currentCard.listId === listId;
+  if (
+    currentUser &&
+    currentUser.profile &&
+    currentUser.profile.boardView === 'board-view-swimlanes'
+  )
+    return (
+      currentCard &&
+      currentCard.listId === listId &&
+      currentCard.swimlaneId === swimlaneId
+    );
+  // Default view: board-view-lists
+  else return currentCard && currentCard.listId === listId;
   // https://github.com/wekan/wekan/issues/1623
   // https://github.com/ChronikEwok/wekan/commit/cad9b20451bb6149bfb527a99b5001873b06c3de
   // TODO: In public board, if you would like to switch between List/Swimlane view, you could
@@ -79,7 +90,11 @@ function initSortable(boardComponent, $listsDom) {
   enableClickOnTouch('.js-list:not(.js-list-composer)');
 
   function userIsMember() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   }
 
   // Disable drag-dropping while in multi-selection mode, or if the current user
@@ -87,8 +102,11 @@ function initSortable(boardComponent, $listsDom) {
   boardComponent.autorun(() => {
     const $listDom = $listsDom;
     if ($listDom.data('sortable')) {
-      $listsDom.sortable('option', 'disabled',
-        MultiSelection.isActive() || !userIsMember());
+      $listsDom.sortable(
+        'option',
+        'disabled',
+        MultiSelection.isActive() || !userIsMember(),
+      );
     }
   });
 }
@@ -124,47 +142,60 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      // Click-and-drag action
-      'mousedown .board-canvas'(evt) {
-        // Translating the board canvas using the click-and-drag action can
-        // conflict with the build-in browser mechanism to select text. We
-        // define a list of elements in which we disable the dragging because
-        // the user will legitimately expect to be able to select some text with
-        // his mouse.
-        const noDragInside = ['a', 'input', 'textarea', 'p', '.js-list-header'];
-        if ($(evt.target).closest(noDragInside.join(',')).length === 0 && this.$('.swimlane').prop('clientHeight') > evt.offsetY) {
-          this._isDragging = true;
-          this._lastDragPositionX = evt.clientX;
-        }
-      },
-      'mouseup'() {
-        if (this._isDragging) {
-          this._isDragging = false;
-        }
-      },
-      'mousemove'(evt) {
-        if (this._isDragging) {
-          // Update the canvas position
-          this.listsDom.scrollLeft -= evt.clientX - this._lastDragPositionX;
-          this._lastDragPositionX = evt.clientX;
-          // Disable browser text selection while dragging
-          evt.stopPropagation();
-          evt.preventDefault();
-          // Don't close opened card or inlined form at the end of the
-          // click-and-drag.
-          EscapeActions.executeUpTo('popup-close');
-          EscapeActions.preventNextClick();
-        }
+    return [
+      {
+        // Click-and-drag action
+        'mousedown .board-canvas'(evt) {
+          // Translating the board canvas using the click-and-drag action can
+          // conflict with the build-in browser mechanism to select text. We
+          // define a list of elements in which we disable the dragging because
+          // the user will legitimately expect to be able to select some text with
+          // his mouse.
+          const noDragInside = [
+            'a',
+            'input',
+            'textarea',
+            'p',
+            '.js-list-header',
+          ];
+          if (
+            $(evt.target).closest(noDragInside.join(',')).length === 0 &&
+            this.$('.swimlane').prop('clientHeight') > evt.offsetY
+          ) {
+            this._isDragging = true;
+            this._lastDragPositionX = evt.clientX;
+          }
+        },
+        mouseup() {
+          if (this._isDragging) {
+            this._isDragging = false;
+          }
+        },
+        mousemove(evt) {
+          if (this._isDragging) {
+            // Update the canvas position
+            this.listsDom.scrollLeft -= evt.clientX - this._lastDragPositionX;
+            this._lastDragPositionX = evt.clientX;
+            // Disable browser text selection while dragging
+            evt.stopPropagation();
+            evt.preventDefault();
+            // Don't close opened card or inlined form at the end of the
+            // click-and-drag.
+            EscapeActions.executeUpTo('popup-close');
+            EscapeActions.preventNextClick();
+          }
+        },
       },
-    }];
+    ];
   },
 }).register('swimlane');
 
 BlazeComponent.extendComponent({
   onCreated() {
     this.currentBoard = Boards.findOne(Session.get('currentBoard'));
-    this.isListTemplatesSwimlane = this.currentBoard.isTemplatesBoard() && this.currentData().isListTemplatesSwimlane();
+    this.isListTemplatesSwimlane =
+      this.currentBoard.isTemplatesBoard() &&
+      this.currentData().isListTemplatesSwimlane();
     this.currentSwimlane = this.currentData();
   },
 
@@ -174,32 +205,40 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      submit(evt) {
-        evt.preventDefault();
-        const titleInput = this.find('.list-name-input');
-        const title = titleInput.value.trim();
-        if (title) {
-          Lists.insert({
-            title,
-            boardId: Session.get('currentBoard'),
-            sort: $('.list').length,
-            type: (this.isListTemplatesSwimlane)?'template-list':'list',
-            swimlaneId: (this.currentBoard.isTemplatesBoard())?this.currentSwimlane._id:'',
-          });
-
-          titleInput.value = '';
-          titleInput.focus();
-        }
+    return [
+      {
+        submit(evt) {
+          evt.preventDefault();
+          const titleInput = this.find('.list-name-input');
+          const title = titleInput.value.trim();
+          if (title) {
+            Lists.insert({
+              title,
+              boardId: Session.get('currentBoard'),
+              sort: $('.list').length,
+              type: this.isListTemplatesSwimlane ? 'template-list' : 'list',
+              swimlaneId: this.currentBoard.isTemplatesBoard()
+                ? this.currentSwimlane._id
+                : '',
+            });
+
+            titleInput.value = '';
+            titleInput.focus();
+          }
+        },
+        'click .js-list-template': Popup.open('searchElement'),
       },
-      'click .js-list-template': Popup.open('searchElement'),
-    }];
+    ];
   },
 }).register('addListForm');
 
 Template.swimlane.helpers({
   canSeeAddList() {
-    return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
+    return (
+      Meteor.user() &&
+      Meteor.user().isBoardMember() &&
+      !Meteor.user().isCommentOnly()
+    );
   },
 });
 

+ 47 - 48
client/components/users/userAvatar.js

@@ -22,14 +22,11 @@ Template.userAvatar.helpers({
   presenceStatusClassName() {
     const user = Users.findOne(this.userId);
     const userPresence = presences.findOne({ userId: this.userId });
-    if (user && user.isInvitedTo(Session.get('currentBoard')))
-      return 'pending';
-    else if (!userPresence)
-      return 'disconnected';
+    if (user && user.isInvitedTo(Session.get('currentBoard'))) return 'pending';
+    else if (!userPresence) return 'disconnected';
     else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
       return 'active';
-    else
-      return 'idle';
+    else return 'idle';
   },
 });
 
@@ -45,7 +42,7 @@ Template.userAvatarInitials.helpers({
 
   viewPortWidth() {
     const user = Users.findOne(this.userId);
-    return (user && user.getInitials().length || 1) * 12;
+    return ((user && user.getInitials().length) || 1) * 12;
   },
 });
 
@@ -64,7 +61,7 @@ BlazeComponent.extendComponent({
   },
 
   uploadedAvatars() {
-    return Avatars.find({userId: Meteor.userId()});
+    return Avatars.find({ userId: Meteor.userId() });
   },
 
   isSelected() {
@@ -89,46 +86,48 @@ BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-upload-avatar'() {
-        this.$('.js-upload-avatar-input').click();
-      },
-      'change .js-upload-avatar-input'(evt) {
-        let file, fileUrl;
-
-        FS.Utility.eachFile(evt, (f) => {
-          try {
-            file = Avatars.insert(new FS.File(f));
-            fileUrl = file.url(this.avatarUrlOptions());
-          } catch (e) {
-            this.setError('avatar-too-big');
+    return [
+      {
+        'click .js-upload-avatar'() {
+          this.$('.js-upload-avatar-input').click();
+        },
+        'change .js-upload-avatar-input'(event) {
+          let file, fileUrl;
+
+          FS.Utility.eachFile(event, f => {
+            try {
+              file = Avatars.insert(new FS.File(f));
+              fileUrl = file.url(this.avatarUrlOptions());
+            } catch (e) {
+              this.setError('avatar-too-big');
+            }
+          });
+
+          if (fileUrl) {
+            this.setError('');
+            const fetchAvatarInterval = window.setInterval(() => {
+              $.ajax({
+                url: fileUrl,
+                success: () => {
+                  this.setAvatar(file.url(this.avatarUrlOptions()));
+                  window.clearInterval(fetchAvatarInterval);
+                },
+              });
+            }, 100);
           }
-        });
-
-        if (fileUrl) {
-          this.setError('');
-          const fetchAvatarInterval = window.setInterval(() => {
-            $.ajax({
-              url: fileUrl,
-              success: () => {
-                this.setAvatar(file.url(this.avatarUrlOptions()));
-                window.clearInterval(fetchAvatarInterval);
-              },
-            });
-          }, 100);
-        }
-      },
-      'click .js-select-avatar'() {
-        const avatarUrl = this.currentData().url(this.avatarUrlOptions());
-        this.setAvatar(avatarUrl);
-      },
-      'click .js-select-initials'() {
-        this.setAvatar('');
-      },
-      'click .js-delete-avatar'() {
-        Avatars.remove(this.currentData()._id);
+        },
+        'click .js-select-avatar'() {
+          const avatarUrl = this.currentData().url(this.avatarUrlOptions());
+          this.setAvatar(avatarUrl);
+        },
+        'click .js-select-initials'() {
+          this.setAvatar('');
+        },
+        'click .js-delete-avatar'() {
+          Avatars.remove(this.currentData()._id);
+        },
       },
-    }];
+    ];
   },
 }).register('changeAvatarPopup');
 
@@ -146,11 +145,11 @@ Template.cardMembersPopup.helpers({
 });
 
 Template.cardMembersPopup.events({
-  'click .js-select-member'(evt) {
+  'click .js-select-member'(event) {
     const card = Cards.findOne(Session.get('currentCard'));
     const memberId = this.userId;
     card.toggleMember(memberId);
-    evt.preventDefault();
+    event.preventDefault();
   },
 });
 

+ 48 - 36
client/components/users/userHeader.js

@@ -18,8 +18,8 @@ Template.memberMenuPopup.events({
   'click .js-change-avatar': Popup.open('changeAvatar'),
   'click .js-change-password': Popup.open('changePassword'),
   'click .js-change-language': Popup.open('changeLanguage'),
-  'click .js-logout'(evt) {
-    evt.preventDefault();
+  'click .js-logout'(event) {
+    event.preventDefault();
 
     AccountsTemplates.logout();
   },
@@ -38,12 +38,12 @@ Template.editProfilePopup.helpers({
 });
 
 Template.editProfilePopup.events({
-  submit(evt, tpl) {
-    evt.preventDefault();
-    const fullname = tpl.find('.js-profile-fullname').value.trim();
-    const username = tpl.find('.js-profile-username').value.trim();
-    const initials = tpl.find('.js-profile-initials').value.trim();
-    const email = tpl.find('.js-profile-email').value.trim();
+  submit(event, templateInstance) {
+    event.preventDefault();
+    const fullname = templateInstance.find('.js-profile-fullname').value.trim();
+    const username = templateInstance.find('.js-profile-username').value.trim();
+    const initials = templateInstance.find('.js-profile-initials').value.trim();
+    const email = templateInstance.find('.js-profile-email').value.trim();
     let isChangeUserName = false;
     let isChangeEmail = false;
     Users.update(Meteor.userId(), {
@@ -53,29 +53,36 @@ Template.editProfilePopup.events({
       },
     });
     isChangeUserName = username !== Meteor.user().username;
-    isChangeEmail = email.toLowerCase() !== Meteor.user().emails[0].address.toLowerCase();
+    isChangeEmail =
+      email.toLowerCase() !== Meteor.user().emails[0].address.toLowerCase();
     if (isChangeUserName && isChangeEmail) {
-      Meteor.call('setUsernameAndEmail', username, email.toLowerCase(), Meteor.userId(), function (error) {
-        const usernameMessageElement = tpl.$('.username-taken');
-        const emailMessageElement = tpl.$('.email-taken');
-        if (error) {
-          const errorElement = error.error;
-          if (errorElement === 'username-already-taken') {
-            usernameMessageElement.show();
-            emailMessageElement.hide();
-          } else if (errorElement === 'email-already-taken') {
+      Meteor.call(
+        'setUsernameAndEmail',
+        username,
+        email.toLowerCase(),
+        Meteor.userId(),
+        function(error) {
+          const usernameMessageElement = templateInstance.$('.username-taken');
+          const emailMessageElement = templateInstance.$('.email-taken');
+          if (error) {
+            const errorElement = error.error;
+            if (errorElement === 'username-already-taken') {
+              usernameMessageElement.show();
+              emailMessageElement.hide();
+            } else if (errorElement === 'email-already-taken') {
+              usernameMessageElement.hide();
+              emailMessageElement.show();
+            }
+          } else {
             usernameMessageElement.hide();
-            emailMessageElement.show();
+            emailMessageElement.hide();
+            Popup.back();
           }
-        } else {
-          usernameMessageElement.hide();
-          emailMessageElement.hide();
-          Popup.back();
-        }
-      });
+        },
+      );
     } else if (isChangeUserName) {
-      Meteor.call('setUsername', username, Meteor.userId(), function (error) {
-        const messageElement = tpl.$('.username-taken');
+      Meteor.call('setUsername', username, Meteor.userId(), function(error) {
+        const messageElement = templateInstance.$('.username-taken');
         if (error) {
           messageElement.show();
         } else {
@@ -84,8 +91,10 @@ Template.editProfilePopup.events({
         }
       });
     } else if (isChangeEmail) {
-      Meteor.call('setEmail', email.toLowerCase(), Meteor.userId(), function (error) {
-        const messageElement = tpl.$('.email-taken');
+      Meteor.call('setEmail', email.toLowerCase(), Meteor.userId(), function(
+        error,
+      ) {
+        const messageElement = templateInstance.$('.email-taken');
         if (error) {
           messageElement.show();
         } else {
@@ -104,7 +113,7 @@ Template.editProfilePopup.events({
 
 // XXX For some reason the useraccounts autofocus isnt working in this case.
 // See https://github.com/meteor-useraccounts/core/issues/384
-Template.changePasswordPopup.onRendered(function () {
+Template.changePasswordPopup.onRendered(function() {
   this.find('#at-field-current_password').focus();
 });
 
@@ -123,7 +132,7 @@ Template.changeLanguagePopup.helpers({
         name = 'Occitan';
       }
       return { tag, name };
-    }).sort(function (a, b) {
+    }).sort(function(a, b) {
       if (a.name === b.name) {
         return 0;
       } else {
@@ -138,13 +147,13 @@ Template.changeLanguagePopup.helpers({
 });
 
 Template.changeLanguagePopup.events({
-  'click .js-set-language'(evt) {
+  'click .js-set-language'(event) {
     Users.update(Meteor.userId(), {
       $set: {
         'profile.language': this.tag,
       },
     });
-    evt.preventDefault();
+    event.preventDefault();
   },
 });
 
@@ -161,9 +170,12 @@ Template.changeSettingsPopup.events({
   'click .js-toggle-system-messages'() {
     Meteor.call('toggleSystemMessages');
   },
-  'click .js-apply-show-cards-at'(evt, tpl) {
-    evt.preventDefault();
-    const minLimit = parseInt(tpl.$('#show-cards-count-at').val(), 10);
+  'click .js-apply-show-cards-at'(event, templateInstance) {
+    event.preventDefault();
+    const minLimit = parseInt(
+      templateInstance.$('#show-cards-count-at').val(),
+      10,
+    );
     if (!isNaN(minLimit)) {
       Meteor.call('changeLimitToShowCardsCount', minLimit);
       Popup.back();

+ 1 - 1
client/config/blazeHelpers.js

@@ -25,7 +25,7 @@ Blaze.registerHelper('currentList', () => {
   }
 });
 
-Blaze.registerHelper('getUser', (userId) => Users.findOne(userId));
+Blaze.registerHelper('getUser', userId => Users.findOne(userId));
 
 Blaze.registerHelper('concat', (...args) => args.slice(0, -1).join(''));
 

+ 1 - 4
client/lib/accessibility.js

@@ -27,10 +27,7 @@ function copyTitleInAriaLabel(attributes) {
 // which is a little bit hacky -- but still reasonable with our ES6 usage. If we
 // end up switching to React we will probably create lower level small
 // components to handle that without overwriting any build-in function.
-const {
-  A: superA,
-  I: superI,
-} = HTML;
+const { A: superA, I: superI } = HTML;
 
 HTML.A = (attributes, ...others) => {
   return superA(copyTitleInAriaLabel(enforceHref(attributes)), ...others);

+ 10 - 10
client/lib/cssEvents.js

@@ -3,11 +3,11 @@
 function whichTransitionEvent() {
   const el = document.createElement('fakeelement');
   const transitions = {
-    transition:'transitionend',
-    OTransition:'oTransitionEnd',
-    MSTransition:'msTransitionEnd',
-    MozTransition:'transitionend',
-    WebkitTransition:'webkitTransitionEnd',
+    transition: 'transitionend',
+    OTransition: 'oTransitionEnd',
+    MSTransition: 'msTransitionEnd',
+    MozTransition: 'transitionend',
+    WebkitTransition: 'webkitTransitionEnd',
   };
 
   for (const t in transitions) {
@@ -21,11 +21,11 @@ function whichTransitionEvent() {
 function whichAnimationEvent() {
   const el = document.createElement('fakeelement');
   const transitions = {
-    animation:'animationend',
-    OAnimation:'oAnimationEnd',
-    MSTransition:'msAnimationEnd',
-    MozAnimation:'animationend',
-    WebkitAnimation:'webkitAnimationEnd',
+    animation: 'animationend',
+    OAnimation: 'oAnimationEnd',
+    MSTransition: 'msAnimationEnd',
+    MozAnimation: 'animationend',
+    WebkitAnimation: 'webkitAnimationEnd',
   };
 
   for (const t in transitions) {

+ 54 - 48
client/lib/datepicker.js

@@ -10,15 +10,20 @@ DatePicker = BlazeComponent.extendComponent({
   },
 
   onRendered() {
-    const $picker = this.$('.js-datepicker').datepicker({
-      todayHighlight: true,
-      todayBtn: 'linked',
-      language: TAPi18n.getLanguage(),
-    }).on('changeDate', function(evt) {
-      this.find('#date').value = moment(evt.date).format('L');
-      this.error.set('');
-      this.find('#time').focus();
-    }.bind(this));
+    const $picker = this.$('.js-datepicker')
+      .datepicker({
+        todayHighlight: true,
+        todayBtn: 'linked',
+        language: TAPi18n.getLanguage(),
+      })
+      .on(
+        'changeDate',
+        function(evt) {
+          this.find('#date').value = moment(evt.date).format('L');
+          this.error.set('');
+          this.find('#time').focus();
+        }.bind(this),
+      );
 
     if (this.date.get().isValid()) {
       $picker.datepicker('update', this.date.get().toDate());
@@ -26,13 +31,11 @@ DatePicker = BlazeComponent.extendComponent({
   },
 
   showDate() {
-    if (this.date.get().isValid())
-      return this.date.get().format('L');
+    if (this.date.get().isValid()) return this.date.get().format('L');
     return '';
   },
   showTime() {
-    if (this.date.get().isValid())
-      return this.date.get().format('LT');
+    if (this.date.get().isValid()) return this.date.get().format('LT');
     return '';
   },
   dateFormat() {
@@ -43,44 +46,47 @@ DatePicker = BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'keyup .js-date-field'() {
-        // parse for localized date format in strict mode
-        const dateMoment = moment(this.find('#date').value, 'L', true);
-        if (dateMoment.isValid()) {
-          this.error.set('');
-          this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
-        }
-      },
-      'keyup .js-time-field'() {
-        // parse for localized time format in strict mode
-        const dateMoment = moment(this.find('#time').value, 'LT', true);
-        if (dateMoment.isValid()) {
-          this.error.set('');
-        }
-      },
-      'submit .edit-date'(evt) {
-        evt.preventDefault();
+    return [
+      {
+        'keyup .js-date-field'() {
+          // parse for localized date format in strict mode
+          const dateMoment = moment(this.find('#date').value, 'L', true);
+          if (dateMoment.isValid()) {
+            this.error.set('');
+            this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
+          }
+        },
+        'keyup .js-time-field'() {
+          // parse for localized time format in strict mode
+          const dateMoment = moment(this.find('#time').value, 'LT', true);
+          if (dateMoment.isValid()) {
+            this.error.set('');
+          }
+        },
+        'submit .edit-date'(evt) {
+          evt.preventDefault();
 
-        // if no time was given, init with 12:00
-        const time = evt.target.time.value || moment(new Date().setHours(12, 0, 0)).format('LT');
+          // if no time was given, init with 12:00
+          const time =
+            evt.target.time.value ||
+            moment(new Date().setHours(12, 0, 0)).format('LT');
 
-        const dateString = `${evt.target.date.value} ${time}`;
-        const newDate = moment(dateString, 'L LT', true);
-        if (newDate.isValid()) {
-          this._storeDate(newDate.toDate());
+          const dateString = `${evt.target.date.value} ${time}`;
+          const newDate = moment(dateString, 'L LT', true);
+          if (newDate.isValid()) {
+            this._storeDate(newDate.toDate());
+            Popup.close();
+          } else {
+            this.error.set('invalid-date');
+            evt.target.date.focus();
+          }
+        },
+        'click .js-delete-date'(evt) {
+          evt.preventDefault();
+          this._deleteDate();
           Popup.close();
-        }
-        else {
-          this.error.set('invalid-date');
-          evt.target.date.focus();
-        }
-      },
-      'click .js-delete-date'(evt) {
-        evt.preventDefault();
-        this._deleteDate();
-        Popup.close();
+        },
       },
-    }];
+    ];
   },
 });

+ 8 - 5
client/lib/dropImage.js

@@ -11,7 +11,10 @@
   $.event.fix = (function(originalFix) {
     return function(event) {
       event = originalFix.apply(this, arguments);
-      if (event.type.indexOf('drag') === 0 || event.type.indexOf('drop') === 0) {
+      if (
+        event.type.indexOf('drag') === 0 ||
+        event.type.indexOf('drop') === 0
+      ) {
         event.dataTransfer = event.originalEvent.dataTransfer;
       }
       return event;
@@ -23,7 +26,7 @@
     matchType: /image.*/,
   };
 
-  return $.fn.dropImageReader = function(options) {
+  return ($.fn.dropImageReader = function(options) {
     if (typeof options === 'function') {
       options = {
         callback: options,
@@ -40,9 +43,9 @@
       return $(element).bind('drop', function(event) {
         stopFn(event);
         const files = event.dataTransfer.files;
-        for(let i=0; i<files.length; i++) {
+        for (let i = 0; i < files.length; i++) {
           const f = files[i];
-          if(f.type.match(options.matchType)) {
+          if (f.type.match(options.matchType)) {
             const reader = new FileReader();
             reader.onload = function(evt) {
               return options.callback.call(element, {
@@ -58,5 +61,5 @@
         }
       });
     });
-  };
+  });
 })(jQuery);

+ 25 - 23
client/lib/escapeActions.js

@@ -33,13 +33,19 @@ EscapeActions = {
 
     const noClickEscapeOn = options.noClickEscapeOn;
 
-    this._actions = _.sortBy([...this._actions, {
-      priority,
-      condition,
-      action,
-      noClickEscapeOn,
-      enabledOnClick,
-    }], (action) => action.priority);
+    this._actions = _.sortBy(
+      [
+        ...this._actions,
+        {
+          priority,
+          condition,
+          action,
+          noClickEscapeOn,
+          enabledOnClick,
+        },
+      ],
+      action => action.priority,
+    );
   },
 
   executeLowest() {
@@ -80,10 +86,8 @@ EscapeActions = {
   },
 
   _stopClick(action, clickTarget) {
-    if (!_.isString(action.noClickEscapeOn))
-      return false;
-    else
-      return $(clickTarget).closest(action.noClickEscapeOn).length > 0;
+    if (!_.isString(action.noClickEscapeOn)) return false;
+    else return $(clickTarget).closest(action.noClickEscapeOn).length > 0;
   },
 
   _execute(options) {
@@ -95,14 +99,11 @@ EscapeActions = {
     let executedAtLeastOne = false;
     let maxPriority;
 
-    if (!maxLabel)
-      maxPriority = Infinity;
-    else
-      maxPriority = this.hierarchy.indexOf(maxLabel);
+    if (!maxLabel) maxPriority = Infinity;
+    else maxPriority = this.hierarchy.indexOf(maxLabel);
 
     for (const currentAction of this._actions) {
-      if (currentAction.priority > maxPriority)
-        return executedAtLeastOne;
+      if (currentAction.priority > maxPriority) return executedAtLeastOne;
 
       if (isClick && this._stopClick(currentAction, clickTarget))
         return executedAtLeastOne;
@@ -111,8 +112,7 @@ EscapeActions = {
       if (isEnabled && currentAction.condition()) {
         currentAction.action();
         executedAtLeastOne = true;
-        if (!multipleActions)
-          return executedAtLeastOne;
+        if (!multipleActions) return executedAtLeastOne;
       }
     }
     return executedAtLeastOne;
@@ -128,13 +128,15 @@ Mousetrap.bindGlobal('esc', () => {
 // On a left click on the document, we try to exectute one escape action (eg,
 // close the popup). We don't execute any action if the user has clicked on a
 // link or a button.
-$(document).on('click', (evt) => {
-  if (evt.button === 0 &&
-    $(evt.target).closest('a,button,.is-editable').length === 0) {
+$(document).on('click', evt => {
+  if (
+    evt.button === 0 &&
+    $(evt.target).closest('a,button,.is-editable').length === 0
+  ) {
     EscapeActions.clickExecute(evt.target, 'multiselection');
   }
 });
 
-$(document).on('click', 'a[href=\\#]',  (evt) => {
+$(document).on('click', 'a[href=\\#]', evt => {
   evt.preventDefault();
 });

+ 213 - 221
client/lib/filter.js

@@ -71,18 +71,19 @@ class SetFilter {
   _getEmptySelector() {
     this._dep.depend();
     let includeEmpty = false;
-    this._selectedElements.forEach((el) => {
+    this._selectedElements.forEach(el => {
       if (el === undefined) {
         includeEmpty = true;
       }
     });
-    return includeEmpty ? {
-      $eq: [],
-    } : null;
+    return includeEmpty
+      ? {
+          $eq: [],
+        }
+      : null;
   }
 }
 
-
 // Advanced filter forms a MongoSelector from a users String.
 // Build by: Ignatz 19.05.2018 (github feuerball11)
 class AdvancedFilter {
@@ -128,7 +129,8 @@ class AdvancedFilter {
         current += char;
         continue;
       }
-      if (char === '\'') {
+      // eslint-disable-next-line quotes
+      if (char === "'") {
         string = !string;
         if (string) wasString = true;
         continue;
@@ -139,8 +141,8 @@ class AdvancedFilter {
       }
       if (char === ' ' && !string) {
         commands.push({
-          'cmd': current,
-          'string': wasString,
+          cmd: current,
+          string: wasString,
           regex,
         });
         wasString = false;
@@ -151,8 +153,8 @@ class AdvancedFilter {
     }
     if (current !== '') {
       commands.push({
-        'cmd': current,
-        'string': wasString,
+        cmd: current,
+        string: wasString,
         regex,
       });
     }
@@ -161,16 +163,19 @@ class AdvancedFilter {
 
   _fieldNameToId(field) {
     const found = CustomFields.findOne({
-      'name': field,
+      name: field,
     });
     return found._id;
   }
 
   _fieldValueToId(field, value) {
     const found = CustomFields.findOne({
-      'name': field,
+      name: field,
     });
-    if (found.settings.dropdownItems && found.settings.dropdownItems.length > 0) {
+    if (
+      found.settings.dropdownItems &&
+      found.settings.dropdownItems.length > 0
+    ) {
       for (let i = 0; i < found.settings.dropdownItems.length; i++) {
         if (found.settings.dropdownItems[i].name === value) {
           return found.settings.dropdownItems[i]._id;
@@ -202,37 +207,32 @@ class AdvancedFilter {
     for (let i = 0; i < commands.length; i++) {
       if (commands[i].cmd) {
         switch (commands[i].cmd) {
-        case '(':
-        {
-          level++;
-          if (start === -1) start = i;
-          continue;
-        }
-        case ')':
-        {
-          level--;
-          commands.splice(i, 1);
-          i--;
-          continue;
-        }
-        default:
-        {
-          if (level > 0) {
-            subcommands.push(commands[i]);
+          case '(': {
+            level++;
+            if (start === -1) start = i;
+            continue;
+          }
+          case ')': {
+            level--;
             commands.splice(i, 1);
             i--;
             continue;
           }
-        }
+          default: {
+            if (level > 0) {
+              subcommands.push(commands[i]);
+              commands.splice(i, 1);
+              i--;
+              continue;
+            }
+          }
         }
       }
     }
     if (start !== -1) {
       this._processSubCommands(subcommands);
-      if (subcommands.length === 1)
-        commands.splice(start, 0, subcommands[0]);
-      else
-        commands.splice(start, 0, subcommands);
+      if (subcommands.length === 1) commands.splice(start, 0, subcommands[0]);
+      else commands.splice(start, 0, subcommands);
     }
     this._processConditions(commands);
     this._processLogicalOperators(commands);
@@ -242,149 +242,139 @@ class AdvancedFilter {
     for (let i = 0; i < commands.length; i++) {
       if (!commands[i].string && commands[i].cmd) {
         switch (commands[i].cmd) {
-        case '=':
-        case '==':
-        case '===':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          if (commands[i + 1].regex) {
-            const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
-            let regex = null;
-            if (match.length > 2)
-              regex = new RegExp(match[1], match[2]);
-            else
-              regex = new RegExp(match[1]);
+          case '=':
+          case '==':
+          case '===': {
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
+            if (commands[i + 1].regex) {
+              const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
+              let regex = null;
+              if (match.length > 2) regex = new RegExp(match[1], match[2]);
+              else regex = new RegExp(match[1]);
+              commands[i] = {
+                'customFields._id': this._fieldNameToId(field),
+                'customFields.value': regex,
+              };
+            } else {
+              commands[i] = {
+                'customFields._id': this._fieldNameToId(field),
+                'customFields.value': {
+                  $in: [this._fieldValueToId(field, str), parseInt(str, 10)],
+                },
+              };
+            }
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
+          case '!=':
+          case '!==': {
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
+            if (commands[i + 1].regex) {
+              const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
+              let regex = null;
+              if (match.length > 2) regex = new RegExp(match[1], match[2]);
+              else regex = new RegExp(match[1]);
+              commands[i] = {
+                'customFields._id': this._fieldNameToId(field),
+                'customFields.value': {
+                  $not: regex,
+                },
+              };
+            } else {
+              commands[i] = {
+                'customFields._id': this._fieldNameToId(field),
+                'customFields.value': {
+                  $not: {
+                    $in: [this._fieldValueToId(field, str), parseInt(str, 10)],
+                  },
+                },
+              };
+            }
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
+          case '>':
+          case 'gt':
+          case 'Gt':
+          case 'GT': {
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
             commands[i] = {
               'customFields._id': this._fieldNameToId(field),
-              'customFields.value': regex,
+              'customFields.value': {
+                $gt: parseInt(str, 10),
+              },
             };
-          } else {
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
+          case '>=':
+          case '>==':
+          case 'gte':
+          case 'Gte':
+          case 'GTE': {
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
             commands[i] = {
               'customFields._id': this._fieldNameToId(field),
               'customFields.value': {
-                $in: [this._fieldValueToId(field, str), parseInt(str, 10)],
+                $gte: parseInt(str, 10),
               },
             };
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
           }
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
-        case '!=':
-        case '!==':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          if (commands[i + 1].regex) {
-            const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
-            let regex = null;
-            if (match.length > 2)
-              regex = new RegExp(match[1], match[2]);
-            else
-              regex = new RegExp(match[1]);
+          case '<':
+          case 'lt':
+          case 'Lt':
+          case 'LT': {
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
             commands[i] = {
               'customFields._id': this._fieldNameToId(field),
               'customFields.value': {
-                $not: regex,
+                $lt: parseInt(str, 10),
               },
             };
-          } else {
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
+          case '<=':
+          case '<==':
+          case 'lte':
+          case 'Lte':
+          case 'LTE': {
+            const field = commands[i - 1].cmd;
+            const str = commands[i + 1].cmd;
             commands[i] = {
               'customFields._id': this._fieldNameToId(field),
               'customFields.value': {
-                $not: {
-                  $in: [this._fieldValueToId(field, str), parseInt(str, 10)],
-                },
+                $lte: parseInt(str, 10),
               },
             };
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
           }
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
-        case '>':
-        case 'gt':
-        case 'Gt':
-        case 'GT':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          commands[i] = {
-            'customFields._id': this._fieldNameToId(field),
-            'customFields.value': {
-              $gt: parseInt(str, 10),
-            },
-          };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
-        case '>=':
-        case '>==':
-        case 'gte':
-        case 'Gte':
-        case 'GTE':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          commands[i] = {
-            'customFields._id': this._fieldNameToId(field),
-            'customFields.value': {
-              $gte: parseInt(str, 10),
-            },
-          };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
-        case '<':
-        case 'lt':
-        case 'Lt':
-        case 'LT':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          commands[i] = {
-            'customFields._id': this._fieldNameToId(field),
-            'customFields.value': {
-              $lt: parseInt(str, 10),
-            },
-          };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
-        case '<=':
-        case '<==':
-        case 'lte':
-        case 'Lte':
-        case 'LTE':
-        {
-          const field = commands[i - 1].cmd;
-          const str = commands[i + 1].cmd;
-          commands[i] = {
-            'customFields._id': this._fieldNameToId(field),
-            'customFields.value': {
-              $lte: parseInt(str, 10),
-            },
-          };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
         }
       }
     }
@@ -394,54 +384,51 @@ class AdvancedFilter {
     for (let i = 0; i < commands.length; i++) {
       if (!commands[i].string && commands[i].cmd) {
         switch (commands[i].cmd) {
-        case 'or':
-        case 'Or':
-        case 'OR':
-        case '|':
-        case '||':
-        {
-          const op1 = commands[i - 1];
-          const op2 = commands[i + 1];
-          commands[i] = {
-            $or: [op1, op2],
-          };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
-        case 'and':
-        case 'And':
-        case 'AND':
-        case '&':
-        case '&&':
-        {
-          const op1 = commands[i - 1];
-          const op2 = commands[i + 1];
-          commands[i] = {
-            $and: [op1, op2],
-          };
-          commands.splice(i - 1, 1);
-          commands.splice(i, 1);
-          //changed = true;
-          i--;
-          break;
-        }
-        case 'not':
-        case 'Not':
-        case 'NOT':
-        case '!':
-        {
-          const op1 = commands[i + 1];
-          commands[i] = {
-            $not: op1,
-          };
-          commands.splice(i + 1, 1);
-          //changed = true;
-          i--;
-          break;
-        }
+          case 'or':
+          case 'Or':
+          case 'OR':
+          case '|':
+          case '||': {
+            const op1 = commands[i - 1];
+            const op2 = commands[i + 1];
+            commands[i] = {
+              $or: [op1, op2],
+            };
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
+          case 'and':
+          case 'And':
+          case 'AND':
+          case '&':
+          case '&&': {
+            const op1 = commands[i - 1];
+            const op2 = commands[i + 1];
+            commands[i] = {
+              $and: [op1, op2],
+            };
+            commands.splice(i - 1, 1);
+            commands.splice(i, 1);
+            //changed = true;
+            i--;
+            break;
+          }
+          case 'not':
+          case 'Not':
+          case 'NOT':
+          case '!': {
+            const op1 = commands[i + 1];
+            commands[i] = {
+              $not: op1,
+            };
+            commands.splice(i + 1, 1);
+            //changed = true;
+            i--;
+            break;
+          }
         }
       }
     }
@@ -452,7 +439,6 @@ class AdvancedFilter {
     const commands = this._filterToCommands();
     return this._arrayToSelector(commands);
   }
-
 }
 
 // The global Filter object.
@@ -477,23 +463,26 @@ Filter = {
   _exceptionsDep: new Tracker.Dependency(),
 
   isActive() {
-    return _.any(this._fields, (fieldName) => {
-      return this[fieldName]._isActive();
-    }) || this.advanced._isActive();
+    return (
+      _.any(this._fields, fieldName => {
+        return this[fieldName]._isActive();
+      }) || this.advanced._isActive()
+    );
   },
 
   _getMongoSelector() {
-    if (!this.isActive())
-      return {};
+    if (!this.isActive()) return {};
 
     const filterSelector = {};
     const emptySelector = {};
     let includeEmptySelectors = false;
-    this._fields.forEach((fieldName) => {
+    this._fields.forEach(fieldName => {
       const filter = this[fieldName];
       if (filter._isActive()) {
         if (filter.subField !== '') {
-          filterSelector[`${fieldName}.${filter.subField}`] = filter._getMongoSelector();
+          filterSelector[
+            `${fieldName}.${filter.subField}`
+          ] = filter._getMongoSelector();
         } else {
           filterSelector[fieldName] = filter._getMongoSelector();
         }
@@ -513,11 +502,15 @@ Filter = {
 
     const selectors = [exceptionsSelector];
 
-    if (_.any(this._fields, (fieldName) => {
-      return this[fieldName]._isActive();
-    })) selectors.push(filterSelector);
+    if (
+      _.any(this._fields, fieldName => {
+        return this[fieldName]._isActive();
+      })
+    )
+      selectors.push(filterSelector);
     if (includeEmptySelectors) selectors.push(emptySelector);
-    if (this.advanced._isActive()) selectors.push(this.advanced._getMongoSelector());
+    if (this.advanced._isActive())
+      selectors.push(this.advanced._getMongoSelector());
 
     return {
       $or: selectors,
@@ -526,8 +519,7 @@ Filter = {
 
   mongoSelector(additionalSelector) {
     const filterSelector = this._getMongoSelector();
-    if (_.isUndefined(additionalSelector))
-      return filterSelector;
+    if (_.isUndefined(additionalSelector)) return filterSelector;
     else
       return {
         $and: [filterSelector, additionalSelector],
@@ -535,7 +527,7 @@ Filter = {
   },
 
   reset() {
-    this._fields.forEach((fieldName) => {
+    this._fields.forEach(fieldName => {
       const filter = this[fieldName];
       filter.reset();
     });

+ 1 - 1
client/lib/i18n.js

@@ -12,7 +12,7 @@ Meteor.startup(() => {
     }
 
     if (!language) {
-      if(navigator.languages) {
+      if (navigator.languages) {
         language = navigator.languages[0];
       } else {
         language = navigator.language || navigator.userLanguage;

+ 29 - 21
client/lib/inlinedform.js

@@ -48,35 +48,43 @@ InlinedForm = BlazeComponent.extendComponent({
   },
 
   events() {
-    return [{
-      'click .js-close-inlined-form': this.close,
-      'click .js-open-inlined-form': this.open,
+    return [
+      {
+        'click .js-close-inlined-form': this.close,
+        'click .js-open-inlined-form': this.open,
 
-      // Pressing Ctrl+Enter should submit the form
-      'keydown form textarea'(evt) {
-        if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
-          this.find('button[type=submit]').click();
-        }
-      },
+        // Pressing Ctrl+Enter should submit the form
+        'keydown form textarea'(evt) {
+          if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
+            this.find('button[type=submit]').click();
+          }
+        },
 
-      // Close the inlined form when after its submission
-      submit() {
-        if (this.currentData().autoclose !== false) {
-          Tracker.afterFlush(() => {
-            this.close();
-          });
-        }
+        // Close the inlined form when after its submission
+        submit() {
+          if (this.currentData().autoclose !== false) {
+            Tracker.afterFlush(() => {
+              this.close();
+            });
+          }
+        },
       },
-    }];
+    ];
   },
 }).register('inlinedForm');
 
 // Press escape to close the currently opened inlinedForm
-EscapeActions.register('inlinedForm',
-  () => { currentlyOpenedForm.get().close(); },
-  () => { return currentlyOpenedForm.get() !== null; }, {
+EscapeActions.register(
+  'inlinedForm',
+  () => {
+    currentlyOpenedForm.get().close();
+  },
+  () => {
+    return currentlyOpenedForm.get() !== null;
+  },
+  {
     enabledOnClick: false,
-  }
+  },
 );
 
 // submit on click outside

+ 39 - 28
client/lib/keyboard.js

@@ -40,8 +40,10 @@ Mousetrap.bind(['down', 'up'], (evt, key) => {
     return;
   }
 
-  const nextFunc = (key === 'down' ? 'next' : 'prev');
-  const nextCard = $('.js-minicard.is-selected')[nextFunc]('.js-minicard').get(0);
+  const nextFunc = key === 'down' ? 'next' : 'prev';
+  const nextCard = $('.js-minicard.is-selected')
+    [nextFunc]('.js-minicard')
+    .get(0);
   if (nextCard) {
     const nextCardId = Blaze.getData(nextCard)._id;
     Utils.goCardId(nextCardId);
@@ -49,7 +51,7 @@ Mousetrap.bind(['down', 'up'], (evt, key) => {
 });
 
 // XXX This shortcut should also work when hovering over a card in board view
-Mousetrap.bind('space', (evt) => {
+Mousetrap.bind('space', evt => {
   if (!Session.get('currentCard')) {
     return;
   }
@@ -69,29 +71,38 @@ Mousetrap.bind('space', (evt) => {
 });
 
 Template.keyboardShortcuts.helpers({
-  mapping: [{
-    keys: ['W'],
-    action: 'shortcut-toggle-sidebar',
-  }, {
-    keys: ['Q'],
-    action: 'shortcut-filter-my-cards',
-  }, {
-    keys: ['F'],
-    action: 'shortcut-toggle-filterbar',
-  }, {
-    keys: ['X'],
-    action: 'shortcut-clear-filters',
-  }, {
-    keys: ['?'],
-    action: 'shortcut-show-shortcuts',
-  }, {
-    keys: ['ESC'],
-    action: 'shortcut-close-dialog',
-  }, {
-    keys: ['@'],
-    action: 'shortcut-autocomplete-members',
-  }, {
-    keys: ['SPACE'],
-    action: 'shortcut-assign-self',
-  }],
+  mapping: [
+    {
+      keys: ['W'],
+      action: 'shortcut-toggle-sidebar',
+    },
+    {
+      keys: ['Q'],
+      action: 'shortcut-filter-my-cards',
+    },
+    {
+      keys: ['F'],
+      action: 'shortcut-toggle-filterbar',
+    },
+    {
+      keys: ['X'],
+      action: 'shortcut-clear-filters',
+    },
+    {
+      keys: ['?'],
+      action: 'shortcut-show-shortcuts',
+    },
+    {
+      keys: ['ESC'],
+      action: 'shortcut-close-dialog',
+    },
+    {
+      keys: ['@'],
+      action: 'shortcut-autocomplete-members',
+    },
+    {
+      keys: ['SPACE'],
+      action: 'shortcut-assign-self',
+    },
+  ],
 });

+ 8 - 8
client/lib/modal.js

@@ -1,6 +1,6 @@
 const closedValue = null;
 
-window.Modal = new class {
+window.Modal = new (class {
   constructor() {
     this._currentModal = new ReactiveVar(closedValue);
     this._onCloseGoTo = '';
@@ -21,7 +21,7 @@ window.Modal = new class {
     return this.getTemplateName() !== closedValue;
   }
 
-  isWide(){
+  isWide() {
     return this._isWideModal;
   }
 
@@ -32,23 +32,23 @@ window.Modal = new class {
     }
   }
 
-  openWide(modalName, { header = '', onCloseGoTo = ''} = {}) {
+  openWide(modalName, { header = '', onCloseGoTo = '' } = {}) {
     this._currentModal.set({ header, modalName });
     this._onCloseGoTo = onCloseGoTo;
     this._isWideModal = true;
   }
 
-  open(modalName, { header = '', onCloseGoTo = ''} = {}) {
+  open(modalName, { header = '', onCloseGoTo = '' } = {}) {
     this._currentModal.set({ header, modalName });
     this._onCloseGoTo = onCloseGoTo;
-
   }
-}();
+})();
 
 Blaze.registerHelper('Modal', Modal);
 
-EscapeActions.register('modalWindow',
+EscapeActions.register(
+  'modalWindow',
   () => Modal.close(),
   () => Modal.isOpen(),
-  { noClickEscapeOn: '.modal-container' }
+  { noClickEscapeOn: '.modal-container' },
 );

+ 27 - 21
client/lib/multiSelection.js

@@ -1,6 +1,4 @@
-
 function getCardsBetween(idA, idB) {
-
   function pluckId(doc) {
     return doc._id;
   }
@@ -15,7 +13,7 @@ function getCardsBetween(idA, idB) {
     }).map(pluckId);
   }
 
-  const cards = _.sortBy([Cards.findOne(idA), Cards.findOne(idB)], (c) => {
+  const cards = _.sortBy([Cards.findOne(idA), Cards.findOne(idB)], c => {
     return c.sort;
   });
 
@@ -31,17 +29,21 @@ function getCardsBetween(idA, idB) {
     };
   } else {
     selector = {
-      $or: [{
-        listId: cards[0].listId,
-        sort: { $lte: cards[0].sort },
-      }, {
-        listId: {
-          $in: getListsStrictlyBetween(cards[0].listId, cards[1].listId),
+      $or: [
+        {
+          listId: cards[0].listId,
+          sort: { $lte: cards[0].sort },
+        },
+        {
+          listId: {
+            $in: getListsStrictlyBetween(cards[0].listId, cards[1].listId),
+          },
         },
-      }, {
-        listId: cards[1].listId,
-        sort: { $gte: cards[1].sort },
-      }],
+        {
+          listId: cards[1].listId,
+          sort: { $gte: cards[1].sort },
+        },
+      ],
       archived: false,
     };
   }
@@ -133,14 +135,12 @@ MultiSelection = {
 
     const selectedCards = this._selectedCards.get();
 
-    cardIds.forEach((cardId) => {
+    cardIds.forEach(cardId => {
       const indexOfCard = selectedCards.indexOf(cardId);
 
       if (options.remove && indexOfCard > -1)
         selectedCards.splice(indexOfCard, 1);
-
-      else if (options.add)
-        selectedCards.push(cardId);
+      else if (options.add) selectedCards.push(cardId);
     });
 
     this._selectedCards.set(selectedCards);
@@ -153,9 +153,15 @@ MultiSelection = {
 
 Blaze.registerHelper('MultiSelection', MultiSelection);
 
-EscapeActions.register('multiselection',
-  () => { MultiSelection.disable(); },
-  () => { return MultiSelection.isActive(); }, {
+EscapeActions.register(
+  'multiselection',
+  () => {
+    MultiSelection.disable();
+  },
+  () => {
+    return MultiSelection.isActive();
+  },
+  {
     noClickEscapeOn: '.js-minicard,.js-board-sidebar-content',
-  }
+  },
 );

+ 11 - 5
client/lib/pasteImage.js

@@ -11,7 +11,10 @@
   $.event.fix = (function(originalFix) {
     return function(event) {
       event = originalFix.apply(this, arguments);
-      if (event.type.indexOf('copy') === 0 || event.type.indexOf('paste') === 0) {
+      if (
+        event.type.indexOf('copy') === 0 ||
+        event.type.indexOf('paste') === 0
+      ) {
         event.clipboardData = event.originalEvent.clipboardData;
       }
       return event;
@@ -23,7 +26,7 @@
     matchType: /image.*/,
   };
 
-  return $.fn.pasteImageReader = function(options) {
+  return ($.fn.pasteImageReader = function(options) {
     if (typeof options === 'function') {
       options = {
         callback: options,
@@ -35,8 +38,11 @@
       return $(element).bind('paste', function(event) {
         const types = event.clipboardData.types;
         const items = event.clipboardData.items;
-        for(let i=0; i<types.length; i++) {
-          if(types[i].match(options.matchType) || items[i].type.match(options.matchType)) {
+        for (let i = 0; i < types.length; i++) {
+          if (
+            types[i].match(options.matchType) ||
+            items[i].type.match(options.matchType)
+          ) {
             const f = items[i].getAsFile();
             const reader = new FileReader();
             reader.onload = function(evt) {
@@ -53,5 +59,5 @@
         }
       });
     });
-  };
+  });
 })(jQuery);

+ 17 - 14
client/lib/popup.js

@@ -1,4 +1,4 @@
-window.Popup = new class {
+window.Popup = new (class {
   constructor() {
     // The template we use to render popups
     this.template = Template.popup;
@@ -67,7 +67,7 @@ window.Popup = new class {
         title: self._getTitle(popupName),
         depth: self._stack.length,
         offset: self._getOffset(openerElement),
-        dataContext: this.currentData && this.currentData() || this,
+        dataContext: (this.currentData && this.currentData()) || this,
       });
 
       // If there are no popup currently opened we use the Blaze API to render
@@ -80,11 +80,14 @@ window.Popup = new class {
       // our internal dependency, and since we just changed the top element of
       // our internal stack, the popup will be updated with the new data.
       if (!self.isOpen()) {
-        self.current = Blaze.renderWithData(self.template, () => {
-          self._dep.depend();
-          return { ...self._getTopStack(), stack: self._stack };
-        }, document.body);
-
+        self.current = Blaze.renderWithData(
+          self.template,
+          () => {
+            self._dep.depend();
+            return { ...self._getTopStack(), stack: self._stack };
+          },
+          document.body,
+        );
       } else {
         self._dep.changed();
       }
@@ -101,7 +104,7 @@ window.Popup = new class {
     const self = this;
 
     return function(evt, tpl) {
-      const context = this.currentData && this.currentData() || this;
+      const context = (this.currentData && this.currentData()) || this;
       context.__afterConfirmAction = action;
       self.open(name).call(context, evt, tpl);
     };
@@ -136,7 +139,6 @@ window.Popup = new class {
       const openerElement = this._getTopStack().openerElement;
       $(openerElement).removeClass('is-active');
 
-
       this._stack = [];
     }
   }
@@ -159,7 +161,7 @@ window.Popup = new class {
     return () => {
       Utils.windowResizeDep.depend();
 
-      if(Utils.isMiniScreen()) return { left:0, top:0 };
+      if (Utils.isMiniScreen()) return { left: 0, top: 0 };
 
       const offset = $element.offset();
       const popupWidth = 300 + 15;
@@ -188,18 +190,19 @@ window.Popup = new class {
       return title !== translationKey ? title : defaultTitle;
     };
   }
-}();
+})();
 
 // We close a potential opened popup on any left click on the document, or go
 // one step back by pressing escape.
 const escapeActions = ['back', 'close'];
-escapeActions.forEach((actionName) => {
-  EscapeActions.register(`popup-${actionName}`,
+escapeActions.forEach(actionName => {
+  EscapeActions.register(
+    `popup-${actionName}`,
     () => Popup[actionName](),
     () => Popup.isOpen(),
     {
       noClickEscapeOn: '.js-pop-over,.js-open-card-title-popup',
       enabledOnClick: actionName === 'close',
-    }
+    },
   );
 });

+ 3 - 6
client/lib/textComplete.js

@@ -47,9 +47,6 @@ $.fn.escapeableTextComplete = function(strategies, options, ...otherArgs) {
   });
 };
 
-EscapeActions.register('textcomplete',
-  () => {},
-  () => dropdownMenuIsOpened, {
-    noClickEscapeOn: '.textcomplete-dropdown',
-  }
-);
+EscapeActions.register('textcomplete', () => {}, () => dropdownMenuIsOpened, {
+  noClickEscapeOn: '.textcomplete-dropdown',
+});

+ 3 - 3
client/lib/unsavedEdits.js

@@ -34,13 +34,13 @@ UnsavedEdits = {
   },
 
   has({ fieldName, docId }) {
-    return Boolean(this.get({fieldName, docId}));
+    return Boolean(this.get({ fieldName, docId }));
   },
 
   set({ fieldName, docId }, value) {
     const currentDoc = this._getCollectionDocument(fieldName, docId);
     if (currentDoc) {
-      UnsavedEditCollection.update(currentDoc._id, { $set: { value }});
+      UnsavedEditCollection.update(currentDoc._id, { $set: { value } });
     } else {
       UnsavedEditCollection.insert({
         fieldName,
@@ -58,7 +58,7 @@ UnsavedEdits = {
   },
 
   _getCollectionDocument(fieldName, docId) {
-    return UnsavedEditCollection.findOne({fieldName, docId});
+    return UnsavedEditCollection.findOne({ fieldName, docId });
   },
 };
 

+ 51 - 27
client/lib/utils.js

@@ -2,20 +2,26 @@ Utils = {
   // XXX We should remove these two methods
   goBoardId(_id) {
     const board = Boards.findOne(_id);
-    return board && FlowRouter.go('board', {
-      id: board._id,
-      slug: board.slug,
-    });
+    return (
+      board &&
+      FlowRouter.go('board', {
+        id: board._id,
+        slug: board.slug,
+      })
+    );
   },
 
   goCardId(_id) {
     const card = Cards.findOne(_id);
     const board = Boards.findOne(card.boardId);
-    return board && FlowRouter.go('card', {
-      cardId: card._id,
-      boardId: board._id,
-      slug: board.slug,
-    });
+    return (
+      board &&
+      FlowRouter.go('card', {
+        cardId: card._id,
+        boardId: board._id,
+        slug: board.slug,
+      })
+    );
   },
 
   capitalize(string) {
@@ -104,13 +110,21 @@ Utils = {
         return window.matchMedia(query).matches;
       };
 
-      if (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch) {
+      if (
+        'ontouchstart' in window ||
+        (window.DocumentTouch && document instanceof window.DocumentTouch)
+      ) {
         return true;
       }
 
       // include the 'heartz' as a way to have a non matching MQ to help terminate the join
       // https://git.io/vznFH
-      const query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');
+      const query = [
+        '(',
+        prefixes.join('touch-enabled),('),
+        'heartz',
+        ')',
+      ].join('');
       return mq(query);
     })();
     Utils.isTouchDevice = () => isTouchable;
@@ -120,7 +134,7 @@ Utils = {
   calculateTouchDistance(touchA, touchB) {
     return Math.sqrt(
       Math.pow(touchA.screenX - touchB.screenX, 2) +
-      Math.pow(touchA.screenY - touchB.screenY, 2)
+        Math.pow(touchA.screenY - touchB.screenY, 2),
     );
   },
 
@@ -136,7 +150,11 @@ Utils = {
       lastTouch = touches[touches.length - 1];
     });
     $(document).on('touchend', selector, function(e) {
-      if (touchStart && lastTouch && Utils.calculateTouchDistance(touchStart, lastTouch) <= 20) {
+      if (
+        touchStart &&
+        lastTouch &&
+        Utils.calculateTouchDistance(touchStart, lastTouch) <= 20
+      ) {
         e.preventDefault();
         const clickEvent = document.createEvent('MouseEvents');
         clickEvent.initEvent('click', true, true);
@@ -145,30 +163,30 @@ Utils = {
     });
   },
 
-  manageCustomUI(){
+  manageCustomUI() {
     Meteor.call('getCustomUI', (err, data) => {
-      if (err && err.error[0] === 'var-not-exist'){
+      if (err && err.error[0] === 'var-not-exist') {
         Session.set('customUI', false); // siteId || address server not defined
       }
-      if (!err){
+      if (!err) {
         Utils.setCustomUI(data);
       }
     });
   },
 
-  setCustomUI(data){
+  setCustomUI(data) {
     const currentBoard = Boards.findOne(Session.get('currentBoard'));
     if (currentBoard) {
-      DocHead.setTitle(`${currentBoard.title  } - ${  data.productName}`);
+      DocHead.setTitle(`${currentBoard.title} - ${data.productName}`);
     } else {
       DocHead.setTitle(`${data.productName}`);
     }
   },
 
-  setMatomo(data){
+  setMatomo(data) {
     window._paq = window._paq || [];
     window._paq.push(['setDoNotTrack', data.doNotTrack]);
-    if (data.withUserName){
+    if (data.withUserName) {
       window._paq.push(['setUserId', Meteor.user().username]);
     }
     window._paq.push(['trackPageView']);
@@ -196,12 +214,12 @@ Utils = {
 
   manageMatomo() {
     const matomo = Session.get('matomo');
-    if (matomo === undefined){
+    if (matomo === undefined) {
       Meteor.call('getMatomoConf', (err, data) => {
-        if (err && err.error[0] === 'var-not-exist'){
+        if (err && err.error[0] === 'var-not-exist') {
           Session.set('matomo', false); // siteId || address server not defined
         }
-        if (!err){
+        if (!err) {
           Utils.setMatomo(data);
         }
       });
@@ -220,15 +238,21 @@ Utils = {
         finalString += element.text().toLowerCase();
       } else if (element.hasClass('user-details')) {
         let username = element.find('input').val();
-        if(username === undefined || username === ''){
+        if (username === undefined || username === '') {
           username = '*';
         }
-        finalString += `${element.find('.trigger-text').text().toLowerCase() } ${  username}`;
+        finalString += `${element
+          .find('.trigger-text')
+          .text()
+          .toLowerCase()} ${username}`;
       } else if (element.find('select').length > 0) {
-        finalString += element.find('select option:selected').text().toLowerCase();
+        finalString += element
+          .find('select option:selected')
+          .text()
+          .toLowerCase();
       } else if (element.find('input').length > 0) {
         let inputvalue = element.find('input').val();
-        if(inputvalue === undefined || inputvalue === ''){
+        if (inputvalue === undefined || inputvalue === '') {
           inputvalue = '*';
         }
         finalString += inputvalue;

+ 38 - 22
config/accounts.js

@@ -1,20 +1,25 @@
 const passwordField = AccountsTemplates.removeField('password');
 const emailField = AccountsTemplates.removeField('email');
 
-AccountsTemplates.addFields([{
-  _id: 'username',
-  type: 'text',
-  displayName: 'username',
-  required: true,
-  minLength: 2,
-}, emailField, passwordField, {
-  _id: 'invitationcode',
-  type: 'text',
-  displayName: 'Invitation Code',
-  required: false,
-  minLength: 6,
-  template: 'invitationCode',
-}]);
+AccountsTemplates.addFields([
+  {
+    _id: 'username',
+    type: 'text',
+    displayName: 'username',
+    required: true,
+    minLength: 2,
+  },
+  emailField,
+  passwordField,
+  {
+    _id: 'invitationcode',
+    type: 'text',
+    displayName: 'Invitation Code',
+    required: false,
+    minLength: 6,
+    template: 'invitationCode',
+  },
+]);
 
 AccountsTemplates.configure({
   defaultLayout: 'userFormsLayout',
@@ -34,7 +39,8 @@ AccountsTemplates.configure({
 });
 
 ['signIn', 'signUp', 'resetPwd', 'forgotPwd', 'enrollAccount'].forEach(
-  (routeName) => AccountsTemplates.configureRoute(routeName));
+  routeName => AccountsTemplates.configureRoute(routeName),
+);
 
 // We display the form to change the password in a popup window that already
 // have a title, so we unset the title automatically displayed by useraccounts.
@@ -56,15 +62,25 @@ AccountsTemplates.configureRoute('changePwd', {
 });
 
 if (Meteor.isServer) {
-
-  ['resetPassword-subject', 'resetPassword-text', 'verifyEmail-subject', 'verifyEmail-text', 'enrollAccount-subject', 'enrollAccount-text'].forEach((str) => {
+  [
+    'resetPassword-subject',
+    'resetPassword-text',
+    'verifyEmail-subject',
+    'verifyEmail-text',
+    'enrollAccount-subject',
+    'enrollAccount-text',
+  ].forEach(str => {
     const [templateName, field] = str.split('-');
     Accounts.emailTemplates[templateName][field] = (user, url) => {
-      return TAPi18n.__(`email-${str}`, {
-        url,
-        user: user.getName(),
-        siteName: Accounts.emailTemplates.siteName,
-      }, user.getLanguage());
+      return TAPi18n.__(
+        `email-${str}`,
+        {
+          url,
+          user: user.getName(),
+          siteName: Accounts.emailTemplates.siteName,
+        },
+        user.getLanguage(),
+      );
     };
   });
 }

+ 10 - 6
config/router.js

@@ -1,7 +1,9 @@
 let previousPath;
-FlowRouter.triggers.exit([({path}) => {
-  previousPath = path;
-}]);
+FlowRouter.triggers.exit([
+  ({ path }) => {
+    previousPath = path;
+  },
+]);
 
 FlowRouter.route('/', {
   name: 'home',
@@ -192,9 +194,11 @@ const redirections = {
 
 _.each(redirections, (newPath, oldPath) => {
   FlowRouter.route(oldPath, {
-    triggersEnter: [(context, redirect) => {
-      redirect(FlowRouter.path(newPath, context.params));
-    }],
+    triggersEnter: [
+      (context, redirect) => {
+        redirect(FlowRouter.path(newPath, context.params));
+      },
+    ],
   });
 });
 

+ 3 - 8
models/accountSettings.js

@@ -37,7 +37,7 @@ AccountSettings.attachSchema(
         }
       },
     },
-  })
+  }),
 );
 
 AccountSettings.allow({
@@ -47,11 +47,6 @@ AccountSettings.allow({
   },
 });
 
-AccountSettings.before.update((userId, doc, fieldNames, modifier, options) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = Date.now();
-});
-
 if (Meteor.isServer) {
   Meteor.startup(() => {
     AccountSettings._collection._ensureIndex({ modifiedAt: -1 });
@@ -62,7 +57,7 @@ if (Meteor.isServer) {
           booleanValue: false,
           sort: 0,
         },
-      }
+      },
     );
     AccountSettings.upsert(
       { _id: 'accounts-allowUserNameChange' },
@@ -71,7 +66,7 @@ if (Meteor.isServer) {
           booleanValue: false,
           sort: 1,
         },
-      }
+      },
     );
   });
 }

+ 0 - 5
models/actions.js

@@ -20,11 +20,6 @@ Actions.helpers({
   },
 });
 
-Actions.before.update((userId, doc, fieldNames, modifier, options) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = Date.now();
-});
-
 if (Meteor.isServer) {
   Meteor.startup(() => {
     Actions._collection._ensureIndex({ modifiedAt: -1 });

+ 7 - 12
models/activities.js

@@ -71,11 +71,6 @@ Activities.after.insert((userId, doc) => {
   RulesHelper.executeRules(activity);
 });
 
-Activities.before.update((userId, doc, fieldNames, modifier, options) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = Date.now();
-});
-
 if (Meteor.isServer) {
   // For efficiency create indexes on the date of creation, and on the date of
   // creation in conjunction with the card or board id, as corresponding views
@@ -87,15 +82,15 @@ if (Meteor.isServer) {
     Activities._collection._ensureIndex({ boardId: 1, createdAt: -1 });
     Activities._collection._ensureIndex(
       { commentId: 1 },
-      { partialFilterExpression: { commentId: { $exists: true } } }
+      { partialFilterExpression: { commentId: { $exists: true } } },
     );
     Activities._collection._ensureIndex(
       { attachmentId: 1 },
-      { partialFilterExpression: { attachmentId: { $exists: true } } }
+      { partialFilterExpression: { attachmentId: { $exists: true } } },
     );
     Activities._collection._ensureIndex(
       { customFieldId: 1 },
-      { partialFilterExpression: { customFieldId: { $exists: true } } }
+      { partialFilterExpression: { customFieldId: { $exists: true } } },
     );
     // Label activity did not work yet, unable to edit labels when tried this.
     //Activities._collection._dropIndex({ labelId: 1 }, { "indexKey": -1 });
@@ -205,20 +200,20 @@ if (Meteor.isServer) {
     if (board) {
       const watchingUsers = _.pluck(
         _.where(board.watchers, { level: 'watching' }),
-        'userId'
+        'userId',
       );
       const trackingUsers = _.pluck(
         _.where(board.watchers, { level: 'tracking' }),
-        'userId'
+        'userId',
       );
       watchers = _.union(
         watchers,
         watchingUsers,
-        _.intersection(participants, trackingUsers)
+        _.intersection(participants, trackingUsers),
       );
     }
 
-    Notifications.getUsers(watchers).forEach((user) => {
+    Notifications.getUsers(watchers).forEach(user => {
       Notifications.notify(user, title, description, params);
     });
 

+ 1 - 6
models/announcements.js

@@ -42,7 +42,7 @@ Announcements.attachSchema(
         }
       },
     },
-  })
+  }),
 );
 
 Announcements.allow({
@@ -52,11 +52,6 @@ Announcements.allow({
   },
 });
 
-Announcements.before.update((userId, doc, fieldNames, modifier, options) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = Date.now();
-});
-
 if (Meteor.isServer) {
   Meteor.startup(() => {
     Announcements._collection._ensureIndex({ modifiedAt: -1 });

+ 2 - 2
models/attachments.js

@@ -12,7 +12,7 @@ Attachments = new FS.Collection('attachments', {
       // XXX Should we use `beforeWrite` option of CollectionFS instead of
       // collection-hooks?
       // We should use `beforeWrite`.
-      beforeWrite: (fileObj) => {
+      beforeWrite: fileObj => {
         if (!fileObj.isImage()) {
           return {
             type: 'application/octet-stream',
@@ -84,7 +84,7 @@ if (Meteor.isServer) {
           $unset: {
             source: '',
           },
-        }
+        },
       );
     }
   });

+ 25 - 30
models/boards.js

@@ -101,7 +101,7 @@ Boards.attachSchema(
           const colors = Boards.simpleSchema()._schema['labels.$.color']
             .allowedValues;
           const defaultLabelsColors = _.clone(colors).splice(0, 6);
-          return defaultLabelsColors.map((color) => ({
+          return defaultLabelsColors.map(color => ({
             color,
             _id: Random.id(6),
             name: '',
@@ -342,7 +342,7 @@ Boards.attachSchema(
       type: String,
       defaultValue: 'board',
     },
-  })
+  }),
 );
 
 Boards.helpers({
@@ -355,7 +355,7 @@ Boards.helpers({
     Swimlanes.find({
       boardId: oldId,
       archived: false,
-    }).forEach((swimlane) => {
+    }).forEach(swimlane => {
       swimlane.type = 'swimlane';
       swimlane.copy(_id);
     });
@@ -382,7 +382,7 @@ Boards.helpers({
   isActiveMember(userId) {
     if (userId) {
       return this.members.find(
-        (member) => member.userId === userId && member.isActive
+        member => member.userId === userId && member.isActive,
       );
     } else {
       return false;
@@ -396,14 +396,14 @@ Boards.helpers({
   cards() {
     return Cards.find(
       { boardId: this._id, archived: false },
-      { sort: { title: 1 } }
+      { sort: { title: 1 } },
     );
   },
 
   lists() {
     return Lists.find(
       { boardId: this._id, archived: false },
-      { sort: { sort: 1 } }
+      { sort: { sort: 1 } },
     );
   },
 
@@ -418,7 +418,7 @@ Boards.helpers({
   swimlanes() {
     return Swimlanes.find(
       { boardId: this._id, archived: false },
-      { sort: { sort: 1 } }
+      { sort: { sort: 1 } },
     );
   },
 
@@ -432,7 +432,7 @@ Boards.helpers({
       },
       {
         sort: { sort: 1 },
-      }
+      },
     );
   },
 
@@ -535,7 +535,7 @@ Boards.helpers({
   customFields() {
     return CustomFields.find(
       { boardIds: { $in: [this._id] } },
-      { sort: { name: 1 } }
+      { sort: { name: 1 } },
     );
   },
 
@@ -848,7 +848,7 @@ Boards.mutations({
     isAdmin,
     isNoComments,
     isCommentOnly,
-    currentUserId = Meteor.userId()
+    currentUserId = Meteor.userId(),
   ) {
     const memberIndex = this.memberIndex(memberId);
     // do not allow change permission of self
@@ -884,9 +884,9 @@ Boards.mutations({
 
 function boardRemover(userId, doc) {
   [Cards, Lists, Swimlanes, Integrations, Rules, Activities].forEach(
-    (element) => {
+    element => {
       element.remove({ boardId: doc._id });
-    }
+    },
   );
 }
 
@@ -927,7 +927,7 @@ if (Meteor.isServer) {
         _.findWhere(doc.members, {
           userId: removedMemberId,
           isAdmin: true,
-        })
+        }),
       );
     },
     fetch: ['members'],
@@ -973,7 +973,7 @@ if (Meteor.isServer) {
         _id: 1,
         'members.userId': 1,
       },
-      { unique: true }
+      { unique: true },
     );
     Boards._collection._ensureIndex({ 'members.userId': 1 });
   });
@@ -1009,12 +1009,12 @@ if (Meteor.isServer) {
           labelIds: removedLabelId,
         },
       },
-      { multi: true }
+      { multi: true },
     );
   });
 
   const foreachRemovedMember = (doc, modifier, callback) => {
-    Object.keys(modifier).forEach((set) => {
+    Object.keys(modifier).forEach(set => {
       if (modifier[set] !== false) {
         return;
       }
@@ -1030,11 +1030,6 @@ if (Meteor.isServer) {
     });
   };
 
-  Boards.before.update((userId, doc, fieldNames, modifier, options) => {
-    modifier.$set = modifier.$set || {};
-    modifier.$set.modifiedAt = Date.now();
-  });
-
   // Remove a member from all objects of the board before leaving the board
   Boards.before.update((userId, doc, fieldNames, modifier) => {
     if (!_.contains(fieldNames, 'members')) {
@@ -1043,7 +1038,7 @@ if (Meteor.isServer) {
 
     if (modifier.$set) {
       const boardId = doc._id;
-      foreachRemovedMember(doc, modifier.$set, (memberId) => {
+      foreachRemovedMember(doc, modifier.$set, memberId => {
         Cards.update(
           { boardId },
           {
@@ -1052,7 +1047,7 @@ if (Meteor.isServer) {
               watchers: memberId,
             },
           },
-          { multi: true }
+          { multi: true },
         );
 
         Lists.update(
@@ -1062,7 +1057,7 @@ if (Meteor.isServer) {
               watchers: memberId,
             },
           },
-          { multi: true }
+          { multi: true },
         );
 
         const board = Boards._transform(doc);
@@ -1112,7 +1107,7 @@ if (Meteor.isServer) {
 
     // Say goodbye to the former member
     if (modifier.$set) {
-      foreachRemovedMember(doc, modifier.$set, (memberId) => {
+      foreachRemovedMember(doc, modifier.$set, memberId => {
         Activities.insert({
           userId,
           memberId,
@@ -1143,7 +1138,7 @@ if (Meteor.isServer) {
       // admins can access boards of any user
       Authentication.checkAdminOrCondition(
         req.userId,
-        req.userId === paramUserId
+        req.userId === paramUserId,
       );
 
       const data = Boards.find(
@@ -1153,7 +1148,7 @@ if (Meteor.isServer) {
         },
         {
           sort: ['title'],
-        }
+        },
       ).map(function(board) {
         return {
           _id: board._id,
@@ -1332,7 +1327,7 @@ if (Meteor.isServer) {
         if (!board.getLabel(name, color)) {
           Boards.direct.update(
             { _id: id },
-            { $push: { labels: { _id: labelId, name, color } } }
+            { $push: { labels: { _id: labelId, name, color } } },
           );
           JsonRoutes.sendResult(res, {
             code: 200,
@@ -1364,7 +1359,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('POST', '/api/boards/:boardId/members/:memberId', function(
     req,
-    res
+    res,
   ) {
     try {
       const boardId = req.params.boardId;
@@ -1384,7 +1379,7 @@ if (Meteor.isServer) {
         isTrue(isAdmin),
         isTrue(isNoComments),
         isTrue(isCommentOnly),
-        req.userId
+        req.userId,
       );
 
       JsonRoutes.sendResult(res, {

+ 5 - 12
models/cardComments.js

@@ -64,7 +64,7 @@ CardComments.attachSchema(
         }
       },
     },
-  })
+  }),
 );
 
 CardComments.allow({
@@ -107,11 +107,6 @@ function commentCreation(userId, doc) {
   });
 }
 
-CardComments.before.update((userId, doc, fieldNames, modifier, options) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = Date.now();
-});
-
 if (Meteor.isServer) {
   // Comments are often fetched within a card, so we create an index to make these
   // queries more efficient.
@@ -125,7 +120,6 @@ if (Meteor.isServer) {
   });
 
   CardComments.after.update((userId, doc) => {
-    const activity = Activities.findOne({ commentId: doc._id });
     const card = Cards.findOne(doc.cardId);
     Activities.insert({
       userId,
@@ -139,7 +133,6 @@ if (Meteor.isServer) {
   });
 
   CardComments.before.remove((userId, doc) => {
-    const activity = Activities.findOne({ commentId: doc._id });
     const card = Cards.findOne(doc.cardId);
     Activities.insert({
       userId,
@@ -174,7 +167,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/comments', function(
     req,
-    res
+    res,
   ) {
     try {
       Authentication.checkUserId(req.userId);
@@ -233,7 +226,7 @@ if (Meteor.isServer) {
           data: error,
         });
       }
-    }
+    },
   );
 
   /**
@@ -280,7 +273,7 @@ if (Meteor.isServer) {
           data: error,
         });
       }
-    }
+    },
   );
 
   /**
@@ -318,7 +311,7 @@ if (Meteor.isServer) {
           data: error,
         });
       }
-    }
+    },
   );
 }
 

File diff suppressed because it is too large
+ 417 - 361
models/cards.js


+ 6 - 11
models/checklistItems.js

@@ -61,7 +61,7 @@ ChecklistItems.attachSchema(
         }
       },
     },
-  })
+  }),
 );
 
 ChecklistItems.allow({
@@ -225,11 +225,6 @@ if (Meteor.isServer) {
     publishChekListUncompleted(userId, doc, fieldNames);
   });
 
-  ChecklistItems.before.update((userId, doc, fieldNames, modifier, options) => {
-    modifier.$set = modifier.$set || {};
-    modifier.$set.modifiedAt = Date.now();
-  });
-
   ChecklistItems.after.insert((userId, doc) => {
     itemCreation(userId, doc);
   });
@@ -281,7 +276,7 @@ if (Meteor.isServer) {
           code: 500,
         });
       }
-    }
+    },
   );
 
   /**
@@ -308,13 +303,13 @@ if (Meteor.isServer) {
       if (req.body.hasOwnProperty('isFinished')) {
         ChecklistItems.direct.update(
           { _id: paramItemId },
-          { $set: { isFinished: req.body.isFinished } }
+          { $set: { isFinished: req.body.isFinished } },
         );
       }
       if (req.body.hasOwnProperty('title')) {
         ChecklistItems.direct.update(
           { _id: paramItemId },
-          { $set: { title: req.body.title } }
+          { $set: { title: req.body.title } },
         );
       }
 
@@ -324,7 +319,7 @@ if (Meteor.isServer) {
           _id: paramItemId,
         },
       });
-    }
+    },
   );
 
   /**
@@ -353,7 +348,7 @@ if (Meteor.isServer) {
           _id: paramItemId,
         },
       });
-    }
+    },
   );
 }
 

+ 9 - 14
models/checklists.js

@@ -59,7 +59,7 @@ Checklists.attachSchema(
       type: Number,
       decimal: true,
     },
-  })
+  }),
 );
 
 Checklists.helpers({
@@ -68,7 +68,7 @@ Checklists.helpers({
     this._id = null;
     this.cardId = newCardId;
     const newChecklistId = Checklists.insert(this);
-    ChecklistItems.find({ checklistId: oldChecklistId }).forEach((item) => {
+    ChecklistItems.find({ checklistId: oldChecklistId }).forEach(item => {
       item._id = null;
       item.checklistId = newChecklistId;
       item.cardId = newCardId;
@@ -84,7 +84,7 @@ Checklists.helpers({
       {
         checklistId: this._id,
       },
-      { sort: ['sort'] }
+      { sort: ['sort'] },
     );
   },
   finishedCount() {
@@ -160,16 +160,11 @@ if (Meteor.isServer) {
     });
   });
 
-  Checklists.before.update((userId, doc, fieldNames, modifier, options) => {
-    modifier.$set = modifier.$set || {};
-    modifier.$set.modifiedAt = Date.now();
-  });
-
   Checklists.before.remove((userId, doc) => {
     const activities = Activities.find({ checklistId: doc._id });
     const card = Cards.findOne(doc.cardId);
     if (activities) {
-      activities.forEach((activity) => {
+      activities.forEach(activity => {
         Activities.remove(activity._id);
       });
     }
@@ -203,7 +198,7 @@ if (Meteor.isServer) {
       Authentication.checkUserId(req.userId);
       const paramCardId = req.params.cardId;
       const checklists = Checklists.find({ cardId: paramCardId }).map(function(
-        doc
+        doc,
       ) {
         return {
           _id: doc._id,
@@ -220,7 +215,7 @@ if (Meteor.isServer) {
           code: 500,
         });
       }
-    }
+    },
   );
 
   /**
@@ -269,7 +264,7 @@ if (Meteor.isServer) {
           code: 500,
         });
       }
-    }
+    },
   );
 
   /**
@@ -313,7 +308,7 @@ if (Meteor.isServer) {
           code: 400,
         });
       }
-    }
+    },
   );
 
   /**
@@ -340,7 +335,7 @@ if (Meteor.isServer) {
           _id: paramChecklistId,
         },
       });
-    }
+    },
   );
 }
 

+ 13 - 18
models/customFields.js

@@ -95,7 +95,7 @@ CustomFields.attachSchema(
         }
       },
     },
-  })
+  }),
 );
 
 CustomFields.mutations({
@@ -118,7 +118,7 @@ CustomFields.allow({
       userId,
       Boards.find({
         _id: { $in: doc.boardIds },
-      }).fetch()
+      }).fetch(),
     );
   },
   update(userId, doc) {
@@ -126,7 +126,7 @@ CustomFields.allow({
       userId,
       Boards.find({
         _id: { $in: doc.boardIds },
-      }).fetch()
+      }).fetch(),
     );
   },
   remove(userId, doc) {
@@ -134,7 +134,7 @@ CustomFields.allow({
       userId,
       Boards.find({
         _id: { $in: doc.boardIds },
-      }).fetch()
+      }).fetch(),
     );
   },
   fetch: ['userId', 'boardIds'],
@@ -152,7 +152,7 @@ function customFieldCreation(userId, doc) {
   });
 }
 
-function customFieldDeletion(userId, doc){
+function customFieldDeletion(userId, doc) {
   Activities.insert({
     userId,
     activityType: 'deleteCustomField',
@@ -163,7 +163,7 @@ function customFieldDeletion(userId, doc){
 
 // This has some bug, it does not show edited customField value at Outgoing Webhook,
 // instead it shows undefined, and no listId and swimlaneId.
-function customFieldEdit(userId, doc){
+function customFieldEdit(userId, doc) {
   const card = Cards.findOne(doc.cardId);
   Activities.insert({
     userId,
@@ -185,17 +185,12 @@ if (Meteor.isServer) {
     customFieldCreation(userId, doc);
   });
 
-  CustomFields.before.update((userId, doc, fieldNames, modifier, options) => {
-    modifier.$set = modifier.$set || {};
-    modifier.$set.modifiedAt = Date.now();
-  });
-
   CustomFields.before.update((userId, doc, fieldNames, modifier) => {
     if (_.contains(fieldNames, 'boardIds') && modifier.$pull) {
       Cards.update(
         { boardId: modifier.$pull.boardIds, 'customFields._id': doc._id },
         { $pull: { customFields: { _id: doc._id } } },
-        { multi: true }
+        { multi: true },
       );
       customFieldEdit(userId, doc);
       Activities.remove({
@@ -223,7 +218,7 @@ if (Meteor.isServer) {
     Cards.update(
       { boardId: { $in: doc.boardIds }, 'customFields._id': doc._id },
       { $pull: { customFields: { _id: doc._id } } },
-      { multi: true }
+      { multi: true },
     );
   });
 }
@@ -241,7 +236,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', function(
     req,
-    res
+    res,
   ) {
     Authentication.checkUserId(req.userId);
     const paramBoardId = req.params.boardId;
@@ -254,7 +249,7 @@ if (Meteor.isServer) {
             name: cf.name,
             type: cf.type,
           };
-        }
+        },
       ),
     });
   });
@@ -281,7 +276,7 @@ if (Meteor.isServer) {
           boardIds: { $in: [paramBoardId] },
         }),
       });
-    }
+    },
   );
 
   /**
@@ -299,7 +294,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function(
     req,
-    res
+    res,
   ) {
     Authentication.checkUserId(req.userId);
     const paramBoardId = req.params.boardId;
@@ -351,7 +346,7 @@ if (Meteor.isServer) {
           _id: id,
         },
       });
-    }
+    },
   );
 }
 

+ 77 - 48
models/export.js

@@ -57,7 +57,10 @@ export class Exporter {
 
   build() {
     const byBoard = { boardId: this._boardId };
-    const byBoardNoLinked = { boardId: this._boardId, linkedId: {$in: ['', null] } };
+    const byBoardNoLinked = {
+      boardId: this._boardId,
+      linkedId: { $in: ['', null] },
+    };
     // we do not want to retrieve boardId in related elements
     const noBoardId = {
       fields: {
@@ -67,15 +70,21 @@ export class Exporter {
     const result = {
       _format: 'wekan-board-1.0.0',
     };
-    _.extend(result, Boards.findOne(this._boardId, {
-      fields: {
-        stars: 0,
-      },
-    }));
+    _.extend(
+      result,
+      Boards.findOne(this._boardId, {
+        fields: {
+          stars: 0,
+        },
+      }),
+    );
     result.lists = Lists.find(byBoard, noBoardId).fetch();
     result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
     result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
-    result.customFields = CustomFields.find({boardIds: {$in: [this.boardId]}}, {fields: {boardId: 0}}).fetch();
+    result.customFields = CustomFields.find(
+      { boardIds: { $in: [this.boardId] } },
+      { fields: { boardId: 0 } },
+    ).fetch();
     result.comments = CardComments.find(byBoard, noBoardId).fetch();
     result.activities = Activities.find(byBoard, noBoardId).fetch();
     result.rules = Rules.find(byBoard, noBoardId).fetch();
@@ -84,24 +93,40 @@ export class Exporter {
     result.subtaskItems = [];
     result.triggers = [];
     result.actions = [];
-    result.cards.forEach((card) => {
-      result.checklists.push(...Checklists.find({
-        cardId: card._id,
-      }).fetch());
-      result.checklistItems.push(...ChecklistItems.find({
-        cardId: card._id,
-      }).fetch());
-      result.subtaskItems.push(...Cards.find({
-        parentid: card._id,
-      }).fetch());
+    result.cards.forEach(card => {
+      result.checklists.push(
+        ...Checklists.find({
+          cardId: card._id,
+        }).fetch(),
+      );
+      result.checklistItems.push(
+        ...ChecklistItems.find({
+          cardId: card._id,
+        }).fetch(),
+      );
+      result.subtaskItems.push(
+        ...Cards.find({
+          parentid: card._id,
+        }).fetch(),
+      );
     });
-    result.rules.forEach((rule) => {
-      result.triggers.push(...Triggers.find({
-        _id: rule.triggerId,
-      }, noBoardId).fetch());
-      result.actions.push(...Actions.find({
-        _id: rule.actionId,
-      }, noBoardId).fetch());
+    result.rules.forEach(rule => {
+      result.triggers.push(
+        ...Triggers.find(
+          {
+            _id: rule.triggerId,
+          },
+          noBoardId,
+        ).fetch(),
+      );
+      result.actions.push(
+        ...Actions.find(
+          {
+            _id: rule.actionId,
+          },
+          noBoardId,
+        ).fetch(),
+      );
     });
 
     // [Old] for attachments we only export IDs and absolute url to original doc
@@ -122,43 +147,45 @@ export class Exporter {
       });
     };
     const getBase64DataSync = Meteor.wrapAsync(getBase64Data);
-    result.attachments = Attachments.find(byBoard).fetch().map((attachment) => {
-      return {
-        _id: attachment._id,
-        cardId: attachment.cardId,
-        // url: FlowRouter.url(attachment.url()),
-        file: getBase64DataSync(attachment),
-        name: attachment.original.name,
-        type: attachment.original.type,
-      };
-    });
+    result.attachments = Attachments.find(byBoard)
+      .fetch()
+      .map(attachment => {
+        return {
+          _id: attachment._id,
+          cardId: attachment.cardId,
+          // url: FlowRouter.url(attachment.url()),
+          file: getBase64DataSync(attachment),
+          name: attachment.original.name,
+          type: attachment.original.type,
+        };
+      });
 
     // we also have to export some user data - as the other elements only
     // include id but we have to be careful:
     // 1- only exports users that are linked somehow to that board
     // 2- do not export any sensitive information
     const users = {};
-    result.members.forEach((member) => {
+    result.members.forEach(member => {
       users[member.userId] = true;
     });
-    result.lists.forEach((list) => {
+    result.lists.forEach(list => {
       users[list.userId] = true;
     });
-    result.cards.forEach((card) => {
+    result.cards.forEach(card => {
       users[card.userId] = true;
       if (card.members) {
-        card.members.forEach((memberId) => {
+        card.members.forEach(memberId => {
           users[memberId] = true;
         });
       }
     });
-    result.comments.forEach((comment) => {
+    result.comments.forEach(comment => {
       users[comment.userId] = true;
     });
-    result.activities.forEach((activity) => {
+    result.activities.forEach(activity => {
       users[activity.userId] = true;
     });
-    result.checklists.forEach((checklist) => {
+    result.checklists.forEach(checklist => {
       users[checklist.userId] = true;
     });
     const byUserIds = {
@@ -177,13 +204,15 @@ export class Exporter {
         'profile.avatarUrl': 1,
       },
     };
-    result.users = Users.find(byUserIds, userFields).fetch().map((user) => {
-      // user avatar is stored as a relative url, we export absolute
-      if ((user.profile || {}).avatarUrl) {
-        user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
-      }
-      return user;
-    });
+    result.users = Users.find(byUserIds, userFields)
+      .fetch()
+      .map(user => {
+        // user avatar is stored as a relative url, we export absolute
+        if ((user.profile || {}).avatarUrl) {
+          user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
+        }
+        return user;
+      });
     return result;
   }
 

+ 8 - 10
models/import.js

@@ -1,6 +1,6 @@
 import { TrelloCreator } from './trelloCreator';
 import { WekanCreator } from './wekanCreator';
-import {Exporter} from './export';
+import { Exporter } from './export';
 import wekanMembersMapper from './wekanmapper';
 
 Meteor.methods({
@@ -11,12 +11,12 @@ Meteor.methods({
     check(currentBoard, Match.Maybe(String));
     let creator;
     switch (importSource) {
-    case 'trello':
-      creator = new TrelloCreator(data);
-      break;
-    case 'wekan':
-      creator = new WekanCreator(data);
-      break;
+      case 'trello':
+        creator = new TrelloCreator(data);
+        break;
+      case 'wekan':
+        creator = new WekanCreator(data);
+        break;
     }
 
     // 1. check all parameters are ok from a syntax point of view
@@ -38,11 +38,9 @@ Meteor.methods({
     const data = exporter.build();
     const addData = {};
     addData.membersMapping = wekanMembersMapper.getMembersToMap(data);
-    const creator =  new WekanCreator(addData);
+    const creator = new WekanCreator(addData);
     //data.title = `${data.title  } - ${  TAPi18n.__('copy-tag')}`;
     data.title = `${data.title}`;
     return creator.create(data, currentBoardId);
   },
 });
-
-

+ 19 - 24
models/integrations.js

@@ -86,14 +86,9 @@ Integrations.attachSchema(
        */
       type: String,
     },
-  })
+  }),
 );
 
-Integrations.before.update((userId, doc, fieldNames, modifier, options) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = Date.now();
-});
-
 Integrations.allow({
   insert(userId, doc) {
     return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
@@ -123,7 +118,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('GET', '/api/boards/:boardId/integrations', function(
     req,
-    res
+    res,
   ) {
     try {
       const paramBoardId = req.params.boardId;
@@ -131,7 +126,7 @@ if (Meteor.isServer) {
 
       const data = Integrations.find(
         { boardId: paramBoardId },
-        { fields: { token: 0 } }
+        { fields: { token: 0 } },
       ).map(function(doc) {
         return doc;
       });
@@ -155,7 +150,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', function(
     req,
-    res
+    res,
   ) {
     try {
       const paramBoardId = req.params.boardId;
@@ -166,7 +161,7 @@ if (Meteor.isServer) {
         code: 200,
         data: Integrations.findOne(
           { _id: paramIntId, boardId: paramBoardId },
-          { fields: { token: 0 } }
+          { fields: { token: 0 } },
         ),
       });
     } catch (error) {
@@ -187,7 +182,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('POST', '/api/boards/:boardId/integrations', function(
     req,
-    res
+    res,
   ) {
     try {
       const paramBoardId = req.params.boardId;
@@ -228,7 +223,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('PUT', '/api/boards/:boardId/integrations/:intId', function(
     req,
-    res
+    res,
   ) {
     try {
       const paramBoardId = req.params.boardId;
@@ -239,35 +234,35 @@ if (Meteor.isServer) {
         const newEnabled = req.body.enabled;
         Integrations.direct.update(
           { _id: paramIntId, boardId: paramBoardId },
-          { $set: { enabled: newEnabled } }
+          { $set: { enabled: newEnabled } },
         );
       }
       if (req.body.hasOwnProperty('title')) {
         const newTitle = req.body.title;
         Integrations.direct.update(
           { _id: paramIntId, boardId: paramBoardId },
-          { $set: { title: newTitle } }
+          { $set: { title: newTitle } },
         );
       }
       if (req.body.hasOwnProperty('url')) {
         const newUrl = req.body.url;
         Integrations.direct.update(
           { _id: paramIntId, boardId: paramBoardId },
-          { $set: { url: newUrl } }
+          { $set: { url: newUrl } },
         );
       }
       if (req.body.hasOwnProperty('token')) {
         const newToken = req.body.token;
         Integrations.direct.update(
           { _id: paramIntId, boardId: paramBoardId },
-          { $set: { token: newToken } }
+          { $set: { token: newToken } },
         );
       }
       if (req.body.hasOwnProperty('activities')) {
         const newActivities = req.body.activities;
         Integrations.direct.update(
           { _id: paramIntId, boardId: paramBoardId },
-          { $set: { activities: newActivities } }
+          { $set: { activities: newActivities } },
         );
       }
 
@@ -306,14 +301,14 @@ if (Meteor.isServer) {
 
         Integrations.direct.update(
           { _id: paramIntId, boardId: paramBoardId },
-          { $pullAll: { activities: newActivities } }
+          { $pullAll: { activities: newActivities } },
         );
 
         JsonRoutes.sendResult(res, {
           code: 200,
           data: Integrations.findOne(
             { _id: paramIntId, boardId: paramBoardId },
-            { fields: { _id: 1, activities: 1 } }
+            { fields: { _id: 1, activities: 1 } },
           ),
         });
       } catch (error) {
@@ -322,7 +317,7 @@ if (Meteor.isServer) {
           data: error,
         });
       }
-    }
+    },
   );
 
   /**
@@ -346,14 +341,14 @@ if (Meteor.isServer) {
 
         Integrations.direct.update(
           { _id: paramIntId, boardId: paramBoardId },
-          { $addToSet: { activities: { $each: newActivities } } }
+          { $addToSet: { activities: { $each: newActivities } } },
         );
 
         JsonRoutes.sendResult(res, {
           code: 200,
           data: Integrations.findOne(
             { _id: paramIntId, boardId: paramBoardId },
-            { fields: { _id: 1, activities: 1 } }
+            { fields: { _id: 1, activities: 1 } },
           ),
         });
       } catch (error) {
@@ -362,7 +357,7 @@ if (Meteor.isServer) {
           data: error,
         });
       }
-    }
+    },
   );
 
   /**
@@ -375,7 +370,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('DELETE', '/api/boards/:boardId/integrations/:intId', function(
     req,
-    res
+    res,
   ) {
     try {
       const paramBoardId = req.params.boardId;

+ 1 - 6
models/invitationCodes.js

@@ -47,7 +47,7 @@ InvitationCodes.attachSchema(
       type: Boolean,
       defaultValue: true,
     },
-  })
+  }),
 );
 
 InvitationCodes.helpers({
@@ -56,11 +56,6 @@ InvitationCodes.helpers({
   },
 });
 
-InvitationCodes.before.update((userId, doc, fieldNames, modifier, options) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = Date.now();
-});
-
 // InvitationCodes.before.insert((userId, doc) => {
 // doc.createdAt = new Date();
 // doc.authorId = userId;

+ 8 - 13
models/lists.js

@@ -156,7 +156,7 @@ Lists.attachSchema(
       type: String,
       defaultValue: 'list',
     },
-  })
+  }),
 );
 
 Lists.allow({
@@ -198,7 +198,7 @@ Lists.helpers({
       swimlaneId: oldSwimlaneId,
       listId: oldId,
       archived: false,
-    }).forEach((card) => {
+    }).forEach(card => {
       card.copy(boardId, swimlaneId, _id);
     });
   },
@@ -262,7 +262,7 @@ Lists.mutations({
 
   archive() {
     if (this.isTemplateList()) {
-      this.cards().forEach((card) => {
+      this.cards().forEach(card => {
         return card.archive();
       });
     }
@@ -271,7 +271,7 @@ Lists.mutations({
 
   restore() {
     if (this.isTemplateList()) {
-      this.allCards().forEach((card) => {
+      this.allCards().forEach(card => {
         return card.restore();
       });
     }
@@ -346,15 +346,10 @@ if (Meteor.isServer) {
     });
   });
 
-  Lists.before.update((userId, doc, fieldNames, modifier, options) => {
-    modifier.$set = modifier.$set || {};
-    modifier.$set.modifiedAt = Date.now();
-  });
-
   Lists.before.remove((userId, doc) => {
     const cards = Cards.find({ listId: doc._id });
     if (cards) {
-      cards.forEach((card) => {
+      cards.forEach(card => {
         Cards.remove(card._id);
       });
     }
@@ -404,7 +399,7 @@ if (Meteor.isServer) {
               _id: doc._id,
               title: doc.title,
             };
-          }
+          },
         ),
       });
     } catch (error) {
@@ -425,7 +420,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId', function(
     req,
-    res
+    res,
   ) {
     try {
       const paramBoardId = req.params.boardId;
@@ -492,7 +487,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId', function(
     req,
-    res
+    res,
   ) {
     try {
       Authentication.checkUserId(req.userId);

+ 1 - 6
models/rules.js

@@ -44,7 +44,7 @@ Rules.attachSchema(
         }
       },
     },
-  })
+  }),
 );
 
 Rules.mutations({
@@ -74,11 +74,6 @@ Rules.allow({
   },
 });
 
-Rules.before.update((userId, doc, fieldNames, modifier, options) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = Date.now();
-});
-
 if (Meteor.isServer) {
   Meteor.startup(() => {
     Rules._collection._ensureIndex({ modifiedAt: -1 });

+ 9 - 16
models/settings.js

@@ -76,7 +76,7 @@ Settings.attachSchema(
         }
       },
     },
-  })
+  }),
 );
 Settings.helpers({
   mailUrl() {
@@ -88,7 +88,7 @@ Settings.helpers({
       return `${protocol}${this.mailServer.host}:${this.mailServer.port}/`;
     }
     return `${protocol}${this.mailServer.username}:${encodeURIComponent(
-      this.mailServer.password
+      this.mailServer.password,
     )}@${this.mailServer.host}:${this.mailServer.port}/`;
   },
 });
@@ -99,11 +99,6 @@ Settings.allow({
   },
 });
 
-Settings.before.update((userId, doc, fieldNames, modifier) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = new Date();
-});
-
 if (Meteor.isServer) {
   Meteor.startup(() => {
     Settings._collection._ensureIndex({ modifiedAt: -1 });
@@ -111,7 +106,7 @@ if (Meteor.isServer) {
     if (!setting) {
       const now = new Date();
       const domain = process.env.ROOT_URL.match(
-        /\/\/(?:www\.)?(.*)?(?:\/)?/
+        /\/\/(?:www\.)?(.*)?(?:\/)?/,
       )[1];
       const from = `Boards Support <support@${domain}>`;
       const defaultSetting = {
@@ -143,9 +138,7 @@ if (Meteor.isServer) {
     if (_.contains(fieldNames, 'mailServer') && doc.mailServer.host) {
       const protocol = doc.mailServer.enableTLS ? 'smtps://' : 'smtp://';
       if (!doc.mailServer.username && !doc.mailServer.password) {
-        process.env.MAIL_URL = `${protocol}${doc.mailServer.host}:${
-          doc.mailServer.port
-        }/`;
+        process.env.MAIL_URL = `${protocol}${doc.mailServer.host}:${doc.mailServer.port}/`;
       } else {
         process.env.MAIL_URL = `${protocol}${
           doc.mailServer.username
@@ -220,14 +213,14 @@ if (Meteor.isServer) {
       if (!user.isAdmin) {
         throw new Meteor.Error('not-allowed');
       }
-      emails.forEach((email) => {
+      emails.forEach(email => {
         if (email && SimpleSchema.RegEx.Email.test(email)) {
           // Checks if the email is already link to an account.
           const userExist = Users.findOne({ email });
           if (userExist) {
             throw new Meteor.Error(
               'user-exist',
-              `The user with the email ${email} has already an account.`
+              `The user with the email ${email} has already an account.`,
             );
           }
           // Checks if the email is already link to an invitation.
@@ -253,10 +246,10 @@ if (Meteor.isServer) {
                 } else {
                   throw new Meteor.Error(
                     'invitation-generated-fail',
-                    err.message
+                    err.message,
                   );
                 }
-              }
+              },
             );
           }
         }
@@ -284,7 +277,7 @@ if (Meteor.isServer) {
         throw new Meteor.Error(
           'email-fail',
           `${TAPi18n.__('email-fail-text', { lng: lang })}: ${message}`,
-          message
+          message,
         );
       }
       return {

+ 11 - 16
models/swimlanes.js

@@ -120,7 +120,7 @@ Swimlanes.attachSchema(
       type: String,
       defaultValue: 'swimlane',
     },
-  })
+  }),
 );
 
 Swimlanes.allow({
@@ -153,7 +153,7 @@ Swimlanes.helpers({
     }
 
     // Copy all lists in swimlane
-    Lists.find(query).forEach((list) => {
+    Lists.find(query).forEach(list => {
       list.type = 'list';
       list.swimlaneId = oldId;
       list.boardId = boardId;
@@ -167,7 +167,7 @@ Swimlanes.helpers({
         swimlaneId: this._id,
         archived: false,
       }),
-      { sort: ['sort'] }
+      { sort: ['sort'] },
     );
   },
 
@@ -178,7 +178,7 @@ Swimlanes.helpers({
         swimlaneId: { $in: [this._id, ''] },
         archived: false,
       },
-      { sort: ['sort'] }
+      { sort: ['sort'] },
     );
   },
 
@@ -234,7 +234,7 @@ Swimlanes.mutations({
 
   archive() {
     if (this.isTemplateSwimlane()) {
-      this.myLists().forEach((list) => {
+      this.myLists().forEach(list => {
         return list.archive();
       });
     }
@@ -243,7 +243,7 @@ Swimlanes.mutations({
 
   restore() {
     if (this.isTemplateSwimlane()) {
-      this.myLists().forEach((list) => {
+      this.myLists().forEach(list => {
         return list.restore();
       });
     }
@@ -262,11 +262,6 @@ Swimlanes.mutations({
   },
 });
 
-Swimlanes.before.update((userId, doc, fieldNames, modifier, options) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = Date.now();
-});
-
 Swimlanes.hookOptions.after.update = { fetchPrevious: false };
 
 if (Meteor.isServer) {
@@ -292,11 +287,11 @@ if (Meteor.isServer) {
         swimlaneId: { $in: [doc._id, ''] },
         archived: false,
       },
-      { sort: ['sort'] }
+      { sort: ['sort'] },
     );
 
     if (lists.count() < 2) {
-      lists.forEach((list) => {
+      lists.forEach(list => {
         list.remove();
       });
     } else {
@@ -350,7 +345,7 @@ if (Meteor.isServer) {
               _id: doc._id,
               title: doc.title,
             };
-          }
+          },
         ),
       });
     } catch (error) {
@@ -372,7 +367,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId', function(
     req,
-    res
+    res,
   ) {
     try {
       const paramBoardId = req.params.boardId;
@@ -459,7 +454,7 @@ if (Meteor.isServer) {
           data: error,
         });
       }
-    }
+    },
   );
 }
 

+ 192 - 168
models/trelloCreator.js

@@ -1,4 +1,4 @@
-const DateString = Match.Where(function (dateAsString) {
+const DateString = Match.Where(function(dateAsString) {
   check(dateAsString, String);
   return moment(dateAsString, moment.ISO_8601).isValid();
 });
@@ -52,10 +52,10 @@ export class TrelloCreator {
    * @param {String} dateString a properly formatted Date
    */
   _now(dateString) {
-    if(dateString) {
+    if (dateString) {
       return new Date(dateString);
     }
-    if(!this._nowDate) {
+    if (!this._nowDate) {
       this._nowDate = new Date();
     }
     return this._nowDate;
@@ -67,75 +67,90 @@ export class TrelloCreator {
    * Otherwise return current logged user.
    * @param trelloUserId
    * @private
-     */
+   */
   _user(trelloUserId) {
-    if(trelloUserId && this.members[trelloUserId]) {
+    if (trelloUserId && this.members[trelloUserId]) {
       return this.members[trelloUserId];
     }
     return Meteor.userId();
   }
 
   checkActions(trelloActions) {
-    check(trelloActions, [Match.ObjectIncluding({
-      data: Object,
-      date: DateString,
-      type: String,
-    })]);
+    check(trelloActions, [
+      Match.ObjectIncluding({
+        data: Object,
+        date: DateString,
+        type: String,
+      }),
+    ]);
     // XXX we could perform more thorough checks based on action type
   }
 
   checkBoard(trelloBoard) {
-    check(trelloBoard, Match.ObjectIncluding({
-      closed: Boolean,
-      name: String,
-      prefs: Match.ObjectIncluding({
-        // XXX refine control by validating 'background' against a list of
-        // allowed values (is it worth the maintenance?)
-        background: String,
-        permissionLevel: Match.Where((value) => {
-          return ['org', 'private', 'public'].indexOf(value)>= 0;
+    check(
+      trelloBoard,
+      Match.ObjectIncluding({
+        closed: Boolean,
+        name: String,
+        prefs: Match.ObjectIncluding({
+          // XXX refine control by validating 'background' against a list of
+          // allowed values (is it worth the maintenance?)
+          background: String,
+          permissionLevel: Match.Where(value => {
+            return ['org', 'private', 'public'].indexOf(value) >= 0;
+          }),
         }),
       }),
-    }));
+    );
   }
 
   checkCards(trelloCards) {
-    check(trelloCards, [Match.ObjectIncluding({
-      closed: Boolean,
-      dateLastActivity: DateString,
-      desc: String,
-      idLabels: [String],
-      idMembers: [String],
-      name: String,
-      pos: Number,
-    })]);
+    check(trelloCards, [
+      Match.ObjectIncluding({
+        closed: Boolean,
+        dateLastActivity: DateString,
+        desc: String,
+        idLabels: [String],
+        idMembers: [String],
+        name: String,
+        pos: Number,
+      }),
+    ]);
   }
 
   checkLabels(trelloLabels) {
-    check(trelloLabels, [Match.ObjectIncluding({
-      // XXX refine control by validating 'color' against a list of allowed
-      // values (is it worth the maintenance?)
-      name: String,
-    })]);
+    check(trelloLabels, [
+      Match.ObjectIncluding({
+        // XXX refine control by validating 'color' against a list of allowed
+        // values (is it worth the maintenance?)
+        name: String,
+      }),
+    ]);
   }
 
   checkLists(trelloLists) {
-    check(trelloLists, [Match.ObjectIncluding({
-      closed: Boolean,
-      name: String,
-    })]);
+    check(trelloLists, [
+      Match.ObjectIncluding({
+        closed: Boolean,
+        name: String,
+      }),
+    ]);
   }
 
   checkChecklists(trelloChecklists) {
-    check(trelloChecklists, [Match.ObjectIncluding({
-      idBoard: String,
-      idCard: String,
-      name: String,
-      checkItems: [Match.ObjectIncluding({
-        state: String,
+    check(trelloChecklists, [
+      Match.ObjectIncluding({
+        idBoard: String,
+        idCard: String,
         name: String,
-      })],
-    })]);
+        checkItems: [
+          Match.ObjectIncluding({
+            state: String,
+            name: String,
+          }),
+        ],
+      }),
+    ]);
   }
 
   // You must call parseActions before calling this one.
@@ -146,31 +161,35 @@ export class TrelloCreator {
       // very old boards won't have a creation activity so no creation date
       createdAt: this._now(this.createdAt.board),
       labels: [],
-      members: [{
-        userId: Meteor.userId(),
-        isAdmin: true,
-        isActive: true,
-        isNoComments: false,
-        isCommentOnly: false,
-        swimlaneId: false,
-      }],
+      members: [
+        {
+          userId: Meteor.userId(),
+          isAdmin: true,
+          isActive: true,
+          isNoComments: false,
+          isCommentOnly: false,
+          swimlaneId: false,
+        },
+      ],
       permission: this.getPermission(trelloBoard.prefs.permissionLevel),
       slug: getSlug(trelloBoard.name) || 'board',
       stars: 0,
       title: trelloBoard.name,
     };
     // now add other members
-    if(trelloBoard.memberships) {
-      trelloBoard.memberships.forEach((trelloMembership) => {
+    if (trelloBoard.memberships) {
+      trelloBoard.memberships.forEach(trelloMembership => {
         const trelloId = trelloMembership.idMember;
         // do we have a mapping?
-        if(this.members[trelloId]) {
+        if (this.members[trelloId]) {
           const wekanId = this.members[trelloId];
           // do we already have it in our list?
-          const wekanMember = boardToCreate.members.find((wekanMember) => wekanMember.userId === wekanId);
-          if(wekanMember) {
+          const wekanMember = boardToCreate.members.find(
+            wekanMember => wekanMember.userId === wekanId,
+          );
+          if (wekanMember) {
             // we're already mapped, but maybe with lower rights
-            if(!wekanMember.isAdmin) {
+            if (!wekanMember.isAdmin) {
               wekanMember.isAdmin = this.getAdmin(trelloMembership.memberType);
             }
           } else {
@@ -186,7 +205,7 @@ export class TrelloCreator {
         }
       });
     }
-    trelloBoard.labels.forEach((label) => {
+    trelloBoard.labels.forEach(label => {
       const labelToCreate = {
         _id: Random.id(6),
         color: label.color ? label.color : 'black',
@@ -198,7 +217,7 @@ export class TrelloCreator {
       boardToCreate.labels.push(labelToCreate);
     });
     const boardId = Boards.direct.insert(boardToCreate);
-    Boards.direct.update(boardId, {$set: {modifiedAt: this._now()}});
+    Boards.direct.update(boardId, { $set: { modifiedAt: this._now() } });
     // log activity
     Activities.direct.insert({
       activityType: 'importBoard',
@@ -225,7 +244,7 @@ export class TrelloCreator {
    */
   createCards(trelloCards, boardId) {
     const result = [];
-    trelloCards.forEach((card) => {
+    trelloCards.forEach(card => {
       const cardToCreate = {
         archived: card.closed,
         boardId,
@@ -243,26 +262,26 @@ export class TrelloCreator {
       };
       // add labels
       if (card.idLabels) {
-        cardToCreate.labelIds = card.idLabels.map((trelloId) => {
+        cardToCreate.labelIds = card.idLabels.map(trelloId => {
           return this.labels[trelloId];
         });
       }
       // add members {
-      if(card.idMembers) {
+      if (card.idMembers) {
         const wekanMembers = [];
         // we can't just map, as some members may not have been mapped
-        card.idMembers.forEach((trelloId) => {
-          if(this.members[trelloId]) {
+        card.idMembers.forEach(trelloId => {
+          if (this.members[trelloId]) {
             const wekanId = this.members[trelloId];
             // we may map multiple Trello members to the same wekan user
             // in which case we risk adding the same user multiple times
-            if(!wekanMembers.find((wId) => wId === wekanId)){
+            if (!wekanMembers.find(wId => wId === wekanId)) {
               wekanMembers.push(wekanId);
             }
           }
           return true;
         });
-        if(wekanMembers.length>0) {
+        if (wekanMembers.length > 0) {
           cardToCreate.members = wekanMembers;
         }
       }
@@ -289,7 +308,7 @@ export class TrelloCreator {
       // add comments
       const comments = this.comments[card.id];
       if (comments) {
-        comments.forEach((comment) => {
+        comments.forEach(comment => {
           const commentToCreate = {
             boardId,
             cardId,
@@ -318,15 +337,15 @@ export class TrelloCreator {
       const attachments = this.attachments[card.id];
       const trelloCoverId = card.idAttachmentCover;
       if (attachments) {
-        attachments.forEach((att) => {
+        attachments.forEach(att => {
           const file = new FS.File();
           // Simulating file.attachData on the client generates multiple errors
           // - HEAD returns null, which causes exception down the line
           // - the template then tries to display the url to the attachment which causes other errors
           // so we make it server only, and let UI catch up once it is done, forget about latency comp.
           const self = this;
-          if(Meteor.isServer) {
-            file.attachData(att.url, function (error) {
+          if (Meteor.isServer) {
+            file.attachData(att.url, function(error) {
               file.boardId = boardId;
               file.cardId = cardId;
               file.userId = self._user(att.idMemberCreator);
@@ -334,15 +353,17 @@ export class TrelloCreator {
               // attachments' related activities automatically
               file.source = 'import';
               if (error) {
-                throw(error);
+                throw error;
               } else {
                 const wekanAtt = Attachments.insert(file, () => {
                   // we do nothing
                 });
                 self.attachmentIds[att.id] = wekanAtt._id;
                 //
-                if(trelloCoverId === att.id) {
-                  Cards.direct.update(cardId, { $set: {coverId: wekanAtt._id}});
+                if (trelloCoverId === att.id) {
+                  Cards.direct.update(cardId, {
+                    $set: { coverId: wekanAtt._id },
+                  });
                 }
               }
             });
@@ -357,7 +378,7 @@ export class TrelloCreator {
 
   // Create labels if they do not exist and load this.labels.
   createLabels(trelloLabels, board) {
-    trelloLabels.forEach((label) => {
+    trelloLabels.forEach(label => {
       const color = label.color;
       const name = label.name;
       const existingLabel = board.getLabel(name, color);
@@ -371,7 +392,7 @@ export class TrelloCreator {
   }
 
   createLists(trelloLists, boardId) {
-    trelloLists.forEach((list) => {
+    trelloLists.forEach(list => {
       const listToCreate = {
         archived: list.closed,
         boardId,
@@ -384,7 +405,7 @@ export class TrelloCreator {
         sort: list.pos,
       };
       const listId = Lists.direct.insert(listToCreate);
-      Lists.direct.update(listId, {$set: {'updatedAt': this._now()}});
+      Lists.direct.update(listId, { $set: { updatedAt: this._now() } });
       this.lists[list.id] = listId;
       // log activity
       // Activities.direct.insert({
@@ -416,12 +437,12 @@ export class TrelloCreator {
       sort: 1,
     };
     const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate);
-    Swimlanes.direct.update(swimlaneId, {$set: {'updatedAt': this._now()}});
+    Swimlanes.direct.update(swimlaneId, { $set: { updatedAt: this._now() } });
     this.swimlane = swimlaneId;
   }
 
   createChecklists(trelloChecklists) {
-    trelloChecklists.forEach((checklist) => {
+    trelloChecklists.forEach(checklist => {
       if (this.cards[checklist.idCard]) {
         // Create the checklist
         const checklistToCreate = {
@@ -435,7 +456,7 @@ export class TrelloCreator {
         this.checklists[checklist.id] = checklistId;
         // Now add the items to the checklistItems
         let counter = 0;
-        checklist.checkItems.forEach((item) => {
+        checklist.checkItems.forEach(item => {
           counter++;
           const checklistItemTocreate = {
             _id: checklistId + counter,
@@ -458,15 +479,15 @@ export class TrelloCreator {
   getColor(trelloColorCode) {
     // trello color name => wekan color
     const mapColors = {
-      'blue': 'belize',
-      'orange': 'pumpkin',
-      'green': 'nephritis',
-      'red': 'pomegranate',
-      'purple': 'wisteria',
-      'pink': 'pomegranate',
-      'lime': 'nephritis',
-      'sky': 'belize',
-      'grey': 'midnight',
+      blue: 'belize',
+      orange: 'pumpkin',
+      green: 'nephritis',
+      red: 'pomegranate',
+      purple: 'wisteria',
+      pink: 'pomegranate',
+      lime: 'nephritis',
+      sky: 'belize',
+      grey: 'midnight',
     };
     const wekanColor = mapColors[trelloColorCode];
     return wekanColor || Boards.simpleSchema()._schema.color.allowedValues[0];
@@ -482,7 +503,7 @@ export class TrelloCreator {
   }
 
   parseActions(trelloActions) {
-    trelloActions.forEach((action) => {
+    trelloActions.forEach(action => {
       if (action.type === 'addAttachmentToCard') {
         // We have to be cautious, because the attachment could have been removed later.
         // In that case Trello still reports its addition, but removes its 'url' field.
@@ -490,11 +511,11 @@ export class TrelloCreator {
         const trelloAttachment = action.data.attachment;
         // We need the idMemberCreator
         trelloAttachment.idMemberCreator = action.idMemberCreator;
-        if(trelloAttachment.url) {
+        if (trelloAttachment.url) {
           // we cannot actually create the Wekan attachment, because we don't yet
           // have the cards to attach it to, so we store it in the instance variable.
           const trelloCardId = action.data.card.id;
-          if(!this.attachments[trelloCardId]) {
+          if (!this.attachments[trelloCardId]) {
             this.attachments[trelloCardId] = [];
           }
           this.attachments[trelloCardId].push(trelloAttachment);
@@ -520,88 +541,89 @@ export class TrelloCreator {
   }
 
   importActions(actions, boardId) {
-    actions.forEach((action) => {
+    actions.forEach(action => {
       switch (action.type) {
-      // Board related actions
-      // TODO: addBoardMember, removeBoardMember
-      case 'createBoard': {
-        Activities.direct.insert({
-          userId: this._user(action.idMemberCreator),
-          type: 'board',
-          activityTypeId: boardId,
-          activityType: 'createBoard',
-          boardId,
-          createdAt: this._now(action.date),
-        });
-        break;
-      }
-      // List related activities
-      // TODO: removeList, archivedList
-      case 'createList': {
-        Activities.direct.insert({
-          userId: this._user(action.idMemberCreator),
-          type: 'list',
-          activityType: 'createList',
-          listId: this.lists[action.data.list.id],
-          boardId,
-          createdAt: this._now(action.date),
-        });
-        break;
-      }
-      // Card related activities
-      // TODO: archivedCard, restoredCard, joinMember, unjoinMember
-      case 'createCard': {
-        Activities.direct.insert({
-          userId: this._user(action.idMemberCreator),
-          activityType: 'createCard',
-          listId: this.lists[action.data.list.id],
-          cardId: this.cards[action.data.card.id],
-          boardId,
-          createdAt: this._now(action.date),
-        });
-        break;
-      }
-      case 'updateCard': {
-        if (action.data.old.idList) {
+        // Board related actions
+        // TODO: addBoardMember, removeBoardMember
+        case 'createBoard': {
+          Activities.direct.insert({
+            userId: this._user(action.idMemberCreator),
+            type: 'board',
+            activityTypeId: boardId,
+            activityType: 'createBoard',
+            boardId,
+            createdAt: this._now(action.date),
+          });
+          break;
+        }
+        // List related activities
+        // TODO: removeList, archivedList
+        case 'createList': {
+          Activities.direct.insert({
+            userId: this._user(action.idMemberCreator),
+            type: 'list',
+            activityType: 'createList',
+            listId: this.lists[action.data.list.id],
+            boardId,
+            createdAt: this._now(action.date),
+          });
+          break;
+        }
+        // Card related activities
+        // TODO: archivedCard, restoredCard, joinMember, unjoinMember
+        case 'createCard': {
           Activities.direct.insert({
             userId: this._user(action.idMemberCreator),
-            oldListId: this.lists[action.data.old.idList],
-            activityType: 'moveCard',
-            listId: this.lists[action.data.listAfter.id],
+            activityType: 'createCard',
+            listId: this.lists[action.data.list.id],
             cardId: this.cards[action.data.card.id],
             boardId,
             createdAt: this._now(action.date),
           });
+          break;
+        }
+        case 'updateCard': {
+          if (action.data.old.idList) {
+            Activities.direct.insert({
+              userId: this._user(action.idMemberCreator),
+              oldListId: this.lists[action.data.old.idList],
+              activityType: 'moveCard',
+              listId: this.lists[action.data.listAfter.id],
+              cardId: this.cards[action.data.card.id],
+              boardId,
+              createdAt: this._now(action.date),
+            });
+          }
+          break;
+        }
+        // Comment related activities
+        // Trello doesn't export the comment id
+        // Attachment related activities
+        case 'addAttachmentToCard': {
+          Activities.direct.insert({
+            userId: this._user(action.idMemberCreator),
+            type: 'card',
+            activityType: 'addAttachment',
+            attachmentId: this.attachmentIds[action.data.attachment.id],
+            cardId: this.cards[action.data.card.id],
+            boardId,
+            createdAt: this._now(action.date),
+          });
+          break;
+        }
+        // Checklist related activities
+        case 'addChecklistToCard': {
+          Activities.direct.insert({
+            userId: this._user(action.idMemberCreator),
+            activityType: 'addChecklist',
+            cardId: this.cards[action.data.card.id],
+            checklistId: this.checklists[action.data.checklist.id],
+            boardId,
+            createdAt: this._now(action.date),
+          });
+          break;
         }
-        break;
-      }
-      // Comment related activities
-      // Trello doesn't export the comment id
-      // Attachment related activities
-      case 'addAttachmentToCard': {
-        Activities.direct.insert({
-          userId: this._user(action.idMemberCreator),
-          type: 'card',
-          activityType: 'addAttachment',
-          attachmentId: this.attachmentIds[action.data.attachment.id],
-          cardId: this.cards[action.data.card.id],
-          boardId,
-          createdAt: this._now(action.date),
-        });
-        break;
       }
-      // Checklist related activities
-      case 'addChecklistToCard': {
-        Activities.direct.insert({
-          userId: this._user(action.idMemberCreator),
-          activityType: 'addChecklist',
-          cardId: this.cards[action.data.card.id],
-          checklistId: this.checklists[action.data.checklist.id],
-          boardId,
-          createdAt: this._now(action.date),
-        });
-        break;
-      }}
       // Trello doesn't have an add checklist item action
     });
   }
@@ -624,7 +646,9 @@ export class TrelloCreator {
 
   create(board, currentBoardId) {
     // TODO : Make isSandstorm variable global
-    const isSandstorm = Meteor.settings && Meteor.settings.public &&
+    const isSandstorm =
+      Meteor.settings &&
+      Meteor.settings.public &&
       Meteor.settings.public.sandstorm;
     if (isSandstorm && currentBoardId) {
       const currentBoard = Boards.findOne(currentBoardId);

+ 1 - 6
models/triggers.js

@@ -51,18 +51,13 @@ Triggers.helpers({
 
   labels() {
     const boardLabels = this.board().labels;
-    const cardLabels = _.filter(boardLabels, (label) => {
+    const cardLabels = _.filter(boardLabels, label => {
       return _.contains(this.labelIds, label._id);
     });
     return cardLabels;
   },
 });
 
-Triggers.before.update((userId, doc, fieldNames, modifier, options) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = Date.now();
-});
-
 if (Meteor.isServer) {
   Meteor.startup(() => {
     Triggers._collection._ensureIndex({ modifiedAt: -1 });

+ 1 - 8
models/unsavedEdits.js

@@ -46,14 +46,7 @@ UnsavedEditCollection.attachSchema(
         }
       },
     },
-  })
-);
-
-UnsavedEditCollection.before.update(
-  (userId, doc, fieldNames, modifier, options) => {
-    modifier.$set = modifier.$set || {};
-    modifier.$set.modifiedAt = Date.now();
-  }
+  }),
 );
 
 if (Meteor.isServer) {

+ 18 - 22
models/users.js

@@ -247,7 +247,7 @@ Users.attachSchema(
       optional: false,
       defaultValue: 'password',
     },
-  })
+  }),
 );
 
 Users.allow({
@@ -259,7 +259,7 @@ Users.allow({
     const adminsNumber = Users.find({ isAdmin: true }).count();
     const { isAdmin } = Users.findOne(
       { _id: userId },
-      { fields: { isAdmin: 1 } }
+      { fields: { isAdmin: 1 } },
     );
 
     // Prevents remove of the only one administrator
@@ -533,7 +533,7 @@ Meteor.methods({
     check(email, String);
     const existingUser = Users.findOne(
       { 'emails.address': email },
-      { fields: { _id: 1 } }
+      { fields: { _id: 1 } },
     );
     if (existingUser) {
       throw new Meteor.Error('email-already-taken');
@@ -700,7 +700,7 @@ if (Meteor.isServer) {
     if (!options || !options.profile) {
       throw new Meteor.Error(
         'error-invitation-code-blank',
-        'The invitation code is required'
+        'The invitation code is required',
       );
     }
     const invitationCode = InvitationCodes.findOne({
@@ -711,7 +711,8 @@ if (Meteor.isServer) {
     if (!invitationCode) {
       throw new Meteor.Error(
         'error-invitation-code-not-exist',
-        'The invitation code doesn\'t exist'
+        // eslint-disable-next-line quotes
+        "The invitation code doesn't exist",
       );
     } else {
       user.profile = { icode: options.profile.invitationcode };
@@ -722,18 +723,13 @@ if (Meteor.isServer) {
         Meteor.bindEnvironment(() => {
           InvitationCodes.remove({ _id: invitationCode._id });
         }),
-        200
+        200,
       );
       return user;
     }
   });
 }
 
-Users.before.update((userId, doc, fieldNames, modifier, options) => {
-  modifier.$set = modifier.$set || {};
-  modifier.$set.modifiedAt = Date.now();
-});
-
 if (Meteor.isServer) {
   // Let mongoDB ensure username unicity
   Meteor.startup(() => {
@@ -742,7 +738,7 @@ if (Meteor.isServer) {
       {
         username: 1,
       },
-      { unique: true }
+      { unique: true },
     );
   });
 
@@ -786,7 +782,7 @@ if (Meteor.isServer) {
     // b. We use it to find deleted and newly inserted ids by using it in one
     // direction and then in the other.
     function incrementBoards(boardsIds, inc) {
-      boardsIds.forEach((boardId) => {
+      boardsIds.forEach(boardId => {
         Boards.update(boardId, { $inc: { stars: inc } });
       });
     }
@@ -855,7 +851,7 @@ if (Meteor.isServer) {
                 Users.update(fakeUserId.get(), {
                   $set: { 'profile.cardTemplatesSwimlaneId': swimlaneId },
                 });
-              }
+              },
             );
 
             // Insert the list templates swimlane
@@ -872,7 +868,7 @@ if (Meteor.isServer) {
                 Users.update(fakeUserId.get(), {
                   $set: { 'profile.listTemplatesSwimlaneId': swimlaneId },
                 });
-              }
+              },
             );
 
             // Insert the board templates swimlane
@@ -889,9 +885,9 @@ if (Meteor.isServer) {
                 Users.update(fakeUserId.get(), {
                   $set: { 'profile.boardTemplatesSwimlaneId': swimlaneId },
                 });
-              }
+              },
             );
-          }
+          },
         );
       });
     });
@@ -921,7 +917,7 @@ if (Meteor.isServer) {
       if (!invitationCode) {
         throw new Meteor.Error('error-invitation-code-not-exist');
       } else {
-        invitationCode.boardsToBeInvited.forEach((boardId) => {
+        invitationCode.boardsToBeInvited.forEach(boardId => {
           const board = Boards.findOne(boardId);
           board.addMember(doc._id);
         });
@@ -1071,7 +1067,7 @@ if (Meteor.isServer) {
                   loginDisabled: true,
                   'services.resume.loginTokens': '',
                 },
-              }
+              },
             );
           } else if (action === 'enableLogin') {
             Users.update({ _id: id }, { $set: { loginDisabled: '' } });
@@ -1112,7 +1108,7 @@ if (Meteor.isServer) {
    */
   JsonRoutes.add('POST', '/api/boards/:boardId/members/:userId/add', function(
     req,
-    res
+    res,
   ) {
     try {
       Authentication.checkUserId(req.userId);
@@ -1136,7 +1132,7 @@ if (Meteor.isServer) {
                 isTrue(isAdmin),
                 isTrue(isNoComments),
                 isTrue(isCommentOnly),
-                userId
+                userId,
               );
             }
             return {
@@ -1207,7 +1203,7 @@ if (Meteor.isServer) {
           data: error,
         });
       }
-    }
+    },
   );
 
   /**

+ 6 - 6
models/watchable.js

@@ -1,5 +1,5 @@
 // simple version, only toggle watch / unwatch
-const simpleWatchable = (collection) => {
+const simpleWatchable = collection => {
   collection.attachSchema({
     watchers: {
       type: [String],
@@ -24,8 +24,8 @@ const simpleWatchable = (collection) => {
   collection.mutations({
     setWatcher(userId, level) {
       // if level undefined or null or false, then remove
-      if (!level) return { $pull: { watchers: userId }};
-      return { $addToSet: { watchers: userId }};
+      if (!level) return { $pull: { watchers: userId } };
+      return { $addToSet: { watchers: userId } };
     },
   });
 };
@@ -34,7 +34,7 @@ const simpleWatchable = (collection) => {
 const complexWatchOptions = ['watching', 'tracking', 'muted'];
 const complexWatchDefault = 'muted';
 
-const complexWatchable = (collection) => {
+const complexWatchable = collection => {
   collection.attachSchema({
     'watchers.$.userId': {
       type: String,
@@ -72,9 +72,9 @@ const complexWatchable = (collection) => {
     setWatcher(userId, level) {
       // if level undefined or null or false, then remove
       if (level === complexWatchDefault) level = null;
-      if (!level) return { $pull: { watchers: { userId }}};
+      if (!level) return { $pull: { watchers: { userId } } };
       const index = this.watcherIndex(userId);
-      if (index<0) return { $push: { watchers: { userId, level }}};
+      if (index < 0) return { $push: { watchers: { userId, level } } };
       return {
         $set: {
           [`watchers.${index}.level`]: level,

+ 301 - 278
models/wekanCreator.js

@@ -87,99 +87,120 @@ export class WekanCreator {
   }
 
   checkActivities(wekanActivities) {
-    check(wekanActivities, [Match.ObjectIncluding({
-      activityType: String,
-      createdAt: DateString,
-    })]);
+    check(wekanActivities, [
+      Match.ObjectIncluding({
+        activityType: String,
+        createdAt: DateString,
+      }),
+    ]);
     // XXX we could perform more thorough checks based on action type
   }
 
   checkBoard(wekanBoard) {
-    check(wekanBoard, Match.ObjectIncluding({
-      archived: Boolean,
-      title: String,
-      // XXX refine control by validating 'color' against a list of
-      // allowed values (is it worth the maintenance?)
-      color: String,
-      permission: Match.Where((value) => {
-        return ['private', 'public'].indexOf(value) >= 0;
+    check(
+      wekanBoard,
+      Match.ObjectIncluding({
+        archived: Boolean,
+        title: String,
+        // XXX refine control by validating 'color' against a list of
+        // allowed values (is it worth the maintenance?)
+        color: String,
+        permission: Match.Where(value => {
+          return ['private', 'public'].indexOf(value) >= 0;
+        }),
       }),
-    }));
+    );
   }
 
   checkCards(wekanCards) {
-    check(wekanCards, [Match.ObjectIncluding({
-      archived: Boolean,
-      dateLastActivity: DateString,
-      labelIds: [String],
-      title: String,
-      sort: Number,
-    })]);
+    check(wekanCards, [
+      Match.ObjectIncluding({
+        archived: Boolean,
+        dateLastActivity: DateString,
+        labelIds: [String],
+        title: String,
+        sort: Number,
+      }),
+    ]);
   }
 
   checkLabels(wekanLabels) {
-    check(wekanLabels, [Match.ObjectIncluding({
-      // XXX refine control by validating 'color' against a list of allowed
-      // values (is it worth the maintenance?)
-      color: String,
-    })]);
+    check(wekanLabels, [
+      Match.ObjectIncluding({
+        // XXX refine control by validating 'color' against a list of allowed
+        // values (is it worth the maintenance?)
+        color: String,
+      }),
+    ]);
   }
 
   checkLists(wekanLists) {
-    check(wekanLists, [Match.ObjectIncluding({
-      archived: Boolean,
-      title: String,
-    })]);
+    check(wekanLists, [
+      Match.ObjectIncluding({
+        archived: Boolean,
+        title: String,
+      }),
+    ]);
   }
 
   checkSwimlanes(wekanSwimlanes) {
-    check(wekanSwimlanes, [Match.ObjectIncluding({
-      archived: Boolean,
-      title: String,
-    })]);
+    check(wekanSwimlanes, [
+      Match.ObjectIncluding({
+        archived: Boolean,
+        title: String,
+      }),
+    ]);
   }
 
   checkChecklists(wekanChecklists) {
-    check(wekanChecklists, [Match.ObjectIncluding({
-      cardId: String,
-      title: String,
-    })]);
+    check(wekanChecklists, [
+      Match.ObjectIncluding({
+        cardId: String,
+        title: String,
+      }),
+    ]);
   }
 
   checkChecklistItems(wekanChecklistItems) {
-    check(wekanChecklistItems, [Match.ObjectIncluding({
-      cardId: String,
-      title: String,
-    })]);
+    check(wekanChecklistItems, [
+      Match.ObjectIncluding({
+        cardId: String,
+        title: String,
+      }),
+    ]);
   }
 
   checkRules(wekanRules) {
-    check(wekanRules, [Match.ObjectIncluding({
-      triggerId: String,
-      actionId: String,
-      title: String,
-    })]);
+    check(wekanRules, [
+      Match.ObjectIncluding({
+        triggerId: String,
+        actionId: String,
+        title: String,
+      }),
+    ]);
   }
 
   checkTriggers(wekanTriggers) {
     // XXX More check based on trigger type
-    check(wekanTriggers, [Match.ObjectIncluding({
-      activityType: String,
-      desc: String,
-    })]);
+    check(wekanTriggers, [
+      Match.ObjectIncluding({
+        activityType: String,
+        desc: String,
+      }),
+    ]);
   }
 
   getMembersToMap(data) {
-  // we will work on the list itself (an ordered array of objects) when a
-  // mapping is done, we add a 'wekan' field to the object representing the
-  // imported member
+    // we will work on the list itself (an ordered array of objects) when a
+    // mapping is done, we add a 'wekan' field to the object representing the
+    // imported member
     const membersToMap = data.members;
     const users = data.users;
     // auto-map based on username
-    membersToMap.forEach((importedMember) => {
+    membersToMap.forEach(importedMember => {
       importedMember.id = importedMember.userId;
       delete importedMember.userId;
-      const user = users.filter((user) => {
+      const user = users.filter(user => {
         return user._id === importedMember.id;
       })[0];
       if (user.profile && user.profile.fullname) {
@@ -196,10 +217,12 @@ export class WekanCreator {
 
   checkActions(wekanActions) {
     // XXX More check based on action type
-    check(wekanActions, [Match.ObjectIncluding({
-      actionType: String,
-      desc: String,
-    })]);
+    check(wekanActions, [
+      Match.ObjectIncluding({
+        actionType: String,
+        desc: String,
+      }),
+    ]);
   }
 
   // You must call parseActions before calling this one.
@@ -210,15 +233,17 @@ export class WekanCreator {
       // very old boards won't have a creation activity so no creation date
       createdAt: this._now(boardToImport.createdAt),
       labels: [],
-      members: [{
-        userId: Meteor.userId(),
-        wekanId: Meteor.userId(),
-        isActive: true,
-        isAdmin: true,
-        isNoComments: false,
-        isCommentOnly: false,
-        swimlaneId: false,
-      }],
+      members: [
+        {
+          userId: Meteor.userId(),
+          wekanId: Meteor.userId(),
+          isActive: true,
+          isAdmin: true,
+          isNoComments: false,
+          isCommentOnly: false,
+          swimlaneId: false,
+        },
+      ],
       // Standalone Export has modifiedAt missing, adding modifiedAt to fix it
       modifiedAt: this._now(boardToImport.modifiedAt),
       permission: boardToImport.permission,
@@ -228,16 +253,20 @@ export class WekanCreator {
     };
     // now add other members
     if (boardToImport.members) {
-      boardToImport.members.forEach((wekanMember) => {
+      boardToImport.members.forEach(wekanMember => {
         // do we already have it in our list?
-        if (!boardToCreate.members.some((member) => member.wekanId === wekanMember.wekanId))
+        if (
+          !boardToCreate.members.some(
+            member => member.wekanId === wekanMember.wekanId,
+          )
+        )
           boardToCreate.members.push({
             ...wekanMember,
             userId: wekanMember.wekanId,
           });
       });
     }
-    boardToImport.labels.forEach((label) => {
+    boardToImport.labels.forEach(label => {
       const labelToCreate = {
         _id: Random.id(6),
         color: label.color,
@@ -279,7 +308,7 @@ export class WekanCreator {
    */
   createCards(wekanCards, boardId) {
     const result = [];
-    wekanCards.forEach((card) => {
+    wekanCards.forEach(card => {
       const cardToCreate = {
         archived: card.archived,
         boardId,
@@ -300,7 +329,7 @@ export class WekanCreator {
       };
       // add labels
       if (card.labelIds) {
-        cardToCreate.labelIds = card.labelIds.map((wekanId) => {
+        cardToCreate.labelIds = card.labelIds.map(wekanId => {
           return this.labels[wekanId];
         });
       }
@@ -308,12 +337,12 @@ export class WekanCreator {
       if (card.members) {
         const wekanMembers = [];
         // we can't just map, as some members may not have been mapped
-        card.members.forEach((sourceMemberId) => {
+        card.members.forEach(sourceMemberId => {
           if (this.members[sourceMemberId]) {
             const wekanId = this.members[sourceMemberId];
             // we may map multiple Wekan members to the same wekan user
             // in which case we risk adding the same user multiple times
-            if (!wekanMembers.find((wId) => wId === wekanId)) {
+            if (!wekanMembers.find(wId => wId === wekanId)) {
               wekanMembers.push(wekanId);
             }
           }
@@ -349,7 +378,7 @@ export class WekanCreator {
       // add comments
       const comments = this.comments[card._id];
       if (comments) {
-        comments.forEach((comment) => {
+        comments.forEach(comment => {
           const commentToCreate = {
             boardId,
             cardId,
@@ -377,7 +406,7 @@ export class WekanCreator {
       const attachments = this.attachments[card._id];
       const wekanCoverId = card.coverId;
       if (attachments) {
-        attachments.forEach((att) => {
+        attachments.forEach(att => {
           const file = new FS.File();
           // Simulating file.attachData on the client generates multiple errors
           // - HEAD returns null, which causes exception down the line
@@ -394,7 +423,7 @@ export class WekanCreator {
                 // attachments' related activities automatically
                 file.source = 'import';
                 if (error) {
-                  throw (error);
+                  throw error;
                 } else {
                   const wekanAtt = Attachments.insert(file, () => {
                     // we do nothing
@@ -411,33 +440,37 @@ export class WekanCreator {
                 }
               });
             } else if (att.file) {
-              file.attachData(new Buffer(att.file, 'base64'), {
-                type: att.type,
-              }, (error) => {
-                file.name(att.name);
-                file.boardId = boardId;
-                file.cardId = cardId;
-                file.userId = self._user(att.userId);
-                // The field source will only be used to prevent adding
-                // attachments' related activities automatically
-                file.source = 'import';
-                if (error) {
-                  throw (error);
-                } else {
-                  const wekanAtt = Attachments.insert(file, () => {
-                    // we do nothing
-                  });
-                  this.attachmentIds[att._id] = wekanAtt._id;
-                  //
-                  if (wekanCoverId === att._id) {
-                    Cards.direct.update(cardId, {
-                      $set: {
-                        coverId: wekanAtt._id,
-                      },
+              file.attachData(
+                new Buffer(att.file, 'base64'),
+                {
+                  type: att.type,
+                },
+                error => {
+                  file.name(att.name);
+                  file.boardId = boardId;
+                  file.cardId = cardId;
+                  file.userId = self._user(att.userId);
+                  // The field source will only be used to prevent adding
+                  // attachments' related activities automatically
+                  file.source = 'import';
+                  if (error) {
+                    throw error;
+                  } else {
+                    const wekanAtt = Attachments.insert(file, () => {
+                      // we do nothing
                     });
+                    this.attachmentIds[att._id] = wekanAtt._id;
+                    //
+                    if (wekanCoverId === att._id) {
+                      Cards.direct.update(cardId, {
+                        $set: {
+                          coverId: wekanAtt._id,
+                        },
+                      });
+                    }
                   }
-                }
-              });
+                },
+              );
             }
           }
           // todo XXX set cover - if need be
@@ -450,7 +483,7 @@ export class WekanCreator {
 
   // Create labels if they do not exist and load this.labels.
   createLabels(wekanLabels, board) {
-    wekanLabels.forEach((label) => {
+    wekanLabels.forEach(label => {
       const color = label.color;
       const name = label.name;
       const existingLabel = board.getLabel(name, color);
@@ -479,7 +512,7 @@ export class WekanCreator {
       const listId = Lists.direct.insert(listToCreate);
       Lists.direct.update(listId, {
         $set: {
-          'updatedAt': this._now(),
+          updatedAt: this._now(),
         },
       });
       this.lists[list._id] = listId;
@@ -520,7 +553,7 @@ export class WekanCreator {
       const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate);
       Swimlanes.direct.update(swimlaneId, {
         $set: {
-          'updatedAt': this._now(),
+          updatedAt: this._now(),
         },
       });
       this.swimlanes[swimlane._id] = swimlaneId;
@@ -545,7 +578,7 @@ export class WekanCreator {
   }
 
   createTriggers(wekanTriggers, boardId) {
-    wekanTriggers.forEach((trigger) => {
+    wekanTriggers.forEach(trigger => {
       if (trigger.hasOwnProperty('labelId')) {
         trigger.labelId = this.labels[trigger.labelId];
       }
@@ -560,7 +593,7 @@ export class WekanCreator {
   }
 
   createActions(wekanActions, boardId) {
-    wekanActions.forEach((action) => {
+    wekanActions.forEach(action => {
       if (action.hasOwnProperty('labelId')) {
         action.labelId = this.labels[action.labelId];
       }
@@ -575,7 +608,7 @@ export class WekanCreator {
   }
 
   createRules(wekanRules, boardId) {
-    wekanRules.forEach((rule) => {
+    wekanRules.forEach(rule => {
       // Create the rule
       rule.boardId = boardId;
       rule.triggerId = this.triggers[rule.triggerId];
@@ -595,189 +628,178 @@ export class WekanCreator {
         sort: checklistitem.sort ? checklistitem.sort : checklistitemIndex,
         isFinished: checklistitem.isFinished,
       };
-      const checklistItemId = ChecklistItems.direct.insert(checklistItemTocreate);
+      const checklistItemId = ChecklistItems.direct.insert(
+        checklistItemTocreate,
+      );
       this.checklistItems[checklistitem._id] = checklistItemId;
     });
   }
 
   parseActivities(wekanBoard) {
-    wekanBoard.activities.forEach((activity) => {
+    wekanBoard.activities.forEach(activity => {
       switch (activity.activityType) {
-      case 'addAttachment':
-      {
-        // We have to be cautious, because the attachment could have been removed later.
-        // In that case Wekan still reports its addition, but removes its 'url' field.
-        // So we test for that
-        const wekanAttachment = wekanBoard.attachments.filter((attachment) => {
-          return attachment._id === activity.attachmentId;
-        })[0];
-
-        if (typeof wekanAttachment !== 'undefined' && wekanAttachment) {
-          if (wekanAttachment.url || wekanAttachment.file) {
-            // we cannot actually create the Wekan attachment, because we don't yet
-            // have the cards to attach it to, so we store it in the instance variable.
-            const wekanCardId = activity.cardId;
-            if (!this.attachments[wekanCardId]) {
-              this.attachments[wekanCardId] = [];
+        case 'addAttachment': {
+          // We have to be cautious, because the attachment could have been removed later.
+          // In that case Wekan still reports its addition, but removes its 'url' field.
+          // So we test for that
+          const wekanAttachment = wekanBoard.attachments.filter(attachment => {
+            return attachment._id === activity.attachmentId;
+          })[0];
+
+          if (typeof wekanAttachment !== 'undefined' && wekanAttachment) {
+            if (wekanAttachment.url || wekanAttachment.file) {
+              // we cannot actually create the Wekan attachment, because we don't yet
+              // have the cards to attach it to, so we store it in the instance variable.
+              const wekanCardId = activity.cardId;
+              if (!this.attachments[wekanCardId]) {
+                this.attachments[wekanCardId] = [];
+              }
+              this.attachments[wekanCardId].push(wekanAttachment);
             }
-            this.attachments[wekanCardId].push(wekanAttachment);
           }
+          break;
         }
-        break;
-      }
-      case 'addComment':
-      {
-        const wekanComment = wekanBoard.comments.filter((comment) => {
-          return comment._id === activity.commentId;
-        })[0];
-        const id = activity.cardId;
-        if (!this.comments[id]) {
-          this.comments[id] = [];
+        case 'addComment': {
+          const wekanComment = wekanBoard.comments.filter(comment => {
+            return comment._id === activity.commentId;
+          })[0];
+          const id = activity.cardId;
+          if (!this.comments[id]) {
+            this.comments[id] = [];
+          }
+          this.comments[id].push(wekanComment);
+          break;
+        }
+        case 'createBoard': {
+          this.createdAt.board = activity.createdAt;
+          break;
+        }
+        case 'createCard': {
+          const cardId = activity.cardId;
+          this.createdAt.cards[cardId] = activity.createdAt;
+          this.createdBy.cards[cardId] = activity.userId;
+          break;
+        }
+        case 'createList': {
+          const listId = activity.listId;
+          this.createdAt.lists[listId] = activity.createdAt;
+          break;
+        }
+        case 'createSwimlane': {
+          const swimlaneId = activity.swimlaneId;
+          this.createdAt.swimlanes[swimlaneId] = activity.createdAt;
+          break;
         }
-        this.comments[id].push(wekanComment);
-        break;
-      }
-      case 'createBoard':
-      {
-        this.createdAt.board = activity.createdAt;
-        break;
-      }
-      case 'createCard':
-      {
-        const cardId = activity.cardId;
-        this.createdAt.cards[cardId] = activity.createdAt;
-        this.createdBy.cards[cardId] = activity.userId;
-        break;
-      }
-      case 'createList':
-      {
-        const listId = activity.listId;
-        this.createdAt.lists[listId] = activity.createdAt;
-        break;
-      }
-      case 'createSwimlane':
-      {
-        const swimlaneId = activity.swimlaneId;
-        this.createdAt.swimlanes[swimlaneId] = activity.createdAt;
-        break;
-      }
       }
     });
   }
 
   importActivities(activities, boardId) {
-    activities.forEach((activity) => {
+    activities.forEach(activity => {
       switch (activity.activityType) {
-      // Board related activities
-      // TODO: addBoardMember, removeBoardMember
-      case 'createBoard':
-      {
-        Activities.direct.insert({
-          userId: this._user(activity.userId),
-          type: 'board',
-          activityTypeId: boardId,
-          activityType: activity.activityType,
-          boardId,
-          createdAt: this._now(activity.createdAt),
-        });
-        break;
-      }
-      // List related activities
-      // TODO: removeList, archivedList
-      case 'createList':
-      {
-        Activities.direct.insert({
-          userId: this._user(activity.userId),
-          type: 'list',
-          activityType: activity.activityType,
-          listId: this.lists[activity.listId],
-          boardId,
-          createdAt: this._now(activity.createdAt),
-        });
-        break;
-      }
-      // Card related activities
-      // TODO: archivedCard, restoredCard, joinMember, unjoinMember
-      case 'createCard':
-      {
-        Activities.direct.insert({
-          userId: this._user(activity.userId),
-          activityType: activity.activityType,
-          listId: this.lists[activity.listId],
-          cardId: this.cards[activity.cardId],
-          boardId,
-          createdAt: this._now(activity.createdAt),
-        });
-        break;
-      }
-      case 'moveCard':
-      {
-        Activities.direct.insert({
-          userId: this._user(activity.userId),
-          oldListId: this.lists[activity.oldListId],
-          activityType: activity.activityType,
-          listId: this.lists[activity.listId],
-          cardId: this.cards[activity.cardId],
-          boardId,
-          createdAt: this._now(activity.createdAt),
-        });
-        break;
-      }
-      // Comment related activities
-      case 'addComment':
-      {
-        Activities.direct.insert({
-          userId: this._user(activity.userId),
-          activityType: activity.activityType,
-          cardId: this.cards[activity.cardId],
-          commentId: this.commentIds[activity.commentId],
-          boardId,
-          createdAt: this._now(activity.createdAt),
-        });
-        break;
-      }
-      // Attachment related activities
-      case 'addAttachment':
-      {
-        Activities.direct.insert({
-          userId: this._user(activity.userId),
-          type: 'card',
-          activityType: activity.activityType,
-          attachmentId: this.attachmentIds[activity.attachmentId],
-          cardId: this.cards[activity.cardId],
-          boardId,
-          createdAt: this._now(activity.createdAt),
-        });
-        break;
-      }
-      // Checklist related activities
-      case 'addChecklist':
-      {
-        Activities.direct.insert({
-          userId: this._user(activity.userId),
-          activityType: activity.activityType,
-          cardId: this.cards[activity.cardId],
-          checklistId: this.checklists[activity.checklistId],
-          boardId,
-          createdAt: this._now(activity.createdAt),
-        });
-        break;
-      }
-      case 'addChecklistItem':
-      {
-        Activities.direct.insert({
-          userId: this._user(activity.userId),
-          activityType: activity.activityType,
-          cardId: this.cards[activity.cardId],
-          checklistId: this.checklists[activity.checklistId],
-          checklistItemId: activity.checklistItemId.replace(
-            activity.checklistId,
-            this.checklists[activity.checklistId]),
-          boardId,
-          createdAt: this._now(activity.createdAt),
-        });
-        break;
-      }
+        // Board related activities
+        // TODO: addBoardMember, removeBoardMember
+        case 'createBoard': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            type: 'board',
+            activityTypeId: boardId,
+            activityType: activity.activityType,
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        // List related activities
+        // TODO: removeList, archivedList
+        case 'createList': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            type: 'list',
+            activityType: activity.activityType,
+            listId: this.lists[activity.listId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        // Card related activities
+        // TODO: archivedCard, restoredCard, joinMember, unjoinMember
+        case 'createCard': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            activityType: activity.activityType,
+            listId: this.lists[activity.listId],
+            cardId: this.cards[activity.cardId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        case 'moveCard': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            oldListId: this.lists[activity.oldListId],
+            activityType: activity.activityType,
+            listId: this.lists[activity.listId],
+            cardId: this.cards[activity.cardId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        // Comment related activities
+        case 'addComment': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            activityType: activity.activityType,
+            cardId: this.cards[activity.cardId],
+            commentId: this.commentIds[activity.commentId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        // Attachment related activities
+        case 'addAttachment': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            type: 'card',
+            activityType: activity.activityType,
+            attachmentId: this.attachmentIds[activity.attachmentId],
+            cardId: this.cards[activity.cardId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        // Checklist related activities
+        case 'addChecklist': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            activityType: activity.activityType,
+            cardId: this.cards[activity.cardId],
+            checklistId: this.checklists[activity.checklistId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        case 'addChecklistItem': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            activityType: activity.activityType,
+            cardId: this.cards[activity.cardId],
+            checklistId: this.checklists[activity.checklistId],
+            checklistItemId: activity.checklistItemId.replace(
+              activity.checklistId,
+              this.checklists[activity.checklistId],
+            ),
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
       }
     });
   }
@@ -788,7 +810,6 @@ export class WekanCreator {
     // check(data, {
     //   membersMapping: Match.Optional(Object),
     // });
-
     // this.checkActivities(board.activities);
     // this.checkBoard(board);
     // this.checkLabels(board.labels);
@@ -807,7 +828,9 @@ export class WekanCreator {
 
   create(board, currentBoardId) {
     // TODO : Make isSandstorm variable global
-    const isSandstorm = Meteor.settings && Meteor.settings.public &&
+    const isSandstorm =
+      Meteor.settings &&
+      Meteor.settings.public &&
       Meteor.settings.public.sandstorm;
     if (isSandstorm && currentBoardId) {
       const currentBoard = Boards.findOne(currentBoardId);

+ 2 - 2
models/wekanmapper.js

@@ -5,10 +5,10 @@ export function getMembersToMap(data) {
   const membersToMap = data.members;
   const users = data.users;
   // auto-map based on username
-  membersToMap.forEach((importedMember) => {
+  membersToMap.forEach(importedMember => {
     importedMember.id = importedMember.userId;
     delete importedMember.userId;
-    const user = users.filter((user) => {
+    const user = users.filter(user => {
       return user._id === importedMember.id;
     })[0];
     if (user.profile && user.profile.fullname) {

+ 1 - 1
package.json

@@ -8,7 +8,7 @@
     "lint:eslint:fix": "eslint --ext .js --ignore-path .eslintignore --fix .",
     "lint:staged": "lint-staged",
     "prettify": "prettier --write '**/*.js' '**/*.jsx'",
-    "test": "npm run --silent lint"
+    "test": "npm run lint"
   },
   "lint-staged": {
     "*.js": [

+ 134 - 92
sandstorm.js

@@ -1,7 +1,7 @@
 // Sandstorm context is detected using the METEOR_SETTINGS environment variable
 // in the package definition.
-const isSandstorm = Meteor.settings && Meteor.settings.public &&
-                    Meteor.settings.public.sandstorm;
+const isSandstorm =
+  Meteor.settings && Meteor.settings.public && Meteor.settings.public.sandstorm;
 
 // In sandstorm we only have one board per sandstorm instance. Since we want to
 // keep most of our code unchanged, we simply hard-code a board `_id` and
@@ -28,15 +28,17 @@ if (isSandstorm && Meteor.isServer) {
   const Package = Capnp.importSystem('sandstorm/package.capnp');
   const Powerbox = Capnp.importSystem('sandstorm/powerbox.capnp');
   const Identity = Capnp.importSystem('sandstorm/identity.capnp');
-  const SandstormHttpBridge =
-    Capnp.importSystem('sandstorm/sandstorm-http-bridge.capnp').SandstormHttpBridge;
+  const SandstormHttpBridge = Capnp.importSystem(
+    'sandstorm/sandstorm-http-bridge.capnp',
+  ).SandstormHttpBridge;
 
   let httpBridge = null;
   let capnpConnection = null;
 
   const bridgeConfig = Capnp.parse(
     Package.BridgeConfig,
-    fs.readFileSync('/sandstorm-http-bridge-config'));
+    fs.readFileSync('/sandstorm-http-bridge-config'),
+  );
 
   function getHttpBridge() {
     if (!httpBridge) {
@@ -54,9 +56,13 @@ if (isSandstorm && Meteor.isServer) {
       const parsedDescriptor = Capnp.parse(
         Powerbox.PowerboxDescriptor,
         new Buffer(descriptor, 'base64'),
-        { packed: true });
+        { packed: true },
+      );
 
-      const tag = Capnp.parse(Identity.Identity.PowerboxTag, parsedDescriptor.tags[0].value);
+      const tag = Capnp.parse(
+        Identity.Identity.PowerboxTag,
+        parsedDescriptor.tags[0].value,
+      );
       const permissions = [];
       if (tag.permissions[1]) {
         permissions.push('configure');
@@ -71,35 +77,43 @@ if (isSandstorm && Meteor.isServer) {
       const session = httpBridge.getSessionContext(sessionId).context;
       const api = httpBridge.getSandstormApi(sessionId).api;
 
-      Meteor.wrapAsync((done) => {
-        session.claimRequest(token).then((response) => {
-          const identity = response.cap.castAs(Identity.Identity);
-          const promises = [api.getIdentityId(identity), identity.getProfile(),
-            httpBridge.saveIdentity(identity)];
-          return Promise.all(promises).then((responses) => {
-            const identityId = responses[0].id.toString('hex').slice(0, 32);
-            const profile = responses[1].profile;
-            return profile.picture.getUrl().then((response) => {
-              const sandstormInfo = {
-                id: identityId,
-                name: profile.displayName.defaultText,
-                permissions,
-                picture: `${response.protocol}://${response.hostPath}`,
-                preferredHandle: profile.preferredHandle,
-                pronouns: profile.pronouns,
-              };
-
-              const login = Accounts.updateOrCreateUserFromExternalService(
-                'sandstorm', sandstormInfo,
-                { profile: { name: sandstormInfo.name } });
-
-              updateUserPermissions(login.userId, permissions);
-              done();
+      Meteor.wrapAsync(done => {
+        session
+          .claimRequest(token)
+          .then(response => {
+            const identity = response.cap.castAs(Identity.Identity);
+            const promises = [
+              api.getIdentityId(identity),
+              identity.getProfile(),
+              httpBridge.saveIdentity(identity),
+            ];
+            return Promise.all(promises).then(responses => {
+              const identityId = responses[0].id.toString('hex').slice(0, 32);
+              const profile = responses[1].profile;
+              return profile.picture.getUrl().then(response => {
+                const sandstormInfo = {
+                  id: identityId,
+                  name: profile.displayName.defaultText,
+                  permissions,
+                  picture: `${response.protocol}://${response.hostPath}`,
+                  preferredHandle: profile.preferredHandle,
+                  pronouns: profile.pronouns,
+                };
+
+                const login = Accounts.updateOrCreateUserFromExternalService(
+                  'sandstorm',
+                  sandstormInfo,
+                  { profile: { name: sandstormInfo.name } },
+                );
+
+                updateUserPermissions(login.userId, permissions);
+                done();
+              });
             });
+          })
+          .catch(e => {
+            done(e, null);
           });
-        }).catch((e) => {
-          done(e, null);
-        });
       })();
     },
   });
@@ -107,32 +121,39 @@ if (isSandstorm && Meteor.isServer) {
   function reportActivity(sessionId, path, type, users, caption) {
     const httpBridge = getHttpBridge();
     const session = httpBridge.getSessionContext(sessionId).context;
-    Meteor.wrapAsync((done) => {
-      return Promise.all(users.map((user) => {
-        return httpBridge.getSavedIdentity(user.id).then((response) => {
-          // Call getProfile() to make sure that the identity successfully resolves.
-          // (In C++ we would instead call whenResolved() here.)
-          const identity = response.identity;
-          return identity.getProfile().then(() => {
-            return { identity,
-              mentioned: !!user.mentioned,
-              subscribed: !!user.subscribed,
-            };
-          });
-        }).catch(() => {
-          // Ignore identities that fail to restore. Either they were added before we set
-          // `saveIdentityCaps` to true, or they have lost access to the board.
-        });
-      })).then((maybeUsers) => {
-        const users = maybeUsers.filter((u) => !!u);
-        const event = { path, type, users };
-        if (caption) {
-          event.notification = { caption };
-        }
+    Meteor.wrapAsync(done => {
+      return Promise.all(
+        users.map(user => {
+          return httpBridge
+            .getSavedIdentity(user.id)
+            .then(response => {
+              // Call getProfile() to make sure that the identity successfully resolves.
+              // (In C++ we would instead call whenResolved() here.)
+              const identity = response.identity;
+              return identity.getProfile().then(() => {
+                return {
+                  identity,
+                  mentioned: !!user.mentioned,
+                  subscribed: !!user.subscribed,
+                };
+              });
+            })
+            .catch(() => {
+              // Ignore identities that fail to restore. Either they were added before we set
+              // `saveIdentityCaps` to true, or they have lost access to the board.
+            });
+        }),
+      )
+        .then(maybeUsers => {
+          const users = maybeUsers.filter(u => !!u);
+          const event = { path, type, users };
+          if (caption) {
+            event.notification = { caption };
+          }
 
-        return session.activity(event);
-      }).then(() => done(),
-        (e) => done(e));
+          return session.activity(event);
+        })
+        .then(() => done(), e => done(e));
     })();
   }
 
@@ -146,7 +167,9 @@ if (isSandstorm && Meteor.isServer) {
 
         const eventTypes = bridgeConfig.viewInfo.eventTypes;
 
-        const defIdx = eventTypes.findIndex((def) => def.name === doc.activityType );
+        const defIdx = eventTypes.findIndex(
+          def => def.name === doc.activityType,
+        );
         if (defIdx >= 0) {
           const users = {};
           function ensureUserListed(userId) {
@@ -188,10 +211,14 @@ if (isSandstorm && Meteor.isServer) {
           if (doc.activityType === 'addComment') {
             const comment = CardComments.findOne(doc.commentId);
             caption = { defaultText: comment.text };
-            const activeMembers =
-              _.pluck(Boards.findOne(sandstormBoard._id).activeMembers(), 'userId');
-            (comment.text.match(/\B@([\w.]*)/g) || []).forEach((username) => {
-              const user = Meteor.users.findOne({ username: username.slice(1)});
+            const activeMembers = _.pluck(
+              Boards.findOne(sandstormBoard._id).activeMembers(),
+              'userId',
+            );
+            (comment.text.match(/\B@([\w.]*)/g) || []).forEach(username => {
+              const user = Meteor.users.findOne({
+                username: username.slice(1),
+              });
               if (user && activeMembers.indexOf(user._id) !== -1) {
                 mentionedUser(user._id);
               }
@@ -209,18 +236,22 @@ if (isSandstorm && Meteor.isServer) {
     const isAdmin = permissions.indexOf('configure') > -1;
     const isCommentOnly = false;
     const isNoComments = false;
-    const permissionDoc = { userId, isActive, isAdmin, isNoComments, isCommentOnly };
+    const permissionDoc = {
+      userId,
+      isActive,
+      isAdmin,
+      isNoComments,
+      isCommentOnly,
+    };
 
     const boardMembers = Boards.findOne(sandstormBoard._id).members;
     const memberIndex = _.pluck(boardMembers, 'userId').indexOf(userId);
 
     let modifier;
     if (memberIndex > -1)
-      modifier = { $set: { [`members.${memberIndex}`]: permissionDoc }};
-    else if (!isActive)
-      modifier = {};
-    else
-      modifier = { $push: { members: permissionDoc }};
+      modifier = { $set: { [`members.${memberIndex}`]: permissionDoc } };
+    else if (!isActive) modifier = {};
+    else modifier = { $push: { members: permissionDoc } };
 
     Boards.update(sandstormBoard._id, modifier);
   }
@@ -259,7 +290,7 @@ if (isSandstorm && Meteor.isServer) {
       });
       Activities.update(
         { activityTypeId: sandstormBoard._id },
-        { $set: { userId: doc._id }}
+        { $set: { userId: doc._id } },
       );
     }
 
@@ -274,10 +305,12 @@ if (isSandstorm && Meteor.isServer) {
 
     const username = doc.services.sandstorm.preferredHandle;
     let appendNumber = 0;
-    while (Users.findOne({
-      _id: { $ne: doc._id },
-      username: generateUniqueUsername(username, appendNumber),
-    })) {
+    while (
+      Users.findOne({
+        _id: { $ne: doc._id },
+        username: generateUniqueUsername(username, appendNumber),
+      })
+    ) {
       appendNumber += 1;
     }
 
@@ -321,40 +354,43 @@ if (isSandstorm && Meteor.isServer) {
   // is now handled by Sandstorm.
   // See https://github.com/wekan/wekan/issues/346
   Migrations.add('enforce-public-visibility-for-sandstorm', () => {
-    Boards.update('sandstorm', { $set: { permission: 'public' }});
+    Boards.update('sandstorm', { $set: { permission: 'public' } });
   });
 
   // Monkey patch to work around the problem described in
   // https://github.com/sandstorm-io/meteor-accounts-sandstorm/pull/31
   const _httpMethods = HTTP.methods;
-  HTTP.methods = (newMethods) => {
-    Object.keys(newMethods).forEach((key) =>  {
+  HTTP.methods = newMethods => {
+    Object.keys(newMethods).forEach(key => {
       if (newMethods[key].auth) {
         newMethods[key].auth = function() {
           const sandstormID = this.req.headers['x-sandstorm-user-id'];
-          const user = Meteor.users.findOne({'services.sandstorm.id': sandstormID});
+          const user = Meteor.users.findOne({
+            'services.sandstorm.id': sandstormID,
+          });
           return user && user._id;
         };
       }
     });
     _httpMethods(newMethods);
   };
-
 }
 
 if (isSandstorm && Meteor.isClient) {
   let rpcCounter = 0;
   const rpcs = {};
 
-  window.addEventListener('message', (event) => {
+  window.addEventListener('message', event => {
     if (event.source === window) {
       // Meteor likes to postmessage itself.
       return;
     }
 
-    if ((event.source !== window.parent) ||
-        typeof event.data !== 'object' ||
-        typeof event.data.rpcId !== 'number') {
+    if (
+      event.source !== window.parent ||
+      typeof event.data !== 'object' ||
+      typeof event.data.rpcId !== 'number'
+    ) {
       throw new Error(`got unexpected postMessage: ${event}`);
     }
 
@@ -374,7 +410,7 @@ if (isSandstorm && Meteor.isClient) {
     obj[name] = message;
     window.parent.postMessage(obj, '*');
     return new Promise((resolve, reject) => {
-      rpcs[id] = (response) => {
+      rpcs[id] = response => {
         if (response.error) {
           reject(new Error(response.error));
         } else {
@@ -401,16 +437,20 @@ if (isSandstorm && Meteor.isClient) {
   function doRequest(serializedPowerboxDescriptor, onSuccess) {
     return sendRpc('powerboxRequest', {
       query: [serializedPowerboxDescriptor],
-    }).then((response) => {
+    }).then(response => {
       if (!response.canceled) {
         onSuccess(response);
       }
     });
   }
 
-  window.sandstormRequestIdentity = function () {
-    doRequest(powerboxDescriptors.identity, (response) => {
-      Meteor.call('sandstormClaimIdentityRequest', response.token, response.descriptor);
+  window.sandstormRequestIdentity = function() {
+    doRequest(powerboxDescriptors.identity, response => {
+      Meteor.call(
+        'sandstormClaimIdentityRequest',
+        response.token,
+        response.descriptor,
+      );
     });
   };
 
@@ -422,9 +462,11 @@ if (isSandstorm && Meteor.isClient) {
     return window.parent.postMessage(msg, '*');
   }
 
-  FlowRouter.triggers.enter([({ path }) => {
-    updateSandstormMetaData({ setPath: path });
-  }]);
+  FlowRouter.triggers.enter([
+    ({ path }) => {
+      updateSandstormMetaData({ setPath: path });
+    },
+  ]);
 
   Tracker.autorun(() => {
     updateSandstormMetaData({ setTitle: DocHead.getTitle() });

+ 12 - 7
server/accounts-lockout.js

@@ -2,15 +2,20 @@
 // server
 import { AccountsLockout } from 'meteor/lucasantoniassi:accounts-lockout';
 
-(new AccountsLockout({
+new AccountsLockout({
   knownUsers: {
-    failuresBeforeLockout: process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE || 3,
+    failuresBeforeLockout:
+      process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE || 3,
     lockoutPeriod: process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD || 60,
-    failureWindow: process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW || 15,
+    failureWindow:
+      process.env.ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW || 15,
   },
   unknownUsers: {
-    failuresBeforeLockout: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE || 3,
-    lockoutPeriod: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD || 60,
-    failureWindow: process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW || 15,
+    failuresBeforeLockout:
+      process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE || 3,
+    lockoutPeriod:
+      process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD || 60,
+    failureWindow:
+      process.env.ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW || 15,
   },
-})).startup();
+}).startup();

+ 13 - 14
server/authentication.js

@@ -1,21 +1,20 @@
 import Fiber from 'fibers';
 
 Meteor.startup(() => {
-
   // Node Fibers 100% CPU usage issue
   // https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161
   // https://github.com/meteor/meteor/issues/9796#issuecomment-381676326
   // https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129
   Fiber.poolSize = 1e9;
 
-  Accounts.validateLoginAttempt(function (options) {
+  Accounts.validateLoginAttempt(function(options) {
     const user = options.user || {};
     return !user.loginDisabled;
   });
 
   Authentication = {};
 
-  Authentication.checkUserId = function (userId) {
+  Authentication.checkUserId = function(userId) {
     if (userId === undefined) {
       const error = new Meteor.Error('Unauthorized', 'Unauthorized');
       error.statusCode = 401;
@@ -28,13 +27,12 @@ Meteor.startup(() => {
       error.statusCode = 403;
       throw error;
     }
-
   };
 
   // This will only check if the user is logged in.
   // The authorization checks for the user will have to be done inside each API endpoint
   Authentication.checkLoggedIn = function(userId) {
-    if(userId === undefined) {
+    if (userId === undefined) {
       const error = new Meteor.Error('Unauthorized', 'Unauthorized');
       error.statusCode = 401;
       throw error;
@@ -44,7 +42,7 @@ Meteor.startup(() => {
   // An admin should be authorized to access everything, so we use a separate check for admins
   // This throws an error if otherReq is false and the user is not an admin
   Authentication.checkAdminOrCondition = function(userId, otherReq) {
-    if(otherReq) return;
+    if (otherReq) return;
     const admin = Users.findOne({ _id: userId, isAdmin: true });
     if (admin === undefined) {
       const error = new Meteor.Error('Forbidden', 'Forbidden');
@@ -58,14 +56,16 @@ Meteor.startup(() => {
     Authentication.checkLoggedIn(userId);
 
     const board = Boards.findOne({ _id: boardId });
-    const normalAccess = board.permission === 'public' || board.members.some((e) => e.userId === userId);
+    const normalAccess =
+      board.permission === 'public' ||
+      board.members.some(e => e.userId === userId);
     Authentication.checkAdminOrCondition(userId, normalAccess);
   };
 
   if (Meteor.isServer) {
-    if(process.env.OAUTH2_CLIENT_ID !== '') {
-
-      ServiceConfiguration.configurations.upsert( // eslint-disable-line no-undef
+    if (process.env.OAUTH2_CLIENT_ID !== '') {
+      ServiceConfiguration.configurations.upsert(
+        // eslint-disable-line no-undef
         { service: 'oidc' },
         {
           $set: {
@@ -76,15 +76,14 @@ Meteor.startup(() => {
             authorizationEndpoint: process.env.OAUTH2_AUTH_ENDPOINT,
             userinfoEndpoint: process.env.OAUTH2_USERINFO_ENDPOINT,
             tokenEndpoint: process.env.OAUTH2_TOKEN_ENDPOINT,
-            idTokenWhitelistFields: process.env.OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [],
+            idTokenWhitelistFields:
+              process.env.OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [],
             requestPermissions: process.env.OAUTH2_REQUEST_PERMISSIONS,
           },
           // OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [],
           // OAUTH2_REQUEST_PERMISSIONS || 'openid profile email',
-        }
+        },
       );
     }
   }
-
 });
-

+ 11 - 7
server/cors.js

@@ -1,23 +1,27 @@
 Meteor.startup(() => {
-
-  if ( process.env.CORS ) {
+  if (process.env.CORS) {
     // Listen to incoming HTTP requests, can only be used on the server
     WebApp.rawConnectHandlers.use(function(req, res, next) {
       res.setHeader('Access-Control-Allow-Origin', process.env.CORS);
       return next();
     });
   }
-  if ( process.env.CORS_ALLOW_HEADERS ) {
+  if (process.env.CORS_ALLOW_HEADERS) {
     WebApp.rawConnectHandlers.use(function(req, res, next) {
-      res.setHeader('Access-Control-Allow-Headers', process.env.CORS_ALLOW_HEADERS);
+      res.setHeader(
+        'Access-Control-Allow-Headers',
+        process.env.CORS_ALLOW_HEADERS,
+      );
       return next();
     });
   }
-  if ( process.env.CORS_EXPOSE_HEADERS ) {
+  if (process.env.CORS_EXPOSE_HEADERS) {
     WebApp.rawConnectHandlers.use(function(req, res, next) {
-      res.setHeader('Access-Control-Expose-Headers', process.env.CORS_EXPOSE_HEADERS);
+      res.setHeader(
+        'Access-Control-Expose-Headers',
+        process.env.CORS_EXPOSE_HEADERS,
+      );
       return next();
     });
   }
-
 });

+ 5 - 5
server/header-login.js

@@ -1,10 +1,10 @@
 Meteor.startup(() => {
-
-  if ( process.env.HEADER_LOGIN_ID ) {
+  if (process.env.HEADER_LOGIN_ID) {
     Meteor.settings.public.headerLoginId = process.env.HEADER_LOGIN_ID;
     Meteor.settings.public.headerLoginEmail = process.env.HEADER_LOGIN_EMAIL;
-    Meteor.settings.public.headerLoginFirstname = process.env.HEADER_LOGIN_FIRSTNAME;
-    Meteor.settings.public.headerLoginLastname = process.env.HEADER_LOGIN_LASTNAME;
+    Meteor.settings.public.headerLoginFirstname =
+      process.env.HEADER_LOGIN_FIRSTNAME;
+    Meteor.settings.public.headerLoginLastname =
+      process.env.HEADER_LOGIN_LASTNAME;
   }
-
 });

Some files were not shown because too many files changed in this diff