summaryrefslogtreecommitdiffstats
path: root/models/cards.js
diff options
context:
space:
mode:
author蔡仲明 (Romulus Urakagi Tsai) <urakagi@gmail.com>2019-11-21 11:25:56 +0800
committerGitHub <noreply@github.com>2019-11-21 11:25:56 +0800
commit3e0bedd8c7a6dec97352212adb1cbde1ade44190 (patch)
tree651ff30d25ddb0416444370368d699e597c142d7 /models/cards.js
parent9bbeb73db1cd0ce1caaaca8dfb14ea92131bbf9d (diff)
parent4f5de87cc4c2281bd576548693de7c94e6a988c6 (diff)
downloadwekan-3e0bedd8c7a6dec97352212adb1cbde1ade44190.tar.gz
wekan-3e0bedd8c7a6dec97352212adb1cbde1ade44190.tar.bz2
wekan-3e0bedd8c7a6dec97352212adb1cbde1ade44190.zip
Merge pull request #1 from wekan/master
Update master
Diffstat (limited to 'models/cards.js')
-rw-r--r--models/cards.js230
1 files changed, 220 insertions, 10 deletions
diff --git a/models/cards.js b/models/cards.js
index d92d003c..816132fe 100644
--- a/models/cards.js
+++ b/models/cards.js
@@ -107,6 +107,8 @@ Cards.attachSchema(
autoValue() {
if (this.isInsert) {
return new Date();
+ } else if (this.isUpsert) {
+ return { $setOnInsert: new Date() };
} else {
this.unset();
}
@@ -201,6 +203,15 @@ Cards.attachSchema(
optional: true,
defaultValue: [],
},
+ assignees: {
+ /**
+ * who is assignee of the card (user ID),
+ * maximum one ID of assignee in array.
+ */
+ type: [String],
+ optional: true,
+ defaultValue: [],
+ },
receivedAt: {
/**
* Date the card was received
@@ -409,6 +420,10 @@ Cards.helpers({
return _.contains(this.getMembers(), memberId);
},
+ isAssignee(assigneeId) {
+ return _.contains(this.getAssignees(), assigneeId);
+ },
+
activities() {
if (this.isLinkedCard()) {
return Activities.find(
@@ -743,6 +758,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.activeMembers().map(assignee => {
+ return assignee.userId;
+ });
+ } else {
+ return this.assignees;
+ }
+ },
+
assignMember(memberId) {
if (this.isLinkedCard()) {
return Cards.update(
@@ -760,6 +789,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) {
if (this.isLinkedCard()) {
return Cards.update(
@@ -774,6 +820,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) {
if (this.getMembers() && this.getMembers().indexOf(memberId) > -1) {
return this.unassignMember(memberId);
@@ -782,6 +845,14 @@ Cards.helpers({
}
},
+ toggleAssignee(assigneeId) {
+ if (this.getAssignees() && this.getAssignees().indexOf(assigneeId) > -1) {
+ return this.unassignAssignee(assigneeId);
+ } else {
+ return this.assignAssignee(assigneeId);
+ }
+ },
+
getReceived() {
if (this.isLinkedCard()) {
const card = Cards.findOne({ _id: this.linkedId });
@@ -1124,6 +1195,19 @@ Cards.mutations({
};
},
+ assignAssignee(assigneeId) {
+ // If there is not any assignee, allow one assignee, not more.
+ if (this.getAssignees().length === 0) {
+ return {
+ $addToSet: {
+ assignees: assigneeId,
+ },
+ };
+ } else {
+ return false;
+ }
+ },
+
unassignMember(memberId) {
return {
$pull: {
@@ -1132,6 +1216,14 @@ Cards.mutations({
};
},
+ unassignAssignee(assigneeId) {
+ return {
+ $pull: {
+ assignees: assigneeId,
+ },
+ };
+ },
+
toggleMember(memberId) {
if (this.members && this.members.indexOf(memberId) > -1) {
return this.unassignMember(memberId);
@@ -1140,6 +1232,14 @@ Cards.mutations({
}
},
+ toggleAssignee(assigneeId) {
+ if (this.assignees && this.assignees.indexOf(assigneeId) > -1) {
+ return this.unassignAssignee(assigneeId);
+ } else {
+ return this.assignAssignee(assigneeId);
+ }
+ },
+
assignCustomField(customFieldId) {
return {
$addToSet: {
@@ -1434,6 +1534,46 @@ 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 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) {
if (!_.contains(fieldNames, 'labelIds')) return;
let labelId;
@@ -1556,6 +1696,7 @@ function cardRemover(userId, doc) {
const findDueCards = days => {
const seekDue = ($from, $to, activityType) => {
Cards.find({
+ archived: false,
dueAt: { $gte: $from, $lt: $to },
}).forEach(card => {
const username = Users.findOne(card.userId).username;
@@ -1576,18 +1717,38 @@ const findDueCards = days => {
const now = new Date(),
aday = 3600 * 24 * 1e3,
then = day => new Date(now.setHours(0, 0, 0, 0) + day * aday);
- seekDue(then(1), then(days), 'almostdue');
- seekDue(then(0), then(1), 'duenow');
- seekDue(then(-days), now, 'pastdue');
+ if (!days) return;
+ if (!days.map) days = [days];
+ days.map(day => {
+ let args = [];
+ if (day === 0) {
+ args = [then(0), then(1), 'duenow'];
+ } else if (day > 0) {
+ args = [then(1), then(day), 'almostdue'];
+ } else {
+ args = [then(day), now, 'pastdue'];
+ }
+ seekDue(...args);
+ });
};
const addCronJob = _.debounce(
Meteor.bindEnvironment(function findDueCardsDebounced() {
- const notifydays =
- parseInt(process.env.NOTIFY_DUE_DAYS_BEFORE_AND_AFTER, 10) || 2; // default as 2 days before and after
- if (!(notifydays > 0 && notifydays < 15)) {
- // notifying due is disabled
+ const envValue = process.env.NOTIFY_DUE_DAYS_BEFORE_AND_AFTER;
+ if (!envValue) {
return;
}
+ const notifydays = envValue
+ .split(',')
+ .map(value => {
+ const iValue = parseInt(value, 10);
+ if (!(iValue > 0 && iValue < 15)) {
+ // notifying due is disabled
+ return false;
+ } else {
+ return iValue;
+ }
+ })
+ .filter(Boolean);
const notifyitvl = process.env.NOTIFY_DUE_AT_HOUR_OF_DAY; //passed in the itvl has to be a number standing for the hour of current time
const defaultitvl = 8; // default every morning at 8am, if the passed env variable has parsing error use default
const itvl = parseInt(notifyitvl, 10) || defaultitvl;
@@ -1650,6 +1811,12 @@ if (Meteor.isServer) {
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
Cards.before.update((userId, doc, fieldNames, modifier) => {
cardLabels(userId, doc, fieldNames, modifier);
@@ -1672,6 +1839,26 @@ if (Meteor.isServer) {
const oldvalue = doc[action] || '';
const activityType = `a-${action}`;
const card = Cards.findOne(doc._id);
+ const list = card.list();
+ if (list) {
+ // change list modifiedAt, when user modified the key values in timingaction array, if it's endAt, put the modifiedAt of list back to one year ago for sorting purpose
+ const modifiedAt = new Date(
+ new Date(value).getTime() -
+ (action === 'endAt' ? 365 * 24 * 3600 * 1e3 : 0),
+ ); // set it as 1 year before
+ const boardId = list.boardId;
+ Lists.direct.update(
+ {
+ _id: list._id,
+ },
+ {
+ $set: {
+ modifiedAt,
+ boardId,
+ },
+ },
+ );
+ }
const username = Users.findOne(userId).username;
const activity = {
userId,
@@ -1809,6 +1996,7 @@ if (Meteor.isServer) {
* @param {string} description the description 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} [assignees] the array of maximum one ID of assignee of the new card
* @return_type {_id: string}
*/
JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', function(
@@ -1830,6 +2018,7 @@ if (Meteor.isServer) {
_id: req.body.authorId,
});
const members = req.body.members || [req.body.authorId];
+ const assignees = req.body.assignees;
if (typeof check !== 'undefined') {
const id = Cards.direct.insert({
title: req.body.title,
@@ -1841,6 +2030,7 @@ if (Meteor.isServer) {
swimlaneId: req.body.swimlaneId,
sort: currentCards.count(),
members,
+ assignees,
});
JsonRoutes.sendResult(res, {
code: 200,
@@ -1892,6 +2082,7 @@ if (Meteor.isServer) {
* @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} [members] the new list of member IDs attached to the card
+ * @param {string} [assignees] the array of maximum one ID of assignee attached to the card
* @param {string} [requestedBy] the new requestedBy field of the card
* @param {string} [assignedBy] the new assignedBy field of the card
* @param {string} [receivedAt] the new receivedAt field of the card
@@ -2152,6 +2343,25 @@ if (Meteor.isServer) {
{ $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')) {
const newParamSwimlaneId = req.body.swimlaneId;
Cards.direct.update(
@@ -2194,14 +2404,14 @@ if (Meteor.isServer) {
const paramListId = req.params.listId;
const paramCardId = req.params.cardId;
+ const card = Cards.findOne({
+ _id: paramCardId,
+ });
Cards.direct.remove({
_id: paramCardId,
listId: paramListId,
boardId: paramBoardId,
});
- const card = Cards.find({
- _id: paramCardId,
- });
cardRemover(req.body.authorId, card);
JsonRoutes.sendResult(res, {
code: 200,