summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLauri Ojansivu <x@xet7.org>2019-03-20 19:30:44 +0200
committerLauri Ojansivu <x@xet7.org>2019-03-20 19:30:44 +0200
commitdce89bcfa84106ecb18ba65247d13003f9aac660 (patch)
treef4ccebb1fbd74981e00ca759c177ee57a5c9ee0e
parente5370ce859a0d18f2d4a94216e9baf808062c617 (diff)
parent777d9ac35320cbfdd8d90136b176d4a3e69e74be (diff)
downloadwekan-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.jade9
-rw-r--r--client/components/activities/activities.js8
-rw-r--r--client/components/cards/cardDetails.js8
-rw-r--r--client/components/lists/list.js4
-rw-r--r--client/components/sidebar/sidebarCustomFields.js16
-rw-r--r--models/boards.js2
-rw-r--r--models/cards.js104
-rw-r--r--models/customFields.js71
-rw-r--r--models/export.js2
-rw-r--r--server/lib/utils.js6
-rw-r--r--server/migrations.js13
-rw-r--r--server/publications/boards.js2
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