summaryrefslogtreecommitdiffstats
path: root/models/cards.js
diff options
context:
space:
mode:
Diffstat (limited to 'models/cards.js')
-rw-r--r--models/cards.js1530
1 files changed, 1459 insertions, 71 deletions
diff --git a/models/cards.js b/models/cards.js
index e322dc2e..d5a59377 100644
--- a/models/cards.js
+++ b/models/cards.js
@@ -5,9 +5,17 @@ Cards = new Mongo.Collection('cards');
// of comments just to display the number of them in the board view.
Cards.attachSchema(new SimpleSchema({
title: {
+ /**
+ * the title of the card
+ */
type: String,
+ optional: true,
+ defaultValue: '',
},
archived: {
+ /**
+ * is the card archived
+ */
type: Boolean,
autoValue() { // eslint-disable-line consistent-return
if (this.isInsert && !this.isSet) {
@@ -15,23 +23,63 @@ Cards.attachSchema(new SimpleSchema({
}
},
},
+ parentId: {
+ /**
+ * ID of the parent card
+ */
+ type: String,
+ optional: true,
+ defaultValue: '',
+ },
listId: {
+ /**
+ * List ID where the card is
+ */
type: String,
+ optional: true,
+ defaultValue: '',
},
swimlaneId: {
+ /**
+ * Swimlane ID where the card is
+ */
type: String,
},
// The system could work without this `boardId` information (we could deduce
// the board identifier from the card), but it would make the system more
// difficult to manage and less efficient.
boardId: {
+ /**
+ * Board ID of the card
+ */
type: String,
+ optional: true,
+ defaultValue: '',
},
coverId: {
+ /**
+ * Cover ID of the card
+ */
+ type: String,
+ optional: true,
+ defaultValue: '',
+
+ },
+ color: {
type: String,
optional: true,
+ allowedValues: [
+ 'white', 'green', 'yellow', 'orange', 'red', 'purple',
+ 'blue', 'sky', 'lime', 'pink', 'black',
+ 'silver', 'peachpuff', 'crimson', 'plum', 'darkgreen',
+ 'slateblue', 'magenta', 'gold', 'navy', 'gray',
+ 'saddlebrown', 'paleturquoise', 'mistyrose', 'indigo',
+ ],
},
createdAt: {
+ /**
+ * creation date
+ */
type: Date,
autoValue() { // eslint-disable-line consistent-return
if (this.isInsert) {
@@ -41,46 +89,124 @@ Cards.attachSchema(new SimpleSchema({
}
},
},
+ customFields: {
+ /**
+ * list of custom fields
+ */
+ type: [Object],
+ optional: true,
+ defaultValue: [],
+ },
+ 'customFields.$': {
+ type: new SimpleSchema({
+ _id: {
+ /**
+ * the ID of the related custom field
+ */
+ type: String,
+ optional: true,
+ defaultValue: '',
+ },
+ value: {
+ /**
+ * value attached to the custom field
+ */
+ type: Match.OneOf(String, Number, Boolean, Date),
+ optional: true,
+ defaultValue: '',
+ },
+ }),
+ },
dateLastActivity: {
+ /**
+ * Date of last activity
+ */
type: Date,
autoValue() {
return new Date();
},
},
description: {
+ /**
+ * description of the card
+ */
+ type: String,
+ optional: true,
+ defaultValue: '',
+ },
+ requestedBy: {
+ /**
+ * who requested the card (ID of the user)
+ */
+ type: String,
+ optional: true,
+ defaultValue: '',
+ },
+ assignedBy: {
+ /**
+ * who assigned the card (ID of the user)
+ */
type: String,
optional: true,
+ defaultValue: '',
},
labelIds: {
+ /**
+ * list of labels ID the card has
+ */
type: [String],
optional: true,
+ defaultValue: [],
},
members: {
+ /**
+ * list of members (user IDs)
+ */
type: [String],
optional: true,
+ defaultValue: [],
},
receivedAt: {
+ /**
+ * Date the card was received
+ */
type: Date,
optional: true,
},
startAt: {
+ /**
+ * Date the card was started to be worked on
+ */
type: Date,
optional: true,
},
dueAt: {
+ /**
+ * Date the card is due
+ */
type: Date,
optional: true,
},
endAt: {
+ /**
+ * Date the card ended
+ */
type: Date,
optional: true,
},
spentTime: {
+ /**
+ * How much time has been spent on this
+ */
type: Number,
decimal: true,
optional: true,
+ defaultValue: 0,
},
isOvertime: {
+ /**
+ * is the card over time?
+ */
type: Boolean,
defaultValue: false,
optional: true,
@@ -88,6 +214,9 @@ Cards.attachSchema(new SimpleSchema({
// XXX Should probably be called `authorId`. Is it even needed since we have
// the `members` field?
userId: {
+ /**
+ * user ID of the author of the card
+ */
type: String,
autoValue() { // eslint-disable-line consistent-return
if (this.isInsert && !this.isSet) {
@@ -96,8 +225,36 @@ Cards.attachSchema(new SimpleSchema({
},
},
sort: {
+ /**
+ * Sort value
+ */
type: Number,
decimal: true,
+ defaultValue: '',
+ },
+ subtaskSort: {
+ /**
+ * subtask sort value
+ */
+ type: Number,
+ decimal: true,
+ defaultValue: -1,
+ optional: true,
+ },
+ type: {
+ /**
+ * type of the card
+ */
+ type: String,
+ defaultValue: 'cardType-card',
+ },
+ linkedId: {
+ /**
+ * ID of the linked card
+ */
+ type: String,
+ optional: true,
+ defaultValue: '',
},
}));
@@ -115,6 +272,68 @@ Cards.allow({
});
Cards.helpers({
+ copy(boardId, swimlaneId, listId) {
+ const oldBoard = Boards.findOne(this.boardId);
+ const oldBoardLabels = oldBoard.labels;
+ // Get old label names
+ const oldCardLabels = _.pluck(_.filter(oldBoardLabels, (label) => {
+ return _.contains(this.labelIds, label._id);
+ }), 'name');
+
+ const newBoard = Boards.findOne(boardId);
+ const newBoardLabels = newBoard.labels;
+ const newCardLabels = _.pluck(_.filter(newBoardLabels, (label) => {
+ return _.contains(oldCardLabels, label.name);
+ }), '_id');
+
+ 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.boardId = boardId;
+ this.swimlaneId = swimlaneId;
+ this.listId = listId;
+ const _id = Cards.insert(this);
+
+ // Copy attachments
+ oldCard.attachments().forEach((att) => {
+ att.cardId = _id;
+ delete att._id;
+ return Attachments.insert(att);
+ });
+
+ // copy checklists
+ Checklists.find({cardId: oldId}).forEach((ch) => {
+ ch.copy(_id);
+ });
+
+ // copy subtasks
+ Cards.find({parentId: oldId}).forEach((subtask) => {
+ subtask.parentId = _id;
+ subtask._id = null;
+ Cards.insert(subtask);
+ });
+
+ // copy card comments
+ CardComments.find({cardId: oldId}).forEach((cmt) => {
+ cmt.copy(_id);
+ });
+
+ return _id;
+ },
+
list() {
return Lists.findOne(this.listId);
},
@@ -140,19 +359,33 @@ Cards.helpers({
},
isAssigned(memberId) {
- return _.contains(this.members, memberId);
+ return _.contains(this.getMembers(), memberId);
},
activities() {
- return Activities.find({cardId: this._id}, {sort: {createdAt: -1}});
+ if (this.isLinkedCard()) {
+ return Activities.find({cardId: this.linkedId}, {sort: {createdAt: -1}});
+ } else if (this.isLinkedBoard()) {
+ return Activities.find({boardId: this.linkedId}, {sort: {createdAt: -1}});
+ } else {
+ return Activities.find({cardId: this._id}, {sort: {createdAt: -1}});
+ }
},
comments() {
- return CardComments.find({cardId: this._id}, {sort: {createdAt: -1}});
+ if (this.isLinkedCard()) {
+ return CardComments.find({cardId: this.linkedId}, {sort: {createdAt: -1}});
+ } else {
+ return CardComments.find({cardId: this._id}, {sort: {createdAt: -1}});
+ }
},
attachments() {
- return Attachments.find({cardId: this._id}, {sort: {uploadedAt: -1}});
+ if (this.isLinkedCard()) {
+ return Attachments.find({cardId: this.linkedId}, {sort: {uploadedAt: -1}});
+ } else {
+ return Attachments.find({cardId: this._id}, {sort: {uploadedAt: -1}});
+ }
},
cover() {
@@ -163,7 +396,11 @@ Cards.helpers({
},
checklists() {
- return Checklists.find({cardId: this._id}, {sort: { sort: 1 } });
+ if (this.isLinkedCard()) {
+ return Checklists.find({cardId: this.linkedId}, {sort: { sort: 1 } });
+ } else {
+ return Checklists.find({cardId: this._id}, {sort: { sort: 1 } });
+ }
},
checklistItemCount() {
@@ -192,6 +429,96 @@ Cards.helpers({
return this.checklistItemCount() !== 0;
},
+ subtasks() {
+ return Cards.find({
+ parentId: this._id,
+ archived: false,
+ }, {
+ sort: {
+ sort: 1,
+ },
+ });
+ },
+
+ allSubtasks() {
+ return Cards.find({
+ parentId: this._id,
+ archived: false,
+ }, {
+ sort: {
+ sort: 1,
+ },
+ });
+ },
+
+ subtasksCount() {
+ return Cards.find({
+ parentId: this._id,
+ archived: false,
+ }).count();
+ },
+
+ subtasksFinishedCount() {
+ return Cards.find({
+ parentId: this._id,
+ archived: true,
+ }).count();
+ },
+
+ subtasksFinished() {
+ const finishCount = this.subtasksFinishedCount();
+ return finishCount > 0 && this.subtasksCount() === finishCount;
+ },
+
+ allowsSubtasks() {
+ return this.subtasksCount() !== 0;
+ },
+
+ customFieldIndex(customFieldId) {
+ return _.pluck(this.customFields, '_id').indexOf(customFieldId);
+ },
+
+ // customFields with definitions
+ customFieldsWD() {
+
+ // get all definitions
+ const definitions = CustomFields.find({
+ boardIds: {$in: [this.boardId]},
+ }).fetch();
+
+ // match right definition to each field
+ if (!this.customFields) return [];
+ return this.customFields.map((customField) => {
+ 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) {
+ for (let i = 0; i < definition.settings.dropdownItems.length; i++) {
+ if (definition.settings.dropdownItems[i]._id === customField.value) {
+ trueValue = definition.settings.dropdownItems[i].name;
+ }
+ }
+ }
+ return {
+ _id: customField._id,
+ value: customField.value,
+ trueValue,
+ definition,
+ };
+ });
+ },
+
+ colorClass() {
+ if (this.color)
+ return this.color;
+ return '';
+ },
+
absoluteUrl() {
const board = this.board();
return FlowRouter.url('card', {
@@ -202,49 +529,560 @@ Cards.helpers({
},
canBeRestored() {
- const list = Lists.findOne({_id: this.listId});
- if(!list.getWipLimit('soft') && list.getWipLimit('enabled') && list.getWipLimit('value') === list.cards().count()){
+ const list = Lists.findOne({
+ _id: this.listId,
+ });
+ if (!list.getWipLimit('soft') && list.getWipLimit('enabled') && list.getWipLimit('value') === list.cards().count()) {
return false;
}
return true;
},
+
+ parentCard() {
+ if (this.parentId === '') {
+ return null;
+ }
+ return Cards.findOne(this.parentId);
+ },
+
+ parentCardName() {
+ let result = '';
+ if (this.parentId !== '') {
+ const card = Cards.findOne(this.parentId);
+ if (card) {
+ result = card.title;
+ }
+ }
+ return result;
+ },
+
+ parentListId() {
+ const result = [];
+ let crtParentId = this.parentId;
+ while (crtParentId !== '') {
+ const crt = Cards.findOne(crtParentId);
+ if ((crt === null) || (crt === undefined)) {
+ // maybe it has been deleted
+ break;
+ }
+ if (crtParentId in result) {
+ // circular reference
+ break;
+ }
+ result.unshift(crtParentId);
+ crtParentId = crt.parentId;
+ }
+ return result;
+ },
+
+ parentList() {
+ const resultId = [];
+ const result = [];
+ let crtParentId = this.parentId;
+ while (crtParentId !== '') {
+ const crt = Cards.findOne(crtParentId);
+ if ((crt === null) || (crt === undefined)) {
+ // maybe it has been deleted
+ break;
+ }
+ if (crtParentId in resultId) {
+ // circular reference
+ break;
+ }
+ resultId.unshift(crtParentId);
+ result.unshift(crt);
+ crtParentId = crt.parentId;
+ }
+ return result;
+ },
+
+ parentString(sep) {
+ return this.parentList().map(function(elem) {
+ return elem.title;
+ }).join(sep);
+ },
+
+ isTopLevel() {
+ return this.parentId === '';
+ },
+
+ isLinkedCard() {
+ return this.type === 'cardType-linkedCard';
+ },
+
+ isLinkedBoard() {
+ return this.type === 'cardType-linkedBoard';
+ },
+
+ isLinked() {
+ return this.isLinkedCard() || this.isLinkedBoard();
+ },
+
+ setDescription(description) {
+ if (this.isLinkedCard()) {
+ return Cards.update({_id: this.linkedId}, {$set: {description}});
+ } else if (this.isLinkedBoard()) {
+ return Boards.update({_id: this.linkedId}, {$set: {description}});
+ } else {
+ return Cards.update(
+ {_id: this._id},
+ {$set: {description}}
+ );
+ }
+ },
+
+ getDescription() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({_id: this.linkedId});
+ if (card && card.description)
+ return card.description;
+ else
+ return null;
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({_id: this.linkedId});
+ if (board && board.description)
+ return board.description;
+ else
+ return null;
+ } else if (this.description) {
+ return this.description;
+ } else {
+ return null;
+ }
+ },
+
+ getMembers() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({_id: this.linkedId});
+ return card.members;
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({_id: this.linkedId});
+ return board.activeMembers().map((member) => {
+ return member.userId;
+ });
+ } else {
+ return this.members;
+ }
+ },
+
+ assignMember(memberId) {
+ if (this.isLinkedCard()) {
+ return Cards.update(
+ { _id: this.linkedId },
+ { $addToSet: { members: memberId }}
+ );
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({_id: this.linkedId});
+ return board.addMember(memberId);
+ } else {
+ return Cards.update(
+ { _id: this._id },
+ { $addToSet: { members: memberId}}
+ );
+ }
+ },
+
+ unassignMember(memberId) {
+ if (this.isLinkedCard()) {
+ return Cards.update(
+ { _id: this.linkedId },
+ { $pull: { members: memberId }}
+ );
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({_id: this.linkedId});
+ return board.removeMember(memberId);
+ } else {
+ return Cards.update(
+ { _id: this._id },
+ { $pull: { members: memberId}}
+ );
+ }
+ },
+
+ toggleMember(memberId) {
+ if (this.getMembers() && this.getMembers().indexOf(memberId) > -1) {
+ return this.unassignMember(memberId);
+ } else {
+ return this.assignMember(memberId);
+ }
+ },
+
+ getReceived() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({_id: this.linkedId});
+ return card.receivedAt;
+ } else {
+ return this.receivedAt;
+ }
+ },
+
+ setReceived(receivedAt) {
+ if (this.isLinkedCard()) {
+ return Cards.update(
+ {_id: this.linkedId},
+ {$set: {receivedAt}}
+ );
+ } else {
+ return Cards.update(
+ {_id: this._id},
+ {$set: {receivedAt}}
+ );
+ }
+ },
+
+ getStart() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({_id: this.linkedId});
+ return card.startAt;
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({_id: this.linkedId});
+ return board.startAt;
+ } else {
+ return this.startAt;
+ }
+ },
+
+ setStart(startAt) {
+ if (this.isLinkedCard()) {
+ return Cards.update(
+ { _id: this.linkedId },
+ {$set: {startAt}}
+ );
+ } else if (this.isLinkedBoard()) {
+ return Boards.update(
+ {_id: this.linkedId},
+ {$set: {startAt}}
+ );
+ } else {
+ return Cards.update(
+ {_id: this._id},
+ {$set: {startAt}}
+ );
+ }
+ },
+
+ getDue() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({_id: this.linkedId});
+ return card.dueAt;
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({_id: this.linkedId});
+ return board.dueAt;
+ } else {
+ return this.dueAt;
+ }
+ },
+
+ setDue(dueAt) {
+ if (this.isLinkedCard()) {
+ return Cards.update(
+ { _id: this.linkedId },
+ {$set: {dueAt}}
+ );
+ } else if (this.isLinkedBoard()) {
+ return Boards.update(
+ {_id: this.linkedId},
+ {$set: {dueAt}}
+ );
+ } else {
+ return Cards.update(
+ {_id: this._id},
+ {$set: {dueAt}}
+ );
+ }
+ },
+
+ getEnd() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({_id: this.linkedId});
+ return card.endAt;
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({_id: this.linkedId});
+ return board.endAt;
+ } else {
+ return this.endAt;
+ }
+ },
+
+ setEnd(endAt) {
+ if (this.isLinkedCard()) {
+ return Cards.update(
+ { _id: this.linkedId },
+ {$set: {endAt}}
+ );
+ } else if (this.isLinkedBoard()) {
+ return Boards.update(
+ {_id: this.linkedId},
+ {$set: {endAt}}
+ );
+ } else {
+ return Cards.update(
+ {_id: this._id},
+ {$set: {endAt}}
+ );
+ }
+ },
+
+ getIsOvertime() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({ _id: this.linkedId });
+ return card.isOvertime;
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({ _id: this.linkedId});
+ return board.isOvertime;
+ } else {
+ return this.isOvertime;
+ }
+ },
+
+ setIsOvertime(isOvertime) {
+ if (this.isLinkedCard()) {
+ return Cards.update(
+ { _id: this.linkedId },
+ {$set: {isOvertime}}
+ );
+ } else if (this.isLinkedBoard()) {
+ return Boards.update(
+ {_id: this.linkedId},
+ {$set: {isOvertime}}
+ );
+ } else {
+ return Cards.update(
+ {_id: this._id},
+ {$set: {isOvertime}}
+ );
+ }
+ },
+
+ getSpentTime() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({ _id: this.linkedId });
+ return card.spentTime;
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({ _id: this.linkedId});
+ return board.spentTime;
+ } else {
+ return this.spentTime;
+ }
+ },
+
+ setSpentTime(spentTime) {
+ if (this.isLinkedCard()) {
+ return Cards.update(
+ { _id: this.linkedId },
+ {$set: {spentTime}}
+ );
+ } else if (this.isLinkedBoard()) {
+ return Boards.update(
+ {_id: this.linkedId},
+ {$set: {spentTime}}
+ );
+ } else {
+ return Cards.update(
+ {_id: this._id},
+ {$set: {spentTime}}
+ );
+ }
+ },
+
+ getId() {
+ if (this.isLinked()) {
+ return this.linkedId;
+ } else {
+ return this._id;
+ }
+ },
+
+ getTitle() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({ _id: this.linkedId });
+ return card.title;
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({ _id: this.linkedId});
+ return board.title;
+ } else {
+ return this.title;
+ }
+ },
+
+ getBoardTitle() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({ _id: this.linkedId });
+ const board = Boards.findOne({ _id: card.boardId });
+ return board.title;
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({ _id: this.linkedId});
+ return board.title;
+ } else {
+ const board = Boards.findOne({ _id: this.boardId });
+ return board.title;
+ }
+ },
+
+ setTitle(title) {
+ if (this.isLinkedCard()) {
+ return Cards.update(
+ { _id: this.linkedId },
+ {$set: {title}}
+ );
+ } else if (this.isLinkedBoard()) {
+ return Boards.update(
+ {_id: this.linkedId},
+ {$set: {title}}
+ );
+ } else {
+ return Cards.update(
+ {_id: this._id},
+ {$set: {title}}
+ );
+ }
+ },
+
+ getArchived() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({ _id: this.linkedId });
+ return card.archived;
+ } else if (this.isLinkedBoard()) {
+ const board = Boards.findOne({ _id: this.linkedId});
+ return board.archived;
+ } else {
+ return this.archived;
+ }
+ },
+
+ setRequestedBy(requestedBy) {
+ if (this.isLinkedCard()) {
+ return Cards.update(
+ { _id: this.linkedId },
+ {$set: {requestedBy}}
+ );
+ } else {
+ return Cards.update(
+ {_id: this._id},
+ {$set: {requestedBy}}
+ );
+ }
+ },
+
+ getRequestedBy() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({ _id: this.linkedId });
+ return card.requestedBy;
+ } else {
+ return this.requestedBy;
+ }
+ },
+
+ setAssignedBy(assignedBy) {
+ if (this.isLinkedCard()) {
+ return Cards.update(
+ { _id: this.linkedId },
+ {$set: {assignedBy}}
+ );
+ } else {
+ return Cards.update(
+ {_id: this._id},
+ {$set: {assignedBy}}
+ );
+ }
+ },
+
+ getAssignedBy() {
+ if (this.isLinkedCard()) {
+ const card = Cards.findOne({ _id: this.linkedId });
+ return card.assignedBy;
+ } else {
+ return this.assignedBy;
+ }
+ },
+
+ isTemplateCard() {
+ return this.type === 'template-card';
+ },
});
Cards.mutations({
+ applyToChildren(funct) {
+ Cards.find({
+ parentId: this._id,
+ }).forEach((card) => {
+ funct(card);
+ });
+ },
+
archive() {
- return {$set: {archived: true}};
+ this.applyToChildren((card) => {
+ return card.archive();
+ });
+ return {
+ $set: {
+ archived: true,
+ },
+ };
},
restore() {
- return {$set: {archived: false}};
+ this.applyToChildren((card) => {
+ return card.restore();
+ });
+ return {
+ $set: {
+ archived: false,
+ },
+ };
},
- setTitle(title) {
- return {$set: {title}};
- },
+ 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);
+ });
+ }
- setDescription(description) {
- return {$set: {description}};
- },
+ // 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');
- move(swimlaneId, listId, sortIndex) {
- const list = Lists.findOne(listId);
const mutatedFields = {
+ boardId,
swimlaneId,
listId,
- boardId: list.boardId,
- sort: sortIndex,
+ sort,
+ labelIds: newCardLabelIds,
};
- return {$set: mutatedFields};
+ Cards.update(this._id, {
+ $set: mutatedFields,
+ });
},
addLabel(labelId) {
- return {$addToSet: {labelIds: labelId}};
+ return {
+ $addToSet: {
+ labelIds: labelId,
+ },
+ };
},
removeLabel(labelId) {
- return {$pull: {labelIds: labelId}};
+ return {
+ $pull: {
+ labelIds: labelId,
+ },
+ };
},
toggleLabel(labelId) {
@@ -255,12 +1093,31 @@ Cards.mutations({
}
},
+ setColor(newColor) {
+ if (newColor === 'white') {
+ newColor = null;
+ }
+ return {
+ $set: {
+ color: newColor,
+ },
+ };
+ },
+
assignMember(memberId) {
- return {$addToSet: {members: memberId}};
+ return {
+ $addToSet: {
+ members: memberId,
+ },
+ };
},
unassignMember(memberId) {
- return {$pull: {members: memberId}};
+ return {
+ $pull: {
+ members: memberId,
+ },
+ };
},
toggleMember(memberId) {
@@ -271,71 +1128,220 @@ Cards.mutations({
}
},
+ assignCustomField(customFieldId) {
+ return {
+ $addToSet: {
+ customFields: {
+ _id: customFieldId,
+ value: null,
+ },
+ },
+ };
+ },
+
+ unassignCustomField(customFieldId) {
+ return {
+ $pull: {
+ customFields: {
+ _id: customFieldId,
+ },
+ },
+ };
+ },
+
+ toggleCustomField(customFieldId) {
+ if (this.customFields && this.customFieldIndex(customFieldId) > -1) {
+ return this.unassignCustomField(customFieldId);
+ } else {
+ return this.assignCustomField(customFieldId);
+ }
+ },
+
+ setCustomField(customFieldId, value) {
+ // todo
+ const index = this.customFieldIndex(customFieldId);
+ if (index > -1) {
+ const update = {
+ $set: {},
+ };
+ update.$set[`customFields.${index}.value`] = value;
+ return update;
+ }
+ // TODO
+ // Ignatz 18.05.2018: Return null to silence ESLint. No Idea if that is correct
+ return null;
+ },
+
setCover(coverId) {
- return {$set: {coverId}};
+ return {
+ $set: {
+ coverId,
+ },
+ };
},
unsetCover() {
- return {$unset: {coverId: ''}};
+ return {
+ $unset: {
+ coverId: '',
+ },
+ };
},
setReceived(receivedAt) {
- return {$set: {receivedAt}};
+ return {
+ $set: {
+ receivedAt,
+ },
+ };
},
unsetReceived() {
- return {$unset: {receivedAt: ''}};
+ return {
+ $unset: {
+ receivedAt: '',
+ },
+ };
},
setStart(startAt) {
- return {$set: {startAt}};
+ return {
+ $set: {
+ startAt,
+ },
+ };
},
unsetStart() {
- return {$unset: {startAt: ''}};
+ return {
+ $unset: {
+ startAt: '',
+ },
+ };
},
setDue(dueAt) {
- return {$set: {dueAt}};
+ return {
+ $set: {
+ dueAt,
+ },
+ };
},
unsetDue() {
- return {$unset: {dueAt: ''}};
+ return {
+ $unset: {
+ dueAt: '',
+ },
+ };
},
setEnd(endAt) {
- return {$set: {endAt}};
+ return {
+ $set: {
+ endAt,
+ },
+ };
},
unsetEnd() {
- return {$unset: {endAt: ''}};
+ return {
+ $unset: {
+ endAt: '',
+ },
+ };
},
setOvertime(isOvertime) {
- return {$set: {isOvertime}};
+ return {
+ $set: {
+ isOvertime,
+ },
+ };
},
setSpentTime(spentTime) {
- return {$set: {spentTime}};
+ return {
+ $set: {
+ spentTime,
+ },
+ };
},
unsetSpentTime() {
- return {$unset: {spentTime: '', isOvertime: false}};
+ return {
+ $unset: {
+ spentTime: '',
+ isOvertime: false,
+ },
+ };
},
-});
+ setParentId(parentId) {
+ return {
+ $set: {
+ parentId,
+ },
+ };
+ },
+});
//FUNCTIONS FOR creation of Activities
-function cardMove(userId, doc, fieldNames, oldListId) {
- 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,
oldListId,
activityType: 'moveCard',
+ listName: Lists.findOne(doc.listId).title,
listId: doc.listId,
boardId: doc.boardId,
cardId: doc._id,
+ cardTitle:doc.title,
+ swimlaneName: Swimlanes.findOne(doc.swimlaneId).title,
+ swimlaneId: doc.swimlaneId,
+ oldSwimlaneId,
});
}
}
@@ -346,17 +1352,21 @@ function cardState(userId, doc, fieldNames) {
Activities.insert({
userId,
activityType: 'archivedCard',
+ listName: Lists.findOne(doc.listId).title,
boardId: doc.boardId,
listId: doc.listId,
cardId: doc._id,
+ swimlaneId: doc.swimlaneId,
});
} else {
Activities.insert({
userId,
activityType: 'restoredCard',
boardId: doc.boardId,
+ listName: Lists.findOne(doc.listId).title,
listId: doc.listId,
cardId: doc._id,
+ swimlaneId: doc.swimlaneId,
});
}
}
@@ -369,13 +1379,17 @@ function cardMembers(userId, doc, fieldNames, modifier) {
// Say hello to the new member
if (modifier.$addToSet && modifier.$addToSet.members) {
memberId = modifier.$addToSet.members;
+ const username = Users.findOne(memberId).username;
if (!_.contains(doc.members, memberId)) {
Activities.insert({
userId,
- memberId,
+ username,
activityType: 'joinMember',
boardId: doc.boardId,
cardId: doc._id,
+ memberId,
+ listId: doc.listId,
+ swimlaneId: doc.swimlaneId,
});
}
}
@@ -383,36 +1397,136 @@ function cardMembers(userId, doc, fieldNames, modifier) {
// Say goodbye to the former member
if (modifier.$pull && modifier.$pull.members) {
memberId = modifier.$pull.members;
+ const username = Users.findOne(memberId).username;
// Check that the former member is member of the card
if (_.contains(doc.members, memberId)) {
Activities.insert({
userId,
- memberId,
+ username,
activityType: 'unjoinMember',
boardId: doc.boardId,
cardId: doc._id,
+ memberId,
+ listId: doc.listId,
+ swimlaneId: doc.swimlaneId,
+ });
+ }
+ }
+}
+
+function cardLabels(userId, doc, fieldNames, modifier) {
+ if (!_.contains(fieldNames, 'labelIds'))
+ return;
+ let labelId;
+ // Say hello to the new label
+ if (modifier.$addToSet && modifier.$addToSet.labelIds) {
+ labelId = modifier.$addToSet.labelIds;
+ if (!_.contains(doc.labelIds, labelId)) {
+ const act = {
+ userId,
+ labelId,
+ activityType: 'addedLabel',
+ boardId: doc.boardId,
+ cardId: doc._id,
+ listId: doc.listId,
+ swimlaneId: doc.swimlaneId,
+ };
+ Activities.insert(act);
+ }
+ }
+
+ // Say goodbye to the label
+ if (modifier.$pull && modifier.$pull.labelIds) {
+ labelId = modifier.$pull.labelIds;
+ // Check that the former member is member of the card
+ if (_.contains(doc.labelIds, labelId)) {
+ Activities.insert({
+ userId,
+ labelId,
+ activityType: 'removedLabel',
+ boardId: doc.boardId,
+ cardId: doc._id,
+ listId: doc.listId,
+ swimlaneId: doc.swimlaneId,
});
}
}
}
+function cardCustomFields(userId, doc, fieldNames, modifier) {
+ if (!_.contains(fieldNames, 'customFields'))
+ return;
+
+ // Say hello to the new customField value
+ if (modifier.$set) {
+ _.each(modifier.$set, (value, key) => {
+ if (key.startsWith('customFields')) {
+ const dotNotation = key.split('.');
+
+ // only individual changes are registered
+ if (dotNotation.length > 1) {
+ const customFieldId = doc.customFields[dotNotation[1]]._id;
+ const act = {
+ userId,
+ customFieldId,
+ value,
+ activityType: 'setCustomField',
+ boardId: doc.boardId,
+ cardId: doc._id,
+ };
+ Activities.insert(act);
+ }
+ }
+ });
+ }
+
+ // Say goodbye to the former customField value
+ if (modifier.$unset) {
+ _.each(modifier.$unset, (value, key) => {
+ if (key.startsWith('customFields')) {
+ const dotNotation = key.split('.');
+
+ // only individual changes are registered
+ if (dotNotation.length > 1) {
+ const customFieldId = doc.customFields[dotNotation[1]]._id;
+ const act = {
+ userId,
+ customFieldId,
+ activityType: 'unsetCustomField',
+ boardId: doc.boardId,
+ cardId: doc._id,
+ };
+ Activities.insert(act);
+ }
+ }
+ });
+ }
+}
+
function cardCreation(userId, doc) {
Activities.insert({
userId,
activityType: 'createCard',
boardId: doc.boardId,
+ listName: Lists.findOne(doc.listId).title,
listId: doc.listId,
cardId: doc._id,
+ cardTitle:doc.title,
+ swimlaneName: Swimlanes.findOne(doc.swimlaneId).title,
+ swimlaneId: doc.swimlaneId,
});
}
function cardRemover(userId, doc) {
- Activities.remove({
+ ChecklistItems.remove({
cardId: doc._id,
});
Checklists.remove({
cardId: doc._id,
});
+ Cards.remove({
+ parentId: doc._id,
+ });
CardComments.remove({
cardId: doc._id,
});
@@ -421,12 +1535,17 @@ function cardRemover(userId, doc) {
});
}
-
if (Meteor.isServer) {
// Cards are often fetched within a board, so we create an index to make these
// queries more efficient.
Meteor.startup(() => {
Cards._collection._ensureIndex({boardId: 1, createdAt: -1});
+ // https://github.com/wekan/wekan/issues/1863
+ // Swimlane added a new field in the cards collection of mongodb named parentId.
+ // When loading a board, mongodb is searching for every cards, the id of the parent (in the swinglanes collection).
+ // With a huge database, this result in a very slow app and high CPU on the mongodb side.
+ // To correct it, add Index to parentId:
+ Cards._collection._ensureIndex({parentId: 1});
});
Cards.after.insert((userId, doc) => {
@@ -439,31 +1558,92 @@ if (Meteor.isServer) {
});
//New activity for card moves
- Cards.after.update(function (userId, doc, fieldNames) {
+ Cards.after.update(function(userId, doc, fieldNames) {
const oldListId = this.previous.listId;
- cardMove(userId, doc, fieldNames, oldListId);
+ const oldSwimlaneId = this.previous.swimlaneId;
+ 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
+ Cards.before.update((userId, doc, fieldNames, modifier) => {
+ cardLabels(userId, doc, fieldNames, modifier);
+ });
+
+ // Add a new activity if we edit a custom field
+ Cards.before.update((userId, doc, fieldNames, modifier) => {
+ cardCustomFields(userId, doc, fieldNames, modifier);
});
// Remove all activities associated with a card if we remove the card
// Remove also card_comments / checklists / attachments
- Cards.after.remove((userId, doc) => {
+ Cards.before.remove((userId, doc) => {
cardRemover(userId, doc);
});
}
+//SWIMLANES REST API
+if (Meteor.isServer) {
+ /**
+ * @operation get_swimlane_cards
+ * @summary get all cards attached to a swimlane
+ *
+ * @param {string} boardId the board ID
+ * @param {string} swimlaneId the swimlane ID
+ * @return_type [{_id: string,
+ * title: string,
+ * description: string,
+ * listId: string}]
+ */
+ JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId/cards', function(req, res) {
+ const paramBoardId = req.params.boardId;
+ const paramSwimlaneId = req.params.swimlaneId;
+ Authentication.checkBoardAccess(req.userId, paramBoardId);
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: Cards.find({
+ boardId: paramBoardId,
+ swimlaneId: paramSwimlaneId,
+ archived: false,
+ }).map(function(doc) {
+ return {
+ _id: doc._id,
+ title: doc.title,
+ description: doc.description,
+ listId: doc.listId,
+ };
+ }),
+ });
+ });
+}
//LISTS REST API
if (Meteor.isServer) {
- JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards', function (req, res) {
+ /**
+ * @operation get_all_cards
+ * @summary Get all Cards attached to a List
+ *
+ * @param {string} boardId the board ID
+ * @param {string} listId the list ID
+ * @return_type [{_id: string,
+ * title: string,
+ * description: string}]
+ */
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards', function(req, res) {
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: Cards.find({boardId: paramBoardId, listId: paramListId, archived: false}).map(function (doc) {
+ data: Cards.find({
+ boardId: paramBoardId,
+ listId: paramListId,
+ archived: false,
+ }).map(function(doc) {
return {
_id: doc._id,
title: doc.title,
@@ -473,24 +1653,57 @@ if (Meteor.isServer) {
});
});
- JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards/:cardId', function (req, res) {
+ /**
+ * @operation get_card
+ * @summary Get a Card
+ *
+ * @param {string} boardId the board ID
+ * @param {string} listId the list ID of the card
+ * @param {string} cardId the card ID
+ * @return_type Cards
+ */
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards/:cardId', function(req, res) {
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
const paramCardId = req.params.cardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: Cards.findOne({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false}),
+ data: Cards.findOne({
+ _id: paramCardId,
+ listId: paramListId,
+ boardId: paramBoardId,
+ archived: false,
+ }),
});
});
- JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', function (req, res) {
+ /**
+ * @operation new_card
+ * @summary Create a new Card
+ *
+ * @param {string} boardId the board ID of the new card
+ * @param {string} listId the list ID of the new card
+ * @param {string} authorID the user ID of the person owning the card
+ * @param {string} title the title 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} [members] the member IDs list of the new card
+ * @return_type {_id: string}
+ */
+ JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', function(req, res) {
Authentication.checkUserId(req.userId);
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
- const check = Users.findOne({_id: req.body.authorId});
+ const currentCards = Cards.find({
+ listId: paramListId,
+ archived: false,
+ }, { sort: ['sort'] });
+ const check = Users.findOne({
+ _id: req.body.authorId,
+ });
const members = req.body.members || [req.body.authorId];
- if (typeof check !== 'undefined') {
+ if (typeof check !== 'undefined') {
const id = Cards.direct.insert({
title: req.body.title,
boardId: paramBoardId,
@@ -498,8 +1711,8 @@ if (Meteor.isServer) {
description: req.body.description,
userId: req.body.authorId,
swimlaneId: req.body.swimlaneId,
- sort: 0,
- members: members,
+ sort: currentCards.count(),
+ members,
});
JsonRoutes.sendResult(res, {
code: 200,
@@ -508,7 +1721,9 @@ if (Meteor.isServer) {
},
});
- const card = Cards.findOne({_id:id});
+ const card = Cards.findOne({
+ _id: id,
+ });
cardCreation(req.body.authorId, card);
} else {
@@ -518,7 +1733,51 @@ if (Meteor.isServer) {
}
});
- JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId/cards/:cardId', function (req, res) {
+ /*
+ * Note for the JSDoc:
+ * 'list' will be interpreted as the path parameter
+ * 'listID' will be interpreted as the body parameter
+ */
+ /**
+ * @operation edit_card
+ * @summary Edit Fields in a Card
+ *
+ * @description Edit a card
+ *
+ * The color has to be chosen between `white`, `green`, `yellow`, `orange`,
+ * `red`, `purple`, `blue`, `sky`, `lime`, `pink`, `black`, `silver`,
+ * `peachpuff`, `crimson`, `plum`, `darkgreen`, `slateblue`, `magenta`,
+ * `gold`, `navy`, `gray`, `saddlebrown`, `paleturquoise`, `mistyrose`,
+ * `indigo`:
+ *
+ * <img src="/card-colors.png" width="40%" alt="Wekan card colors" />
+ *
+ * Note: setting the color to white has the same effect than removing it.
+ *
+ * @param {string} boardId the board ID of the card
+ * @param {string} list the list ID of the card
+ * @param {string} cardId the ID of the card
+ * @param {string} [title] the new title of the card
+ * @param {string} [listId] the new list ID of the card (move operation)
+ * @param {string} [description] the new description of the card
+ * @param {string} [authorId] change the owner of 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} [members] the new list of member IDs 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
+ * @param {string} [assignBy] the new assignBy field of the card
+ * @param {string} [startAt] the new startAt field of the card
+ * @param {string} [dueAt] the new dueAt field of the card
+ * @param {string} [endAt] the new endAt field of the card
+ * @param {string} [spentTime] the new spentTime field of the card
+ * @param {boolean} [isOverTime] the new isOverTime field of the card
+ * @param {string} [customFields] the new customFields value of the card
+ * @param {string} [color] the new color of the card
+ * @return_type {_id: string}
+ */
+ JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId/cards/:cardId', function(req, res) {
Authentication.checkUserId(req.userId);
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
@@ -526,27 +1785,139 @@ if (Meteor.isServer) {
if (req.body.hasOwnProperty('title')) {
const newTitle = req.body.title;
- Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
- {$set: {title: newTitle}});
+ Cards.direct.update({
+ _id: paramCardId,
+ listId: paramListId,
+ boardId: paramBoardId,
+ archived: false,
+ }, {
+ $set: {
+ title: newTitle,
+ },
+ });
}
if (req.body.hasOwnProperty('listId')) {
const newParamListId = req.body.listId;
- Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
- {$set: {listId: newParamListId}});
+ Cards.direct.update({
+ _id: paramCardId,
+ listId: paramListId,
+ boardId: paramBoardId,
+ archived: false,
+ }, {
+ $set: {
+ listId: newParamListId,
+ },
+ });
- const card = Cards.findOne({_id: paramCardId} );
- cardMove(req.body.authorId, card, {fieldName: 'listId'}, paramListId);
+ const card = Cards.findOne({
+ _id: paramCardId,
+ });
+ cardMove(req.body.authorId, card, {
+ fieldName: 'listId',
+ }, paramListId);
}
if (req.body.hasOwnProperty('description')) {
const newDescription = req.body.description;
+ Cards.direct.update({
+ _id: paramCardId,
+ listId: paramListId,
+ boardId: paramBoardId,
+ archived: false,
+ }, {
+ $set: {
+ description: newDescription,
+ },
+ });
+ }
+ if (req.body.hasOwnProperty('color')) {
+ const newColor = req.body.color;
Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
- {$set: {description: newDescription}});
+ {$set: {color: newColor}});
}
if (req.body.hasOwnProperty('labelIds')) {
- const newlabelIds = req.body.labelIds;
+ let newlabelIds = req.body.labelIds;
+ if (_.isString(newlabelIds)) {
+ if (newlabelIds === '') {
+ newlabelIds = null;
+ }
+ else {
+ newlabelIds = [newlabelIds];
+ }
+ }
+ Cards.direct.update({
+ _id: paramCardId,
+ listId: paramListId,
+ boardId: paramBoardId,
+ archived: false,
+ }, {
+ $set: {
+ labelIds: newlabelIds,
+ },
+ });
+ }
+ if (req.body.hasOwnProperty('requestedBy')) {
+ const newrequestedBy = req.body.requestedBy;
+ Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
+ {$set: {requestedBy: newrequestedBy}});
+ }
+ if (req.body.hasOwnProperty('assignedBy')) {
+ const newassignedBy = req.body.assignedBy;
+ Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
+ {$set: {assignedBy: newassignedBy}});
+ }
+ if (req.body.hasOwnProperty('receivedAt')) {
+ const newreceivedAt = req.body.receivedAt;
Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
- {$set: {labelIds: newlabelIds}});
+ {$set: {receivedAt: newreceivedAt}});
+ }
+ if (req.body.hasOwnProperty('startAt')) {
+ const newstartAt = req.body.startAt;
+ Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
+ {$set: {startAt: newstartAt}});
+ }
+ if (req.body.hasOwnProperty('dueAt')) {
+ const newdueAt = req.body.dueAt;
+ Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
+ {$set: {dueAt: newdueAt}});
+ }
+ if (req.body.hasOwnProperty('endAt')) {
+ const newendAt = req.body.endAt;
+ Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
+ {$set: {endAt: newendAt}});
+ }
+ if (req.body.hasOwnProperty('spentTime')) {
+ const newspentTime = req.body.spentTime;
+ Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
+ {$set: {spentTime: newspentTime}});
+ }
+ if (req.body.hasOwnProperty('isOverTime')) {
+ const newisOverTime = req.body.isOverTime;
+ Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
+ {$set: {isOverTime: newisOverTime}});
+ }
+ if (req.body.hasOwnProperty('customFields')) {
+ const newcustomFields = req.body.customFields;
+ Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
+ {$set: {customFields: newcustomFields}});
+ }
+ if (req.body.hasOwnProperty('members')) {
+ let newmembers = req.body.members;
+ if (_.isString(newmembers)) {
+ if (newmembers === '') {
+ newmembers = null;
+ }
+ else {
+ newmembers = [newmembers];
+ }
+ }
+ Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
+ {$set: {members: newmembers}});
+ }
+ if (req.body.hasOwnProperty('swimlaneId')) {
+ const newParamSwimlaneId = req.body.swimlaneId;
+ Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
+ {$set: {swimlaneId: newParamSwimlaneId}});
}
JsonRoutes.sendResult(res, {
code: 200,
@@ -556,15 +1927,32 @@ if (Meteor.isServer) {
});
});
-
- JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId/cards/:cardId', function (req, res) {
+ /**
+ * @operation delete_card
+ * @summary Delete a card from a board
+ *
+ * @description This operation **deletes** a card, and therefore the card
+ * is not put in the recycle bin.
+ *
+ * @param {string} boardId the board ID of the card
+ * @param {string} list the list ID of the card
+ * @param {string} cardId the ID of the card
+ * @return_type {_id: string}
+ */
+ JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId/cards/:cardId', function(req, res) {
Authentication.checkUserId(req.userId);
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
const paramCardId = req.params.cardId;
- Cards.direct.remove({_id: paramCardId, listId: paramListId, boardId: paramBoardId});
- const card = Cards.find({_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,