|
@@ -201,6 +201,14 @@ Cards.attachSchema(
|
|
optional: true,
|
|
optional: true,
|
|
defaultValue: [],
|
|
defaultValue: [],
|
|
},
|
|
},
|
|
|
|
+ assignees: {
|
|
|
|
+ /**
|
|
|
|
+ * list of assignees (user IDs) who are responsible for completing card
|
|
|
|
+ */
|
|
|
|
+ type: [String],
|
|
|
|
+ optional: true,
|
|
|
|
+ defaultValue: [],
|
|
|
|
+ },
|
|
receivedAt: {
|
|
receivedAt: {
|
|
/**
|
|
/**
|
|
* Date the card was received
|
|
* Date the card was received
|
|
@@ -409,6 +417,10 @@ Cards.helpers({
|
|
return _.contains(this.getMembers(), memberId);
|
|
return _.contains(this.getMembers(), memberId);
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ isAssignee(assigneeId) {
|
|
|
|
+ return _.contains(this.getAssignee(), assigneeId);
|
|
|
|
+ },
|
|
|
|
+
|
|
activities() {
|
|
activities() {
|
|
if (this.isLinkedCard()) {
|
|
if (this.isLinkedCard()) {
|
|
return Activities.find(
|
|
return Activities.find(
|
|
@@ -743,6 +755,20 @@ Cards.helpers({
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ getAssignees() {
|
|
|
|
+ if (this.isLinkedCard()) {
|
|
|
|
+ const card = Cards.findOne({ _id: this.linkedId });
|
|
|
|
+ return card.assignees;
|
|
|
|
+ } else if (this.isLinkedBoard()) {
|
|
|
|
+ const board = Boards.findOne({ _id: this.linkedId });
|
|
|
|
+ return board.activeAssignees().map(assignee => {
|
|
|
|
+ return assignee.userId;
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ return this.assignees;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
assignMember(memberId) {
|
|
assignMember(memberId) {
|
|
if (this.isLinkedCard()) {
|
|
if (this.isLinkedCard()) {
|
|
return Cards.update(
|
|
return Cards.update(
|
|
@@ -760,6 +786,23 @@ Cards.helpers({
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ assignAssignee(assigneeId) {
|
|
|
|
+ if (this.isLinkedCard()) {
|
|
|
|
+ return Cards.update(
|
|
|
|
+ { _id: this.linkedId },
|
|
|
|
+ { $addToSet: { assignees: assigneeId } },
|
|
|
|
+ );
|
|
|
|
+ } else if (this.isLinkedBoard()) {
|
|
|
|
+ const board = Boards.findOne({ _id: this.linkedId });
|
|
|
|
+ return board.addAssignee(assigneeId);
|
|
|
|
+ } else {
|
|
|
|
+ return Cards.update(
|
|
|
|
+ { _id: this._id },
|
|
|
|
+ { $addToSet: { assignees: assigneeId } },
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
unassignMember(memberId) {
|
|
unassignMember(memberId) {
|
|
if (this.isLinkedCard()) {
|
|
if (this.isLinkedCard()) {
|
|
return Cards.update(
|
|
return Cards.update(
|
|
@@ -774,6 +817,23 @@ Cards.helpers({
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ unassignAssignee(assigneeId) {
|
|
|
|
+ if (this.isLinkedCard()) {
|
|
|
|
+ return Cards.update(
|
|
|
|
+ { _id: this.linkedId },
|
|
|
|
+ { $pull: { assignees: assigneeId } },
|
|
|
|
+ );
|
|
|
|
+ } else if (this.isLinkedBoard()) {
|
|
|
|
+ const board = Boards.findOne({ _id: this.linkedId });
|
|
|
|
+ return board.removeAssignee(assigneeId);
|
|
|
|
+ } else {
|
|
|
|
+ return Cards.update(
|
|
|
|
+ { _id: this._id },
|
|
|
|
+ { $pull: { assignees: assigneeId } },
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
toggleMember(memberId) {
|
|
toggleMember(memberId) {
|
|
if (this.getMembers() && this.getMembers().indexOf(memberId) > -1) {
|
|
if (this.getMembers() && this.getMembers().indexOf(memberId) > -1) {
|
|
return this.unassignMember(memberId);
|
|
return this.unassignMember(memberId);
|
|
@@ -782,6 +842,14 @@ Cards.helpers({
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ toggleAssignee(assigneeId) {
|
|
|
|
+ if (this.getAssignees() && this.getAssignees().indexOf(assigneeId) > -1) {
|
|
|
|
+ return this.unassignAssignee(assigneeId);
|
|
|
|
+ } else {
|
|
|
|
+ return this.assignAssignee(assigneeId);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
getReceived() {
|
|
getReceived() {
|
|
if (this.isLinkedCard()) {
|
|
if (this.isLinkedCard()) {
|
|
const card = Cards.findOne({ _id: this.linkedId });
|
|
const card = Cards.findOne({ _id: this.linkedId });
|
|
@@ -1124,6 +1192,14 @@ Cards.mutations({
|
|
};
|
|
};
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ assignAssignee(assigneeId) {
|
|
|
|
+ return {
|
|
|
|
+ $addToSet: {
|
|
|
|
+ assignees: assigneeId,
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+ },
|
|
|
|
+
|
|
unassignMember(memberId) {
|
|
unassignMember(memberId) {
|
|
return {
|
|
return {
|
|
$pull: {
|
|
$pull: {
|
|
@@ -1132,6 +1208,14 @@ Cards.mutations({
|
|
};
|
|
};
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ unassignAssignee(assigneeId) {
|
|
|
|
+ return {
|
|
|
|
+ $pull: {
|
|
|
|
+ assignee: assigneeId,
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+ },
|
|
|
|
+
|
|
toggleMember(memberId) {
|
|
toggleMember(memberId) {
|
|
if (this.members && this.members.indexOf(memberId) > -1) {
|
|
if (this.members && this.members.indexOf(memberId) > -1) {
|
|
return this.unassignMember(memberId);
|
|
return this.unassignMember(memberId);
|
|
@@ -1140,6 +1224,14 @@ Cards.mutations({
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ toggleAssignee(assigneeId) {
|
|
|
|
+ if (this.assignees && this.assignees.indexOf(assigneeId) > -1) {
|
|
|
|
+ return this.unassignAssignee(assigneeId);
|
|
|
|
+ } else {
|
|
|
|
+ return this.assignAssignee(assigneeId);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
assignCustomField(customFieldId) {
|
|
assignCustomField(customFieldId) {
|
|
return {
|
|
return {
|
|
$addToSet: {
|
|
$addToSet: {
|
|
@@ -1414,6 +1506,28 @@ function cardMembers(userId, doc, fieldNames, modifier) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ function cardAssignees(userId, doc, fieldNames, modifier) {
|
|
|
|
+ if (!_.contains(fieldNames, 'assignees')) return;
|
|
|
|
+ let assigneeId;
|
|
|
|
+ // Say hello to the new assignee
|
|
|
|
+ if (modifier.$addToSet && modifier.$addToSet.assignees) {
|
|
|
|
+ assigneeId = modifier.$addToSet.assignees;
|
|
|
|
+ const username = Users.findOne(assigneeId).username;
|
|
|
|
+ if (!_.contains(doc.assignees, assigneeId)) {
|
|
|
|
+ Activities.insert({
|
|
|
|
+ userId,
|
|
|
|
+ username,
|
|
|
|
+ activityType: 'joinAssignee',
|
|
|
|
+ boardId: doc.boardId,
|
|
|
|
+ cardId: doc._id,
|
|
|
|
+ assigneeId,
|
|
|
|
+ listId: doc.listId,
|
|
|
|
+ swimlaneId: doc.swimlaneId,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
// Say goodbye to the former member
|
|
// Say goodbye to the former member
|
|
if (modifier.$pull && modifier.$pull.members) {
|
|
if (modifier.$pull && modifier.$pull.members) {
|
|
memberId = modifier.$pull.members;
|
|
memberId = modifier.$pull.members;
|
|
@@ -1432,6 +1546,25 @@ function cardMembers(userId, doc, fieldNames, modifier) {
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // Say goodbye to the former assignee
|
|
|
|
+ if (modifier.$pull && modifier.$pull.assignees) {
|
|
|
|
+ assigneeId = modifier.$pull.assignees;
|
|
|
|
+ const username = Users.findOne(assigneeId).username;
|
|
|
|
+ // Check that the former assignee is assignee of the card
|
|
|
|
+ if (_.contains(doc.assignees, assigneeId)) {
|
|
|
|
+ Activities.insert({
|
|
|
|
+ userId,
|
|
|
|
+ username,
|
|
|
|
+ activityType: 'unjoinAssignee',
|
|
|
|
+ boardId: doc.boardId,
|
|
|
|
+ cardId: doc._id,
|
|
|
|
+ assigneeId,
|
|
|
|
+ listId: doc.listId,
|
|
|
|
+ swimlaneId: doc.swimlaneId,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
function cardLabels(userId, doc, fieldNames, modifier) {
|
|
function cardLabels(userId, doc, fieldNames, modifier) {
|
|
@@ -1650,6 +1783,12 @@ if (Meteor.isServer) {
|
|
updateActivities(doc, fieldNames, modifier);
|
|
updateActivities(doc, fieldNames, modifier);
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ // Add a new activity if we add or remove a assignee to the card
|
|
|
|
+ Cards.before.update((userId, doc, fieldNames, modifier) => {
|
|
|
|
+ cardAssignees(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
|
|
Cards.before.update((userId, doc, fieldNames, modifier) => {
|
|
Cards.before.update((userId, doc, fieldNames, modifier) => {
|
|
cardLabels(userId, doc, fieldNames, modifier);
|
|
cardLabels(userId, doc, fieldNames, modifier);
|
|
@@ -1809,6 +1948,7 @@ if (Meteor.isServer) {
|
|
* @param {string} description the description of the new card
|
|
* @param {string} description the description of the new card
|
|
* @param {string} swimlaneId the swimlane ID of the new card
|
|
* @param {string} swimlaneId the swimlane ID of the new card
|
|
* @param {string} [members] the member IDs list of the new card
|
|
* @param {string} [members] the member IDs list of the new card
|
|
|
|
+ * @param {string} [assignees] the assignee IDs list of the new card
|
|
* @return_type {_id: string}
|
|
* @return_type {_id: string}
|
|
*/
|
|
*/
|
|
JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', function(
|
|
JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', function(
|
|
@@ -1830,6 +1970,7 @@ if (Meteor.isServer) {
|
|
_id: req.body.authorId,
|
|
_id: req.body.authorId,
|
|
});
|
|
});
|
|
const members = req.body.members || [req.body.authorId];
|
|
const members = req.body.members || [req.body.authorId];
|
|
|
|
+ const assignees = req.body.assignees;
|
|
if (typeof check !== 'undefined') {
|
|
if (typeof check !== 'undefined') {
|
|
const id = Cards.direct.insert({
|
|
const id = Cards.direct.insert({
|
|
title: req.body.title,
|
|
title: req.body.title,
|
|
@@ -1841,6 +1982,7 @@ if (Meteor.isServer) {
|
|
swimlaneId: req.body.swimlaneId,
|
|
swimlaneId: req.body.swimlaneId,
|
|
sort: currentCards.count(),
|
|
sort: currentCards.count(),
|
|
members,
|
|
members,
|
|
|
|
+ assignees,
|
|
});
|
|
});
|
|
JsonRoutes.sendResult(res, {
|
|
JsonRoutes.sendResult(res, {
|
|
code: 200,
|
|
code: 200,
|
|
@@ -1892,6 +2034,7 @@ if (Meteor.isServer) {
|
|
* @param {string} [labelIds] the new list of label IDs attached to the card
|
|
* @param {string} [labelIds] the new list of label IDs attached to the card
|
|
* @param {string} [swimlaneId] the new swimlane ID of the card
|
|
* @param {string} [swimlaneId] the new swimlane ID of the card
|
|
* @param {string} [members] the new list of member IDs attached to the card
|
|
* @param {string} [members] the new list of member IDs attached to the card
|
|
|
|
+ * @param {string} [assignees] the new list of assignee IDs attached to the card
|
|
* @param {string} [requestedBy] the new requestedBy field of the card
|
|
* @param {string} [requestedBy] the new requestedBy field of the card
|
|
* @param {string} [assignedBy] the new assignedBy field of the card
|
|
* @param {string} [assignedBy] the new assignedBy field of the card
|
|
* @param {string} [receivedAt] the new receivedAt field of the card
|
|
* @param {string} [receivedAt] the new receivedAt field of the card
|
|
@@ -2152,6 +2295,25 @@ if (Meteor.isServer) {
|
|
{ $set: { members: newmembers } },
|
|
{ $set: { members: newmembers } },
|
|
);
|
|
);
|
|
}
|
|
}
|
|
|
|
+ if (req.body.hasOwnProperty('assignees')) {
|
|
|
|
+ let newassignees = req.body.assignees;
|
|
|
|
+ if (_.isString(newassignees)) {
|
|
|
|
+ if (newassignees === '') {
|
|
|
|
+ newassignees = null;
|
|
|
|
+ } else {
|
|
|
|
+ newassignees = [newassignees];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ Cards.direct.update(
|
|
|
|
+ {
|
|
|
|
+ _id: paramCardId,
|
|
|
|
+ listId: paramListId,
|
|
|
|
+ boardId: paramBoardId,
|
|
|
|
+ archived: false,
|
|
|
|
+ },
|
|
|
|
+ { $set: { assignees: newassignees } },
|
|
|
|
+ );
|
|
|
|
+ }
|
|
if (req.body.hasOwnProperty('swimlaneId')) {
|
|
if (req.body.hasOwnProperty('swimlaneId')) {
|
|
const newParamSwimlaneId = req.body.swimlaneId;
|
|
const newParamSwimlaneId = req.body.swimlaneId;
|
|
Cards.direct.update(
|
|
Cards.direct.update(
|