Ver código fonte

- Fix card copy & move between boards with customFields
- Fix card copy & move between boards with labels with same name
- Fix activities for labels when copying and moving card
- Fix activities for customFields when copying and moving card

Andrés Manelli 6 anos atrás
pai
commit
d01fccd949

+ 8 - 1
client/components/activities/activities.jade

@@ -99,6 +99,9 @@ template(name="boardActivities")
           else
           else
             | {{{_ 'activity-added' memberLink cardLink}}}.
             | {{{_ 'activity-added' memberLink cardLink}}}.
 
 
+        if($eq activityType 'moveCardBoard')
+          | {{{_ 'activity-moved' cardLink oldBoardName boardName}}}.
+
         if($eq activityType 'moveCard')
         if($eq activityType 'moveCard')
           | {{{_ 'activity-moved' cardLink oldList.title list.title}}}.
           | {{{_ 'activity-moved' cardLink oldList.title list.title}}}.
 
 
@@ -135,7 +138,7 @@ template(name="cardActivities")
       p.activity-desc
       p.activity-desc
         +memberName(user=user)
         +memberName(user=user)
         if($eq activityType 'createCard')
         if($eq activityType 'createCard')
-          | {{_ 'activity-added' cardLabel list.title}}.
+          | {{_ 'activity-added' cardLabel listName}}.
         if($eq activityType 'importCard')
         if($eq activityType 'importCard')
           | {{{_ 'activity-imported' cardLabel list.title sourceLink}}}.
           | {{{_ 'activity-imported' cardLabel list.title sourceLink}}}.
         if($eq activityType 'joinMember')
         if($eq activityType 'joinMember')
@@ -176,6 +179,10 @@ template(name="cardActivities")
           | {{_ 'activity-sent' cardLabel boardLabel}}.
           | {{_ 'activity-sent' cardLabel boardLabel}}.
         if($eq activityType 'moveCard')
         if($eq activityType 'moveCard')
           | {{_ 'activity-moved' cardLabel oldList.title list.title}}.
           | {{_ 'activity-moved' cardLabel oldList.title list.title}}.
+
+        if($eq activityType 'moveCardBoard')
+          | {{{_ 'activity-moved' cardLink oldBoardName boardName}}}.
+
         if($eq activityType 'addAttachment')
         if($eq activityType 'addAttachment')
           | {{{_ 'activity-attached' attachmentLink cardLabel}}}.
           | {{{_ 'activity-attached' attachmentLink cardLabel}}}.
           if attachment.isImage
           if attachment.isImage

+ 8 - 0
client/components/activities/activities.js

