summaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
Diffstat (limited to 'models')
-rw-r--r--models/activities.js3
-rw-r--r--models/boards.js77
-rw-r--r--models/cards.js546
-rw-r--r--models/export.js10
-rw-r--r--models/lists.js6
-rw-r--r--models/settings.js46
-rw-r--r--models/swimlanes.js6
-rw-r--r--models/trelloCreator.js2
-rw-r--r--models/users.js55
-rw-r--r--models/wekanCreator.js1
10 files changed, 673 insertions, 79 deletions
diff --git a/models/activities.js b/models/activities.js
index fe24c9c4..c14760c2 100644
--- a/models/activities.js
+++ b/models/activities.js
@@ -128,6 +128,9 @@ if (Meteor.isServer) {
params.url = card.absoluteUrl();
params.cardId = activity.cardId;
}
+ if (activity.swimlaneId) {
+ params.swimlaneId = activity.swimlaneId;
+ }
if (activity.commentId) {
const comment = activity.comment();
params.comment = comment.text;
diff --git a/models/boards.js b/models/boards.js
index c77c9de2..641ecdb9 100644
--- a/models/boards.js
+++ b/models/boards.js
@@ -110,6 +110,7 @@ Boards.attachSchema(new SimpleSchema({
userId: this.userId,
isAdmin: true,
isActive: true,
+ isNoComments: false,
isCommentOnly: false,
}];
}
@@ -124,6 +125,9 @@ Boards.attachSchema(new SimpleSchema({
'members.$.isActive': {
type: Boolean,
},
+ 'members.$.isNoComments': {
+ type: Boolean,
+ },
'members.$.isCommentOnly': {
type: Boolean,
},
@@ -177,6 +181,28 @@ Boards.attachSchema(new SimpleSchema({
optional: true,
defaultValue: 'no-parent',
},
+ startAt: {
+ type: Date,
+ optional: true,
+ },
+ dueAt: {
+ type: Date,
+ optional: true,
+ },
+ endAt: {
+ type: Date,
+ optional: true,
+ },
+ spentTime: {
+ type: Number,
+ decimal: true,
+ optional: true,
+ },
+ isOvertime: {
+ type: Boolean,
+ defaultValue: false,
+ optional: true,
+ },
}));
@@ -212,6 +238,10 @@ Boards.helpers({
return this.permission === 'public';
},
+ cards() {
+ return Cards.find({ boardId: this._id, archived: false }, { sort: { title: 1 } });
+ },
+
lists() {
return Lists.find({ boardId: this._id, archived: false }, { sort: { sort: 1 } });
},
@@ -220,10 +250,6 @@ Boards.helpers({
return Swimlanes.find({ boardId: this._id, archived: false }, { sort: { sort: 1 } });
},
- cards() {
- return Cards.find({ boardId: this._id, archived: false }, { sort: { sort: 1 } });
- },
-
hasOvertimeCards(){
const card = Cards.findOne({isOvertime: true, boardId: this._id, archived: false} );
return card !== undefined;
@@ -274,6 +300,10 @@ Boards.helpers({
return !!_.findWhere(this.members, { userId: memberId, isActive: true, isAdmin: true });
},
+ hasNoComments(memberId) {
+ return !!_.findWhere(this.members, { userId: memberId, isActive: true, isAdmin: false, isNoComments: true });
+ },
+
hasCommentOnly(memberId) {
return !!_.findWhere(this.members, { userId: memberId, isActive: true, isAdmin: false, isCommentOnly: true });
},
@@ -298,22 +328,22 @@ Boards.helpers({
return _id;
},
- searchCards(term) {
+ searchCards(term, excludeLinked) {
check(term, Match.OneOf(String, null, undefined));
- let query = { boardId: this._id };
+ const query = { boardId: this._id };
+ if (excludeLinked) {
+ query.linkedId = null;
+ }
const projection = { limit: 10, sort: { createdAt: -1 } };
if (term) {
const regex = new RegExp(term, 'i');
- query = {
- boardId: this._id,
- $or: [
- { title: regex },
- { description: regex },
- ],
- };
+ query.$or = [
+ { title: regex },
+ { description: regex },
+ ];
}
return Cards.find(query, projection);
@@ -376,6 +406,7 @@ Boards.helpers({
cardsInInterval(start, end) {
return Cards.find({
+ boardId: this._id,
$or: [
{
startAt: {
@@ -482,6 +513,7 @@ Boards.mutations({
userId: memberId,
isAdmin: false,
isActive: true,
+ isNoComments: false,
isCommentOnly: false,
},
},
@@ -509,7 +541,7 @@ Boards.mutations({
};
},
- setMemberPermission(memberId, isAdmin, isCommentOnly) {
+ setMemberPermission(memberId, isAdmin, isNoComments, isCommentOnly) {
const memberIndex = this.memberIndex(memberId);
// do not allow change permission of self
@@ -520,6 +552,7 @@ Boards.mutations({
return {
$set: {
[`members.${memberIndex}.isAdmin`]: isAdmin,
+ [`members.${memberIndex}.isNoComments`]: isNoComments,
[`members.${memberIndex}.isCommentOnly`]: isCommentOnly,
},
};
@@ -817,18 +850,24 @@ if (Meteor.isServer) {
members: [
{
userId: req.body.owner,
- isAdmin: true,
- isActive: true,
- isCommentOnly: false,
+ isAdmin: req.body.isAdmin || true,
+ isActive: req.body.isActive || true,
+ isNoComments: req.body.isNoComments || false,
+ isCommentOnly: req.body.isCommentOnly || false,
},
],
- permission: 'public',
- color: 'belize',
+ permission: req.body.permission || 'private',
+ color: req.body.color || 'belize',
+ });
+ const swimlaneId = Swimlanes.insert({
+ title: TAPi18n.__('default'),
+ boardId: id,
});
JsonRoutes.sendResult(res, {
code: 200,
data: {
_id: id,
+ defaultSwimlaneId: swimlaneId,
},
});
}
diff --git a/models/cards.js b/models/cards.js
index efc25a8f..2595e934 100644
--- a/models/cards.js
+++ b/models/cards.js
@@ -6,6 +6,8 @@ Cards = new Mongo.Collection('cards');
Cards.attachSchema(new SimpleSchema({
title: {
type: String,
+ optional: true,
+ defaultValue: '',
},
archived: {
type: Boolean,
@@ -22,6 +24,8 @@ Cards.attachSchema(new SimpleSchema({
},
listId: {
type: String,
+ optional: true,
+ defaultValue: '',
},
swimlaneId: {
type: String,
@@ -31,10 +35,14 @@ Cards.attachSchema(new SimpleSchema({
// difficult to manage and less efficient.
boardId: {
type: String,
+ optional: true,
+ defaultValue: '',
},
coverId: {
type: String,
optional: true,
+ defaultValue: '',
+
},
createdAt: {
type: Date,
@@ -49,15 +57,19 @@ Cards.attachSchema(new SimpleSchema({
customFields: {
type: [Object],
optional: true,
+ defaultValue: [],
},
'customFields.$': {
type: new SimpleSchema({
_id: {
type: String,
+ optional: true,
+ defaultValue: '',
},
value: {
type: Match.OneOf(String, Number, Boolean, Date),
optional: true,
+ defaultValue: '',
},
}),
},
@@ -70,22 +82,27 @@ Cards.attachSchema(new SimpleSchema({
description: {
type: String,
optional: true,
+ defaultValue: '',
},
requestedBy: {
type: String,
optional: true,
+ defaultValue: '',
},
assignedBy: {
type: String,
optional: true,
+ defaultValue: '',
},
labelIds: {
type: [String],
optional: true,
+ defaultValue: [],
},
members: {
type: [String],
optional: true,
+ defaultValue: [],
},
receivedAt: {
type: Date,
@@ -107,6 +124,7 @@ Cards.attachSchema(new SimpleSchema({
type: Number,
decimal: true,
optional: true,
+ defaultValue: 0,
},
isOvertime: {
type: Boolean,
@@ -126,6 +144,7 @@ Cards.attachSchema(new SimpleSchema({
sort: {
type: Number,
decimal: true,
+ defaultValue: '',
},
subtaskSort: {
type: Number,
@@ -133,6 +152,15 @@ Cards.attachSchema(new SimpleSchema({
defaultValue: -1,
optional: true,
},
+ type: {
+ type: String,
+ defaultValue: '',
+ },
+ linkedId: {
+ type: String,
+ optional: true,
+ defaultValue: '',
+ },
}));
Cards.allow({
@@ -174,37 +202,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() {
@@ -215,13 +239,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() {
@@ -418,6 +440,396 @@ Cards.helpers({
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;
+ }
+ },
});
Cards.mutations({
@@ -521,6 +933,8 @@ Cards.mutations({
}
},
+<<<<<<< HEAD
+=======
assignMember(memberId) {
return {
$addToSet: {
@@ -545,6 +959,7 @@ Cards.mutations({
}
},
+>>>>>>> 36c04edb9f7cf16fb450b76598c4957968d4674b
assignCustomField(customFieldId) {
return {
$addToSet: {
@@ -605,6 +1020,8 @@ Cards.mutations({
};
},
+<<<<<<< HEAD
+=======
setReceived(receivedAt) {
return {
$set: {
@@ -694,6 +1111,7 @@ Cards.mutations({
};
},
+>>>>>>> 36c04edb9f7cf16fb450b76598c4957968d4674b
setParentId(parentId) {
return {
$set: {
@@ -701,13 +1119,13 @@ Cards.mutations({
}
};
},
-
});
//FUNCTIONS FOR creation of Activities
-function cardMove(userId, doc, fieldNames, oldListId) {
- if (_.contains(fieldNames, 'listId') && doc.listId !== oldListId) {
+function cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId) {
+ if ((_.contains(fieldNames, 'listId') && doc.listId !== oldListId) ||
+ (_.contains(fieldNames, 'swimlaneId') && doc.swimlaneId !== oldSwimlaneId)){
Activities.insert({
userId,
oldListId,
@@ -716,6 +1134,8 @@ function cardMove(userId, doc, fieldNames, oldListId) {
listId: doc.listId,
boardId: doc.boardId,
cardId: doc._id,
+ swimlaneId: doc.swimlaneId,
+ oldSwimlaneId,
});
}
}
@@ -821,6 +1241,7 @@ function cardCreation(userId, doc) {
listName: Lists.findOne(doc.listId).title,
listId: doc.listId,
cardId: doc._id,
+ swimlaneId: doc.swimlaneId,
});
}
@@ -846,10 +1267,13 @@ 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
- });
+ 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) => {
@@ -864,7 +1288,8 @@ if (Meteor.isServer) {
//New activity for card moves
Cards.after.update(function(userId, doc, fieldNames) {
const oldListId = this.previous.listId;
- cardMove(userId, doc, fieldNames, oldListId);
+ const oldSwimlaneId = this.previous.swimlaneId;
+ cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId);
});
// Add a new activity if we add or remove a member to the card
@@ -1025,6 +1450,51 @@ if (Meteor.isServer) {
}
});
}
+ 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: {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}});
+ }
JsonRoutes.sendResult(res, {
code: 200,
data: {
@@ -1056,4 +1526,4 @@ if (Meteor.isServer) {
});
});
-} \ No newline at end of file
+}
diff --git a/models/export.js b/models/export.js
index f4ea6789..c65ebf52 100644
--- a/models/export.js
+++ b/models/export.js
@@ -47,9 +47,8 @@ class Exporter {
}
build() {
- const byBoard = {
- boardId: this._boardId
- };
+ const byBoard = { boardId: this._boardId };
+ const byBoardNoLinked = { boardId: this._boardId, linkedId: '' };
// we do not want to retrieve boardId in related elements
const noBoardId = {
fields: {
@@ -65,8 +64,9 @@ class Exporter {
}
}));
result.lists = Lists.find(byBoard, noBoardId).fetch();
- result.cards = Cards.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.comments = CardComments.find(byBoard, noBoardId).fetch();
result.activities = Activities.find(byBoard, noBoardId).fetch();
result.rules = Rules.find(byBoard, noBoardId).fetch();
@@ -182,4 +182,4 @@ class Exporter {
const board = Boards.findOne(this._boardId);
return board && board.isVisibleBy(user);
}
-} \ No newline at end of file
+}
diff --git a/models/lists.js b/models/lists.js
index ceda9ad1..bf5aae3c 100644
--- a/models/lists.js
+++ b/models/lists.js
@@ -63,13 +63,13 @@ Lists.attachSchema(new SimpleSchema({
Lists.allow({
insert(userId, doc) {
- return allowIsBoardMemberNonComment(userId, Boards.findOne(doc.boardId));
+ return allowIsBoardMemberCommentOnly(userId, Boards.findOne(doc.boardId));
},
update(userId, doc) {
- return allowIsBoardMemberNonComment(userId, Boards.findOne(doc.boardId));
+ return allowIsBoardMemberCommentOnly(userId, Boards.findOne(doc.boardId));
},
remove(userId, doc) {
- return allowIsBoardMemberNonComment(userId, Boards.findOne(doc.boardId));
+ return allowIsBoardMemberCommentOnly(userId, Boards.findOne(doc.boardId));
},
fetch: ['boardId'],
});
diff --git a/models/settings.js b/models/settings.js
index 34f693d9..3b9b4eae 100644
--- a/models/settings.js
+++ b/models/settings.js
@@ -96,6 +96,14 @@ if (Meteor.isServer) {
return (min + Math.round(rand * range));
}
+ function getEnvVar(name){
+ const value = process.env[name];
+ if (value){
+ return value;
+ }
+ throw new Meteor.Error(['var-not-exist', `The environment variable ${name} does not exist`]);
+ }
+
function sendInvitationEmail (_id){
const icode = InvitationCodes.findOne(_id);
const author = Users.findOne(Meteor.userId());
@@ -124,20 +132,33 @@ if (Meteor.isServer) {
sendInvitation(emails, boards) {
check(emails, [String]);
check(boards, [String]);
+
const user = Users.findOne(Meteor.userId());
if(!user.isAdmin){
throw new Meteor.Error('not-allowed');
}
emails.forEach((email) => {
if (email && SimpleSchema.RegEx.Email.test(email)) {
- const code = getRandomNum(100000, 999999);
- InvitationCodes.insert({code, email, boardsToBeInvited: boards, createdAt: new Date(), authorId: Meteor.userId()}, function(err, _id){
- if (!err && _id) {
- sendInvitationEmail(_id);
- } else {
- throw new Meteor.Error('invitation-generated-fail', err.message);
- }
- });
+ // Checks if the email is already link to an account.
+ const userExist = Users.findOne({email});
+ if (userExist){
+ throw new Meteor.Error('user-exist', `The user with the email ${email} has already an account.`);
+ }
+ // Checks if the email is already link to an invitation.
+ const invitation = InvitationCodes.findOne({email});
+ if (invitation){
+ InvitationCodes.update(invitation, {$set : {boardsToBeInvited: boards}});
+ sendInvitationEmail(invitation._id);
+ }else {
+ const code = getRandomNum(100000, 999999);
+ InvitationCodes.insert({code, email, boardsToBeInvited: boards, createdAt: new Date(), authorId: Meteor.userId()}, function(err, _id){
+ if (!err && _id) {
+ sendInvitationEmail(_id);
+ } else {
+ throw new Meteor.Error('invitation-generated-fail', err.message);
+ }
+ });
+ }
}
});
},
@@ -167,5 +188,14 @@ if (Meteor.isServer) {
email: user.emails[0].address,
};
},
+
+ getMatomoConf(){
+ return {
+ address: getEnvVar('MATOMO_ADDRESS'),
+ siteId: getEnvVar('MATOMO_SITE_ID'),
+ doNotTrack: process.env.MATOMO_DO_NOT_TRACK || false,
+ withUserName: process.env.MATOMO_WITH_USERNAME || false,
+ };
+ },
});
}
diff --git a/models/swimlanes.js b/models/swimlanes.js
index 72ef3f36..3559bcd2 100644
--- a/models/swimlanes.js
+++ b/models/swimlanes.js
@@ -46,13 +46,13 @@ Swimlanes.attachSchema(new SimpleSchema({
Swimlanes.allow({
insert(userId, doc) {
- return allowIsBoardMemberNonComment(userId, Boards.findOne(doc.boardId));
+ return allowIsBoardMemberCommentOnly(userId, Boards.findOne(doc.boardId));
},
update(userId, doc) {
- return allowIsBoardMemberNonComment(userId, Boards.findOne(doc.boardId));
+ return allowIsBoardMemberCommentOnly(userId, Boards.findOne(doc.boardId));
},
remove(userId, doc) {
- return allowIsBoardMemberNonComment(userId, Boards.findOne(doc.boardId));
+ return allowIsBoardMemberCommentOnly(userId, Boards.findOne(doc.boardId));
},
fetch: ['boardId'],
});
diff --git a/models/trelloCreator.js b/models/trelloCreator.js
index 30f0bc2b..b5a255cc 100644
--- a/models/trelloCreator.js
+++ b/models/trelloCreator.js
@@ -150,6 +150,7 @@ export class TrelloCreator {
userId: Meteor.userId(),
isAdmin: true,
isActive: true,
+ isNoComments: false,
isCommentOnly: false,
swimlaneId: false,
}],
@@ -177,6 +178,7 @@ export class TrelloCreator {
userId: wekanId,
isAdmin: this.getAdmin(trelloMembership.memberType),
isActive: true,
+ isNoComments: false,
isCommentOnly: false,
swimlaneId: false,
});
diff --git a/models/users.js b/models/users.js
index 5a7fbbe5..01673e4f 100644
--- a/models/users.js
+++ b/models/users.js
@@ -151,6 +151,16 @@ if (Meteor.isClient) {
return board && board.hasMember(this._id);
},
+ isNotNoComments() {
+ const board = Boards.findOne(Session.get('currentBoard'));
+ return board && board.hasMember(this._id) && !board.hasNoComments(this._id);
+ },
+
+ isNoComments() {
+ const board = Boards.findOne(Session.get('currentBoard'));
+ return board && board.hasNoComments(this._id);
+ },
+
isNotCommentOnly() {
const board = Boards.findOne(Session.get('currentBoard'));
return board && board.hasMember(this._id) && !board.hasCommentOnly(this._id);
@@ -478,6 +488,30 @@ if (Meteor.isServer) {
return user;
}
+ if (user.services.oidc) {
+ const email = user.services.oidc.email.toLowerCase();
+
+ user.username = user.services.oidc.username;
+ user.emails = [{ address: email, verified: true }];
+ const initials = user.services.oidc.fullname.match(/\b[a-zA-Z]/g).join('').toUpperCase();
+ user.profile = { initials, fullname: user.services.oidc.fullname };
+
+ // see if any existing user has this email address or username, otherwise create new
+ const existingUser = Meteor.users.findOne({$or: [{'emails.address': email}, {'username':user.username}]});
+ if (!existingUser)
+ return user;
+
+ // copy across new service info
+ const service = _.keys(user.services)[0];
+ existingUser.services[service] = user.services[service];
+ existingUser.emails = user.emails;
+ existingUser.username = user.username;
+ existingUser.profile = user.profile;
+
+ Meteor.users.remove({_id: existingUser._id}); // remove existing record
+ return existingUser;
+ }
+
if (options.from === 'admin') {
user.createdThroughApi = true;
return user;
@@ -501,9 +535,13 @@ if (Meteor.isServer) {
} else {
user.profile = {icode: options.profile.invitationcode};
user.profile.boardView = 'board-view-lists';
- }
- return user;
+ // Deletes the invitation code after the user was created successfully.
+ setTimeout(Meteor.bindEnvironment(() => {
+ InvitationCodes.remove({'_id': invitationCode._id});
+ }), 200);
+ return user;
+ }
});
}
@@ -618,9 +656,20 @@ if (Meteor.isServer) {
});
}
-
// USERS REST API
if (Meteor.isServer) {
+ // Middleware which checks that API is enabled.
+ JsonRoutes.Middleware.use(function (req, res, next) {
+ const api = req.url.search('api');
+ if (api === 1 && process.env.WITH_API === 'true' || api === -1){
+ return next();
+ }
+ else {
+ res.writeHead(301, {Location: '/'});
+ return res.end();
+ }
+ });
+
JsonRoutes.add('GET', '/api/user', function(req, res) {
try {
Authentication.checkLoggedIn(req.userId);
diff --git a/models/wekanCreator.js b/models/wekanCreator.js
index 33a2767a..6841a6ae 100644
--- a/models/wekanCreator.js
+++ b/models/wekanCreator.js
@@ -188,6 +188,7 @@ export class WekanCreator {
wekanId: Meteor.userId(),
isActive: true,
isAdmin: true,
+ isNoComments: false,
isCommentOnly: false,
swimlaneId: false,
}],