diff options
author | Lauri Ojansivu <x@xet7.org> | 2019-03-20 19:30:44 +0200 |
---|---|---|
committer | Lauri Ojansivu <x@xet7.org> | 2019-03-20 19:30:44 +0200 |
commit | dce89bcfa84106ecb18ba65247d13003f9aac660 (patch) | |
tree | f4ccebb1fbd74981e00ca759c177ee57a5c9ee0e | |
parent | e5370ce859a0d18f2d4a94216e9baf808062c617 (diff) | |
parent | 777d9ac35320cbfdd8d90136b176d4a3e69e74be (diff) | |
download | wekan-dce89bcfa84106ecb18ba65247d13003f9aac660.tar.gz wekan-dce89bcfa84106ecb18ba65247d13003f9aac660.tar.bz2 wekan-dce89bcfa84106ecb18ba65247d13003f9aac660.zip |
Merge branch 'edge' of https://github.com/andresmanelli/wekan into andresmanelli-edge
-rw-r--r-- | client/components/activities/activities.jade | 9 | ||||
-rw-r--r-- | client/components/activities/activities.js | 8 | ||||
-rw-r--r-- | client/components/cards/cardDetails.js | 8 | ||||
-rw-r--r-- | client/components/lists/list.js | 4 | ||||
-rw-r--r-- | client/components/sidebar/sidebarCustomFields.js | 16 | ||||
-rw-r--r-- | models/boards.js | 2 | ||||
-rw-r--r-- | models/cards.js | 104 | ||||
-rw-r--r-- | models/customFields.js | 71 | ||||
-rw-r--r-- | models/export.js | 2 | ||||
-rw-r--r-- | server/lib/utils.js | 6 | ||||
-rw-r--r-- | server/migrations.js | 13 | ||||
-rw-r--r-- | server/publications/boards.js | 2 |
12 files changed, 202 insertions, 43 deletions
diff --git a/client/components/activities/activities.jade b/client/components/activities/activities.jade index 949400f6..54066da8 100644 --- a/client/components/activities/activities.jade +++ b/client/components/activities/activities.jade @@ -99,6 +99,9 @@ template(name="boardActivities") else | {{{_ 'activity-added' memberLink cardLink}}}. + if($eq activityType 'moveCardBoard') + | {{{_ 'activity-moved' cardLink oldBoardName boardName}}}. + if($eq activityType 'moveCard') | {{{_ 'activity-moved' cardLink oldList.title list.title}}}. @@ -135,7 +138,7 @@ template(name="cardActivities") p.activity-desc +memberName(user=user) if($eq activityType 'createCard') - | {{_ 'activity-added' cardLabel list.title}}. + | {{_ 'activity-added' cardLabel listName}}. if($eq activityType 'importCard') | {{{_ 'activity-imported' cardLabel list.title sourceLink}}}. if($eq activityType 'joinMember') @@ -176,6 +179,10 @@ template(name="cardActivities") | {{_ 'activity-sent' cardLabel boardLabel}}. if($eq activityType 'moveCard') | {{_ 'activity-moved' cardLabel oldList.title list.title}}. + + if($eq activityType 'moveCardBoard') + | {{{_ 'activity-moved' cardLink oldBoardName boardName}}}. + if($eq activityType 'addAttachment') | {{{_ 'activity-attached' attachmentLink cardLabel}}}. if attachment.isImage diff --git a/client/components/activities/activities.js b/client/components/activities/activities.js index 81995221..0476897f 100644 --- a/client/components/activities/activities.js +++ b/client/components/activities/activities.js @@ -74,6 +74,8 @@ BlazeComponent.extendComponent({ 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 === ''){ return lastLabel.color; @@ -84,11 +86,15 @@ BlazeComponent.extendComponent({ 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; const value = this.currentData().value; if (lastCustomField.settings.dropdownItems && lastCustomField.settings.dropdownItems.length > 0) { const dropDownValue = _.find(lastCustomField.settings.dropdownItems, (item) => { @@ -135,6 +141,8 @@ BlazeComponent.extendComponent({ customField() { const customField = this.currentData().customField(); + if (!customField) + return null; return customField.name; }, diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index d27fe732..79e9e311 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -412,11 +412,13 @@ Template.moveCardPopup.events({ // XXX We should *not* get the currentCard from the global state, but // instead from a “component” state. 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 newListId = lSelect.options[lSelect.selectedIndex].value; + const listId = lSelect.options[lSelect.selectedIndex].value; 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(); }, }); diff --git a/client/components/lists/list.js b/client/components/lists/list.js index 868be2ce..cceae7b4 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -86,12 +86,12 @@ BlazeComponent.extendComponent({ if (MultiSelection.isActive()) { 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 { const cardDomElement = ui.item.get(0); const card = Blaze.getData(cardDomElement); - card.move(swimlaneId, listId, sortIndex.base); + card.move(currentBoard._id, swimlaneId, listId, sortIndex.base); } boardComponent.setIsDragging(false); }, diff --git a/client/components/sidebar/sidebarCustomFields.js b/client/components/sidebar/sidebarCustomFields.js index ccc8ffb9..28af973b 100644 --- a/client/components/sidebar/sidebarCustomFields.js +++ b/client/components/sidebar/sidebarCustomFields.js @@ -2,7 +2,7 @@ BlazeComponent.extendComponent({ customFields() { return CustomFields.find({ - boardId: Session.get('currentBoard'), + boardIds: {$in: [Session.get('currentBoard')]}, }); }, @@ -103,7 +103,6 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ evt.preventDefault(); const data = { - boardId: Session.get('currentBoard'), name: this.find('.js-field-name').value.trim(), type: this.type.get(), settings: this.getSettings(), @@ -114,6 +113,7 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ // insert or update if (!this.data()._id) { + data.boardIds = [Session.get('currentBoard')]; CustomFields.insert(data); } else { CustomFields.update(this.data()._id, {$set: data}); @@ -122,8 +122,16 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ Popup.back(); }, '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(); }), }]; diff --git a/models/boards.js b/models/boards.js index 9a71ede8..99ac8e6e 100644 --- a/models/boards.js +++ b/models/boards.js @@ -466,7 +466,7 @@ Boards.helpers({ }, 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 diff --git a/models/cards.js b/models/cards.js index c3bae400..2bf83825 100644 --- a/models/cards.js +++ b/models/cards.js @@ -289,9 +289,19 @@ Cards.helpers({ const oldId = this._id; 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.labelIds; - this.labelIds= newCardLabels; + this.labelIds = newCardLabels; this.boardId = boardId; this.swimlaneId = swimlaneId; this.listId = listId; @@ -306,7 +316,6 @@ Cards.helpers({ // copy checklists Checklists.find({cardId: oldId}).forEach((ch) => { - // REMOVE verify copy with arguments ch.copy(_id); }); @@ -314,13 +323,11 @@ Cards.helpers({ Cards.find({parentId: oldId}).forEach((subtask) => { subtask.parentId = _id; subtask._id = null; - // REMOVE verify copy with arguments instead of insert? Cards.insert(subtask); }); // copy card comments CardComments.find({cardId: oldId}).forEach((cmt) => { - // REMOVE verify copy with arguments cmt.copy(_id); }); @@ -476,7 +483,7 @@ Cards.helpers({ // get all definitions const definitions = CustomFields.find({ - boardId: this.boardId, + boardIds: {$in: [this.boardId]}, }).fetch(); // match right definition to each field @@ -485,6 +492,9 @@ Cards.helpers({ const definition = definitions.find((definition) => { return definition._id === customField._id; }); + if (!definition) { + return {}; + } //search for "True Value" which is for DropDowns other then the Value (which is the id) let trueValue = customField.value; 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 = { + boardId, swimlaneId, listId, - boardId: list.boardId, - sort: sortIndex, + sort, + labelIds: newCardLabelIds, }; - return { + Cards.update(this._id, { $set: mutatedFields, - }; + }); }, addLabel(labelId) { @@ -1287,8 +1320,47 @@ Cards.mutations({ //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)){ Activities.insert({ userId, @@ -1435,7 +1507,7 @@ function cardCustomFields(userId, doc, fieldNames, modifier) { // only individual changes are registered if (dotNotation.length > 1) { - const customFieldId = doc.customFields[dot_notation[1]]._id; + const customFieldId = doc.customFields[dotNotation[1]]._id; const act = { userId, customFieldId, @@ -1508,12 +1580,14 @@ if (Meteor.isServer) { Cards.after.update(function(userId, doc, fieldNames) { const oldListId = this.previous.listId; 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 Cards.before.update((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 diff --git a/models/customFields.js b/models/customFields.js index b7ad5467..a67ddb7d 100644 --- a/models/customFields.js +++ b/models/customFields.js @@ -4,11 +4,11 @@ CustomFields = new Mongo.Collection('customFields'); * A custom field on a card in the board */ CustomFields.attachSchema(new SimpleSchema({ - boardId: { + boardIds: { /** * the ID of the board */ - type: String, + type: [String], }, name: { /** @@ -72,17 +72,37 @@ CustomFields.attachSchema(new SimpleSchema({ }, })); +CustomFields.mutations({ + addBoard(boardId) { + if (boardId) { + return { + $push: { + boardIds: boardId, + }, + }; + } else { + return null; + } + }, +}); + CustomFields.allow({ insert(userId, doc) { - return allowIsBoardMember(userId, Boards.findOne(doc.boardId)); + return allowIsAnyBoardMember(userId, Boards.find({ + _id: {$in: doc.boardIds}, + }).fetch()); }, update(userId, doc) { - return allowIsBoardMember(userId, Boards.findOne(doc.boardId)); + return allowIsAnyBoardMember(userId, Boards.find({ + _id: {$in: doc.boardIds}, + }).fetch()); }, 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? @@ -92,27 +112,48 @@ function customFieldCreation(userId, doc){ Activities.insert({ userId, activityType: 'createCustomField', - boardId: doc.boardId, + boardId: doc.boardIds[0], // We are creating a customField, it has only one boardId customFieldId: doc._id, }); } if (Meteor.isServer) { Meteor.startup(() => { - CustomFields._collection._ensureIndex({ boardId: 1 }); + CustomFields._collection._ensureIndex({ boardIds: 1 }); }); CustomFields.after.insert((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({ customFieldId: doc._id, }); Cards.update( - {'boardId': doc.boardId, 'customFields._id': doc._id}, + {boardId: {$in: doc.boardIds}, 'customFields._id': doc._id}, {$pull: {'customFields': {'_id': doc._id}}}, {multi: true} ); @@ -135,7 +176,7 @@ if (Meteor.isServer) { const paramBoardId = req.params.boardId; JsonRoutes.sendResult(res, { code: 200, - data: CustomFields.find({ boardId: paramBoardId }).map(function (cf) { + data: CustomFields.find({ boardIds: {$in: [paramBoardId]} }).map(function (cf) { return { _id: cf._id, name: cf.name, @@ -159,7 +200,7 @@ if (Meteor.isServer) { const paramCustomFieldId = req.params.customFieldId; JsonRoutes.sendResult(res, { code: 200, - data: CustomFields.findOne({ _id: paramCustomFieldId, boardId: paramBoardId }), + data: CustomFields.findOne({ _id: paramCustomFieldId, boardIds: {$in: [paramBoardId]} }), }); }); @@ -186,10 +227,10 @@ if (Meteor.isServer) { showOnCard: req.body.showOnCard, automaticallyOnCard: req.body.automaticallyOnCard, showLabelOnMiniCard: req.body.showLabelOnMiniCard, - boardId: paramBoardId, + boardIds: {$in: [paramBoardId]}, }); - const customField = CustomFields.findOne({_id: id, boardId: paramBoardId }); + const customField = CustomFields.findOne({_id: id, boardIds: {$in: [paramBoardId]} }); customFieldCreation(req.body.authorId, customField); JsonRoutes.sendResult(res, { @@ -214,7 +255,7 @@ if (Meteor.isServer) { Authentication.checkUserId( req.userId); const paramBoardId = req.params.boardId; const id = req.params.customFieldId; - CustomFields.remove({ _id: id, boardId: paramBoardId }); + CustomFields.remove({ _id: id, boardIds: {$in: [paramBoardId]} }); JsonRoutes.sendResult(res, { code: 200, data: { diff --git a/models/export.js b/models/export.js index 76f2da06..f281b34a 100644 --- a/models/export.js +++ b/models/export.js @@ -75,7 +75,7 @@ class Exporter { 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(byBoard, noBoardId).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(); diff --git a/server/lib/utils.js b/server/lib/utils.js index ee925847..3b4412fe 100644 --- a/server/lib/utils.js +++ b/server/lib/utils.js @@ -6,6 +6,12 @@ allowIsBoardMember = function(userId, board) { return board && board.hasMember(userId); }; +allowIsAnyBoardMember = function(userId, boards) { + return _.some(boards, (board) => { + return board && board.hasMember(userId); + }); +}; + allowIsBoardMemberCommentOnly = function(userId, board) { return board && board.hasMember(userId) && !board.hasCommentOnly(userId); }; diff --git a/server/migrations.js b/server/migrations.js index b8915fe9..09852495 100644 --- a/server/migrations.js +++ b/server/migrations.js @@ -525,3 +525,16 @@ Migrations.add('fix-circular-reference_', () => { } }); }); + +Migrations.add('mutate-boardIds-in-customfields', () => { + CustomFields.find().forEach((cf) => { + CustomFields.update(cf, { + $set: { + boardIds: [cf.boardId], + }, + $unset: { + boardId: '', + }, + }, noValidateMulti); + }); +}); diff --git a/server/publications/boards.js b/server/publications/boards.js index 18c44d2b..6d9d2b9e 100644 --- a/server/publications/boards.js +++ b/server/publications/boards.js @@ -78,7 +78,7 @@ Meteor.publishRelations('board', function(boardId) { this.cursor(Lists.find({ boardId })); this.cursor(Swimlanes.find({ boardId })); this.cursor(Integrations.find({ boardId })); - this.cursor(CustomFields.find({ boardId }, { sort: { name: 1 } })); + this.cursor(CustomFields.find({ boardIds: {$in: [boardId]} }, { sort: { name: 1 } })); // Cards and cards comments // XXX Originally we were publishing the card documents as a child of the |