@@ -74,6 +74,8 @@ BlazeComponent.extendComponent({
 
 
   lastLabel(){
   lastLabel(){
     const lastLabelId = this.currentData().labelId;
     const lastLabelId = this.currentData().labelId;
+    if (!lastLabelId)
+      return;
     const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(lastLabelId);
     const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(lastLabelId);
     if(lastLabel.name === undefined || lastLabel.name === ''){
     if(lastLabel.name === undefined || lastLabel.name === ''){
       return lastLabel.color;
       return lastLabel.color;
@@ -84,11 +86,15 @@ BlazeComponent.extendComponent({
 
 
   lastCustomField(){
   lastCustomField(){
     const lastCustomField = CustomFields.findOne(this.currentData().customFieldId);
     const lastCustomField = CustomFields.findOne(this.currentData().customFieldId);
+    if (!lastCustomField)
+      return null;
     return lastCustomField.name;
     return lastCustomField.name;
   },
   },
 
 
   lastCustomFieldValue(){
   lastCustomFieldValue(){
     const lastCustomField = CustomFields.findOne(this.currentData().customFieldId);
     const lastCustomField = CustomFields.findOne(this.currentData().customFieldId);
+    if (!lastCustomField)
+      return null;
     const value = this.currentData().value;
     const value = this.currentData().value;
     if (lastCustomField.settings.dropdownItems && lastCustomField.settings.dropdownItems.length > 0) {
     if (lastCustomField.settings.dropdownItems && lastCustomField.settings.dropdownItems.length > 0) {
       const dropDownValue = _.find(lastCustomField.settings.dropdownItems, (item) => {
       const dropDownValue = _.find(lastCustomField.settings.dropdownItems, (item) => {
@@ -135,6 +141,8 @@ BlazeComponent.extendComponent({
 
 
   customField() {
   customField() {
     const customField = this.currentData().customField();
     const customField = this.currentData().customField();
+    if (!customField)
+      return null;
     return customField.name;
     return customField.name;
   },
   },
 
 

+ 5 - 3
client/components/cards/cardDetails.js

@@ -412,11 +412,13 @@ Template.moveCardPopup.events({
     // XXX We should *not* get the currentCard from the global state, but
     // XXX We should *not* get the currentCard from the global state, but
     // instead from a “component” state.
     // instead from a “component” state.
     const card = Cards.findOne(Session.get('currentCard'));
     const card = Cards.findOne(Session.get('currentCard'));
+    const bSelect = $('.js-select-boards')[0];
+    const boardId = bSelect.options[bSelect.selectedIndex].value;
     const lSelect = $('.js-select-lists')[0];
     const lSelect = $('.js-select-lists')[0];
-    const newListId = lSelect.options[lSelect.selectedIndex].value;
+    const listId = lSelect.options[lSelect.selectedIndex].value;
     const slSelect = $('.js-select-swimlanes')[0];
     const slSelect = $('.js-select-swimlanes')[0];
-    card.swimlaneId = slSelect.options[slSelect.selectedIndex].value;
-    card.move(card.swimlaneId, newListId, 0);
+    const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
+    card.move(boardId, swimlaneId, listId, 0);
     Popup.close();
     Popup.close();
   },
   },
 });
 });

+ 2 - 2
client/components/lists/list.js

@@ -86,12 +86,12 @@ BlazeComponent.extendComponent({
 
 
         if (MultiSelection.isActive()) {
         if (MultiSelection.isActive()) {
           Cards.find(MultiSelection.getMongoSelector()).forEach((card, i) => {
           Cards.find(MultiSelection.getMongoSelector()).forEach((card, i) => {
-            card.move(swimlaneId, listId, sortIndex.base + i * sortIndex.increment);
+            card.move(currentBoard._id, swimlaneId, listId, sortIndex.base + i * sortIndex.increment);
           });
           });
         } else {
         } else {
           const cardDomElement = ui.item.get(0);
           const cardDomElement = ui.item.get(0);
           const card = Blaze.getData(cardDomElement);
           const card = Blaze.getData(cardDomElement);
-          card.move(swimlaneId, listId, sortIndex.base);
+          card.move(currentBoard._id, swimlaneId, listId, sortIndex.base);
         }
         }
         boardComponent.setIsDragging(false);
         boardComponent.setIsDragging(false);
       },
       },

+ 10 - 3
client/components/sidebar/sidebarCustomFields.js

@@ -116,15 +116,22 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
           data.boardIds = [Session.get('currentBoard')];
           data.boardIds = [Session.get('currentBoard')];
           CustomFields.insert(data);
           CustomFields.insert(data);
         } else {
         } else {
-          data.boardIds = {$in: [Session.get('currentBoard')]};
           CustomFields.update(this.data()._id, {$set: data});
           CustomFields.update(this.data()._id, {$set: data});
         }
         }
 
 
         Popup.back();
         Popup.back();
       },
       },
       'click .js-delete-custom-field': Popup.afterConfirm('deleteCustomField', function() {
       'click .js-delete-custom-field': Popup.afterConfirm('deleteCustomField', function() {
-        const customFieldId = this._id;
-        CustomFields.remove(customFieldId);
+        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();
         Popup.close();
       }),
       }),
     }];
     }];

+ 1 - 1
models/boards.js

@@ -466,7 +466,7 @@ Boards.helpers({
   },
   },
 
 
   customFields() {
   customFields() {
-    return CustomFields.find({ boardId: this._id }, { sort: { name: 1 } });
+    return CustomFields.find({ boardIds: {$in: [this._id]} }, { sort: { name: 1 } });
   },
   },
 
 
   // XXX currently mutations return no value so we have an issue when using addLabel in import
   // XXX currently mutations return no value so we have an issue when using addLabel in import

+ 88 - 14
models/cards.js

@@ -289,9 +289,19 @@ Cards.helpers({
     const oldId = this._id;
     const oldId = this._id;
     const oldCard = Cards.findOne(oldId);
     const oldCard = Cards.findOne(oldId);
 
 
+    // Copy Custom Fields
+    if (oldBoard._id !== boardId) {
+      CustomFields.find({
+        _id: {$in: oldCard.customFields.map((cf) => { return cf._id; })},
+      }).forEach((cf) => {
+        if (!_.contains(cf.boardIds, boardId))
+          cf.addBoard(boardId);
+      });
+    }
+
     delete this._id;
     delete this._id;
     delete this.labelIds;
     delete this.labelIds;
-    this.labelIds= newCardLabels;
+    this.labelIds = newCardLabels;
     this.boardId = boardId;
     this.boardId = boardId;
     this.swimlaneId = swimlaneId;
     this.swimlaneId = swimlaneId;
     this.listId = listId;
     this.listId = listId;
@@ -306,7 +316,6 @@ Cards.helpers({
 
 
     // copy checklists
     // copy checklists
     Checklists.find({cardId: oldId}).forEach((ch) => {
     Checklists.find({cardId: oldId}).forEach((ch) => {
-      // REMOVE verify copy with arguments
       ch.copy(_id);
       ch.copy(_id);
     });
     });
 
 
@@ -314,13 +323,11 @@ Cards.helpers({
     Cards.find({parentId: oldId}).forEach((subtask) => {
     Cards.find({parentId: oldId}).forEach((subtask) => {
       subtask.parentId = _id;
       subtask.parentId = _id;
       subtask._id = null;
       subtask._id = null;
-      // REMOVE verify copy with arguments instead of insert?
       Cards.insert(subtask);
       Cards.insert(subtask);
     });
     });
 
 
     // copy card comments
     // copy card comments
     CardComments.find({cardId: oldId}).forEach((cmt) => {
     CardComments.find({cardId: oldId}).forEach((cmt) => {
-      // REMOVE verify copy with arguments
       cmt.copy(_id);
       cmt.copy(_id);
     });
     });
 
 
@@ -485,6 +492,9 @@ Cards.helpers({
       const definition = definitions.find((definition) => {
       const definition = definitions.find((definition) => {
         return definition._id === customField._id;
         return definition._id === customField._id;
       });
       });
+      if (!definition) {
+        return {};
+      }
       //search for "True Value" which is for DropDowns other then the Value (which is the id)
       //search for "True Value" which is for DropDowns other then the Value (which is the id)
       let trueValue = customField.value;
       let trueValue = customField.value;
       if (definition.settings.dropdownItems && definition.settings.dropdownItems.length > 0) {
       if (definition.settings.dropdownItems && definition.settings.dropdownItems.length > 0) {
@@ -1054,18 +1064,41 @@ Cards.mutations({
     };
     };
   },
   },
 
 
-  move(swimlaneId, listId, sortIndex) {
-    const list = Lists.findOne(listId);
+  move(boardId, swimlaneId, listId, sort) {
+    // Copy Custom Fields
+    if (this.boardId !== boardId) {
+      CustomFields.find({
+        _id: {$in: this.customFields.map((cf) => { return cf._id; })},
+      }).forEach((cf) => {
+        if (!_.contains(cf.boardIds, boardId))
+          cf.addBoard(boardId);
+      });
+    }
+
+    // Get label names
+    const oldBoard = Boards.findOne(this.boardId);
+    const oldBoardLabels = oldBoard.labels;
+    const oldCardLabels = _.pluck(_.filter(oldBoardLabels, (label) => {
+      return _.contains(this.labelIds, label._id);
+    }), 'name');
+
+    const newBoard = Boards.findOne(boardId);
+    const newBoardLabels = newBoard.labels;
+    const newCardLabelIds = _.pluck(_.filter(newBoardLabels, (label) => {
+      return label.name && _.contains(oldCardLabels, label.name);
+    }), '_id');
+
     const mutatedFields = {
     const mutatedFields = {
+      boardId,
       swimlaneId,
       swimlaneId,
       listId,
       listId,
-      boardId: list.boardId,
-      sort: sortIndex,
+      sort,
+      labelIds: newCardLabelIds,
     };
     };
 
 
-    return {
+    Cards.update(this._id, {
       $set: mutatedFields,
       $set: mutatedFields,
-    };
+    });
   },
   },
 
 
   addLabel(labelId) {
   addLabel(labelId) {
@@ -1287,8 +1320,47 @@ Cards.mutations({
 
 
 //FUNCTIONS FOR creation of Activities
 //FUNCTIONS FOR creation of Activities
 
 
-function cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId) {
-  if ((_.contains(fieldNames, 'listId') && doc.listId !== oldListId) ||
+function updateActivities(doc, fieldNames, modifier) {
+  if (_.contains(fieldNames, 'labelIds') && _.contains(fieldNames, 'boardId')) {
+    Activities.find({
+      activityType: 'addedLabel',
+      cardId: doc._id,
+    }).forEach((a) => {
+      const lidx = doc.labelIds.indexOf(a.labelId);
+      if (lidx !== -1 && modifier.$set.labelIds.length > lidx) {
+        Activities.update(a._id, {
+          $set: {
+            labelId: modifier.$set.labelIds[doc.labelIds.indexOf(a.labelId)],
+            boardId: modifier.$set.boardId,
+          }
+        });
+      } else {
+        Activities.remove(a._id);
+      }
+    });
+  } else if (_.contains(fieldNames, 'boardId')) {
+    Activities.remove({
+      activityType: 'addedLabel',
+      cardId: doc._id,
+    });
+  }
+}
+
+function cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId, oldBoardId) {
+  if (_.contains(fieldNames, 'boardId') && (doc.boardId !== oldBoardId)) {
+    Activities.insert({
+      userId,
+      activityType: 'moveCardBoard',
+      boardName: Boards.findOne(doc.boardId).title,
+      boardId: doc.boardId,
+      oldBoardId,
+      oldBoardName: Boards.findOne(oldBoardId).title,
+      cardId: doc._id,
+      swimlaneName: Swimlanes.findOne(doc.swimlaneId).title,
+      swimlaneId: doc.swimlaneId,
+      oldSwimlaneId,
+    });
+  } else if ((_.contains(fieldNames, 'listId') && doc.listId !== oldListId) ||
     (_.contains(fieldNames, 'swimlaneId') && doc.swimlaneId !== oldSwimlaneId)){
     (_.contains(fieldNames, 'swimlaneId') && doc.swimlaneId !== oldSwimlaneId)){
     Activities.insert({
     Activities.insert({
       userId,
       userId,
@@ -1435,7 +1507,7 @@ function cardCustomFields(userId, doc, fieldNames, modifier) {
 
 
         // only individual changes are registered
         // only individual changes are registered
         if (dotNotation.length > 1) {
         if (dotNotation.length > 1) {
-          const customFieldId = doc.customFields[dot_notation[1]]._id;
+          const customFieldId = doc.customFields[dotNotation[1]]._id;
           const act = {
           const act = {
             userId,
             userId,
             customFieldId,
             customFieldId,
@@ -1508,12 +1580,14 @@ if (Meteor.isServer) {
   Cards.after.update(function(userId, doc, fieldNames) {
   Cards.after.update(function(userId, doc, fieldNames) {
     const oldListId = this.previous.listId;
     const oldListId = this.previous.listId;
     const oldSwimlaneId = this.previous.swimlaneId;
     const oldSwimlaneId = this.previous.swimlaneId;
-    cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId);
+    const oldBoardId = this.previous.boardId;
+    cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId, oldBoardId);
   });
   });
 
 
   // Add a new activity if we add or remove a member to the card
   // Add a new activity if we add or remove a member to the card
   Cards.before.update((userId, doc, fieldNames, modifier) => {
   Cards.before.update((userId, doc, fieldNames, modifier) => {
     cardMembers(userId, doc, fieldNames, modifier);
     cardMembers(userId, doc, fieldNames, modifier);
+    updateActivities(doc, fieldNames, modifier);
   });
   });
 
 
   // Add a new activity if we add or remove a label to the card
   // Add a new activity if we add or remove a label to the card

+ 51 - 10
models/customFields.js

@@ -72,17 +72,37 @@ CustomFields.attachSchema(new SimpleSchema({
   },
   },
 }));
 }));
 
 
+CustomFields.mutations({
+  addBoard(boardId) {
+    if (boardId) {
+      return {
+        $push: {
+          boardIds: boardId,
+        },
+      };
+    } else {
+      return null;
+    }
+  },
+});
+
 CustomFields.allow({
 CustomFields.allow({
   insert(userId, doc) {
   insert(userId, doc) {
-    return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
+    return allowIsAnyBoardMember(userId, Boards.find({
+      _id: {$in: doc.boardIds},
+    }).fetch());
   },
   },
   update(userId, doc) {
   update(userId, doc) {
-    return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
+    return allowIsAnyBoardMember(userId, Boards.find({
+      _id: {$in: doc.boardIds},
+    }).fetch());
   },
   },
   remove(userId, doc) {
   remove(userId, doc) {
-    return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
+    return allowIsAnyBoardMember(userId, Boards.find({
+      _id: {$in: doc.boardIds},
+    }).fetch());
   },
   },
-  fetch: ['userId', 'boardId'],
+  fetch: ['userId', 'boardIds'],
 });
 });
 
 
 // not sure if we need this?
 // not sure if we need this?
@@ -92,27 +112,48 @@ function customFieldCreation(userId, doc){
   Activities.insert({
   Activities.insert({
     userId,
     userId,
     activityType: 'createCustomField',
     activityType: 'createCustomField',
-    boardId: doc.boardId,
+    boardId: doc.boardIds[0], // We are creating a customField, it has only one boardId
     customFieldId: doc._id,
     customFieldId: doc._id,
   });
   });
 }
 }
 
 
 if (Meteor.isServer) {
 if (Meteor.isServer) {
   Meteor.startup(() => {
   Meteor.startup(() => {
-    CustomFields._collection._ensureIndex({ boardId: 1 });
+    CustomFields._collection._ensureIndex({ boardIds: 1 });
   });
   });
 
 
   CustomFields.after.insert((userId, doc) => {
   CustomFields.after.insert((userId, doc) => {
     customFieldCreation(userId, doc);
     customFieldCreation(userId, doc);
   });
   });
 
 
-  CustomFields.after.remove((userId, doc) => {
+  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}
+      );
+      Activities.remove({
+        customFieldId: doc._id,
+        boardId: modifier.$pull.boardIds,
+      });
+    } else if (_.contains(fieldNames, 'boardIds') && modifier.$push) {
+      Activities.insert({
+        userId,
+        activityType: 'createCustomField',
+        boardId: modifier.$push.boardIds,
+        customFieldId: doc._id,
+      });
+    }
+  });
+
+  CustomFields.before.remove((userId, doc) => {
     Activities.remove({
     Activities.remove({
       customFieldId: doc._id,
       customFieldId: doc._id,
     });
     });
 
 
     Cards.update(
     Cards.update(
-      {'boardId': doc.boardId, 'customFields._id': doc._id},
+      {boardId: {$in: doc.boardIds}, 'customFields._id': doc._id},
       {$pull: {'customFields': {'_id': doc._id}}},
       {$pull: {'customFields': {'_id': doc._id}}},
       {multi: true}
       {multi: true}
     );
     );
@@ -186,7 +227,7 @@ if (Meteor.isServer) {
       showOnCard: req.body.showOnCard,
       showOnCard: req.body.showOnCard,
       automaticallyOnCard: req.body.automaticallyOnCard,
       automaticallyOnCard: req.body.automaticallyOnCard,
       showLabelOnMiniCard: req.body.showLabelOnMiniCard,
       showLabelOnMiniCard: req.body.showLabelOnMiniCard,
-      boardId: paramBoardId,
+      boardIds: {$in: [paramBoardId]},
     });
     });
 
 
     const customField = CustomFields.findOne({_id: id, boardIds: {$in: [paramBoardId]} });
     const customField = CustomFields.findOne({_id: id, boardIds: {$in: [paramBoardId]} });
@@ -214,7 +255,7 @@ if (Meteor.isServer) {
     Authentication.checkUserId( req.userId);
     Authentication.checkUserId( req.userId);
     const paramBoardId = req.params.boardId;
     const paramBoardId = req.params.boardId;
     const id = req.params.customFieldId;
     const id = req.params.customFieldId;
-    CustomFields.remove({ _id: id, boardId: paramBoardId });
+    CustomFields.remove({ _id: id, boardIds: {$in: [paramBoardId]} });
     JsonRoutes.sendResult(res, {
     JsonRoutes.sendResult(res, {
       code: 200,
       code: 200,
       data: {
       data: {

+ 6 - 0
server/lib/utils.js

@@ -6,6 +6,12 @@ allowIsBoardMember = function(userId, board) {
   return board && board.hasMember(userId);
   return board && board.hasMember(userId);
 };
 };
 
 
+allowIsAnyBoardMember = function(userId, boards) {
+  return _.some(boards, (board) => {
+    return board && board.hasMember(userId);
+  });
+};
+
 allowIsBoardMemberCommentOnly = function(userId, board) {
 allowIsBoardMemberCommentOnly = function(userId, board) {
   return board && board.hasMember(userId) && !board.hasCommentOnly(userId);
   return board && board.hasMember(userId) && !board.hasCommentOnly(userId);
 };
 };