git push origin edgeMerge branch 'andresmanelli-edge' into edge

This commit is contained in:
Lauri Ojansivu 2019-03-20 21:24:45 +02:00
commit 6bc8d8dd56
12 changed files with 202 additions and 43 deletions

View file

@ -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

View file

@ -74,6 +74,8 @@ BlazeComponent.extendComponent({
lastLabel(){ lastLabel(){
const lastLabelId = this.currentData().labelId; const lastLabelId = this.currentData().labelId;
if (!lastLabelId)
return null;
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;
}, },

View file

@ -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; const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
card.move(card.swimlaneId, newListId, 0); card.move(boardId, swimlaneId, listId, 0);
Popup.close(); Popup.close();
}, },
}); });

View file

@ -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);
}, },

View file

@ -2,7 +2,7 @@ BlazeComponent.extendComponent({
customFields() { customFields() {
return CustomFields.find({ return CustomFields.find({
boardId: Session.get('currentBoard'), boardIds: {$in: [Session.get('currentBoard')]},
}); });
}, },
@ -103,7 +103,6 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
evt.preventDefault(); evt.preventDefault();
const data = { const data = {
boardId: Session.get('currentBoard'),
name: this.find('.js-field-name').value.trim(), name: this.find('.js-field-name').value.trim(),
type: this.type.get(), type: this.type.get(),
settings: this.getSettings(), settings: this.getSettings(),
@ -114,6 +113,7 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
// insert or update // insert or update
if (!this.data()._id) { if (!this.data()._id) {
data.boardIds = [Session.get('currentBoard')];
CustomFields.insert(data); CustomFields.insert(data);
} else { } else {
CustomFields.update(this.data()._id, {$set: data}); CustomFields.update(this.data()._id, {$set: data});
@ -122,8 +122,16 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
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; const customField = CustomFields.findOne(this._id);
CustomFields.remove(customFieldId); if (customField.boardIds.length > 1) {
CustomFields.update(customField._id, {
$pull: {
boardIds: Session.get('currentBoard'),
},
});
} else {
CustomFields.remove(customField._id);
}
Popup.close(); Popup.close();
}), }),
}]; }];

View file

@ -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

View file

@ -289,6 +289,16 @@ 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;
@ -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);
}); });
@ -476,7 +483,7 @@ Cards.helpers({
// get all definitions // get all definitions
const definitions = CustomFields.find({ const definitions = CustomFields.find({
boardId: this.boardId, boardIds: {$in: [this.boardId]},
}).fetch(); }).fetch();
// match right definition to each field // match right definition to each field
@ -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) { move(boardId, swimlaneId, listId, sort) {
const list = Lists.findOne(listId); // 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,
sort: sortIndex, 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) { function updateActivities(doc, fieldNames, modifier) {
if ((_.contains(fieldNames, 'listId') && doc.listId !== oldListId) || 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

View file

@ -4,11 +4,11 @@ CustomFields = new Mongo.Collection('customFields');
* A custom field on a card in the board * A custom field on a card in the board
*/ */
CustomFields.attachSchema(new SimpleSchema({ CustomFields.attachSchema(new SimpleSchema({
boardId: { boardIds: {
/** /**
* the ID of the board * the ID of the board
*/ */
type: String, type: [String],
}, },
name: { name: {
/** /**
@ -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}
); );
@ -135,7 +176,7 @@ if (Meteor.isServer) {
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;
JsonRoutes.sendResult(res, { JsonRoutes.sendResult(res, {
code: 200, code: 200,
data: CustomFields.find({ boardId: paramBoardId }).map(function (cf) { data: CustomFields.find({ boardIds: {$in: [paramBoardId]} }).map(function (cf) {
return { return {
_id: cf._id, _id: cf._id,
name: cf.name, name: cf.name,
@ -159,7 +200,7 @@ if (Meteor.isServer) {
const paramCustomFieldId = req.params.customFieldId; const paramCustomFieldId = req.params.customFieldId;
JsonRoutes.sendResult(res, { JsonRoutes.sendResult(res, {
code: 200, 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, 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, boardId: paramBoardId }); const customField = CustomFields.findOne({_id: id, boardIds: {$in: [paramBoardId]} });
customFieldCreation(req.body.authorId, customField); customFieldCreation(req.body.authorId, customField);
JsonRoutes.sendResult(res, { JsonRoutes.sendResult(res, {
@ -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: {

View file

@ -75,7 +75,7 @@ class Exporter {
result.lists = Lists.find(byBoard, noBoardId).fetch(); result.lists = Lists.find(byBoard, noBoardId).fetch();
result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch(); result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
result.swimlanes = Swimlanes.find(byBoard, 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.comments = CardComments.find(byBoard, noBoardId).fetch();
result.activities = Activities.find(byBoard, noBoardId).fetch(); result.activities = Activities.find(byBoard, noBoardId).fetch();
result.rules = Rules.find(byBoard, noBoardId).fetch(); result.rules = Rules.find(byBoard, noBoardId).fetch();

View file

@ -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);
}; };

View file

@ -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);
});
});

View file

@ -78,7 +78,7 @@ Meteor.publishRelations('board', function(boardId) {
this.cursor(Lists.find({ boardId })); this.cursor(Lists.find({ boardId }));
this.cursor(Swimlanes.find({ boardId })); this.cursor(Swimlanes.find({ boardId }));
this.cursor(Integrations.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 // Cards and cards comments
// XXX Originally we were publishing the card documents as a child of the // XXX Originally we were publishing the card documents as a child of the