summaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
Diffstat (limited to 'models')
-rw-r--r--models/boards.js110
-rw-r--r--models/cardComments.js6
-rw-r--r--models/cards.js33
-rw-r--r--models/checklists.js13
-rw-r--r--models/lists.js59
-rw-r--r--models/swimlanes.js74
-rw-r--r--models/users.js82
7 files changed, 372 insertions, 5 deletions
diff --git a/models/boards.js b/models/boards.js
index 71831a63..0db2e48e 100644
--- a/models/boards.js
+++ b/models/boards.js
@@ -304,10 +304,32 @@ Boards.attachSchema(new SimpleSchema({
defaultValue: false,
optional: true,
},
+ type: {
+ /**
+ * The type of board
+ */
+ type: String,
+ defaultValue: 'board',
+ },
}));
Boards.helpers({
+ copy() {
+ const oldId = this._id;
+ delete this._id;
+ const _id = Boards.insert(this);
+
+ // Copy all swimlanes in board
+ Swimlanes.find({
+ boardId: oldId,
+ archived: false,
+ }).forEach((swimlane) => {
+ swimlane.type = 'swimlane';
+ swimlane.boardId = _id;
+ swimlane.copy(oldId);
+ });
+ },
/**
* Is supplied user authorized to view this board?
*/
@@ -456,6 +478,75 @@ Boards.helpers({
return _id;
},
+ searchBoards(term) {
+ check(term, Match.OneOf(String, null, undefined));
+
+ const query = { boardId: this._id };
+ query.type = 'cardType-linkedBoard';
+ query.archived = false;
+
+ const projection = { limit: 10, sort: { createdAt: -1 } };
+
+ if (term) {
+ const regex = new RegExp(term, 'i');
+
+ query.$or = [
+ { title: regex },
+ { description: regex },
+ ];
+ }
+
+ return Cards.find(query, projection);
+ },
+
+ searchSwimlanes(term) {
+ check(term, Match.OneOf(String, null, undefined));
+
+ const query = { boardId: this._id };
+ if (this.isTemplatesBoard()) {
+ query.type = 'template-swimlane';
+ query.archived = false;
+ } else {
+ query.type = {$nin: ['template-swimlane']};
+ }
+ const projection = { limit: 10, sort: { createdAt: -1 } };
+
+ if (term) {
+ const regex = new RegExp(term, 'i');
+
+ query.$or = [
+ { title: regex },
+ { description: regex },
+ ];
+ }
+
+ return Swimlanes.find(query, projection);
+ },
+
+ searchLists(term) {
+ check(term, Match.OneOf(String, null, undefined));
+
+ const query = { boardId: this._id };
+ if (this.isTemplatesBoard()) {
+ query.type = 'template-list';
+ query.archived = false;
+ } else {
+ query.type = {$nin: ['template-list']};
+ }
+ const projection = { limit: 10, sort: { createdAt: -1 } };
+
+ if (term) {
+ const regex = new RegExp(term, 'i');
+
+ query.$or = [
+ { title: regex },
+ { description: regex },
+ ];
+ }
+
+ return Lists.find(query, projection);
+ },
+
searchCards(term, excludeLinked) {
check(term, Match.OneOf(String, null, undefined));
@@ -463,6 +554,12 @@ Boards.helpers({
if (excludeLinked) {
query.linkedId = null;
}
+ if (this.isTemplatesBoard()) {
+ query.type = 'template-card';
+ query.archived = false;
+ } else {
+ query.type = {$nin: ['template-card']};
+ }
const projection = { limit: 10, sort: { createdAt: -1 } };
if (term) {
@@ -559,6 +656,13 @@ Boards.helpers({
});
},
+ isTemplateBoard() {
+ return this.type === 'template-board';
+ },
+
+ isTemplatesBoard() {
+ return this.type === 'template-container';
+ },
});
@@ -907,7 +1011,7 @@ if (Meteor.isServer) {
* @param {string} userId the ID of the user to retrieve the data
* @return_type [{_id: string,
title: string}]
- */
+ */
JsonRoutes.add('GET', '/api/users/:userId/boards', function (req, res) {
try {
Authentication.checkLoggedIn(req.userId);
@@ -944,7 +1048,7 @@ if (Meteor.isServer) {
*
* @return_type [{_id: string,
title: string}]
- */
+ */
JsonRoutes.add('GET', '/api/boards', function (req, res) {
try {
Authentication.checkUserId(req.userId);
@@ -1015,7 +1119,7 @@ if (Meteor.isServer) {
*
* @return_type {_id: string,
defaultSwimlaneId: string}
- */
+ */
JsonRoutes.add('POST', '/api/boards', function (req, res) {
try {
Authentication.checkUserId(req.userId);
diff --git a/models/cardComments.js b/models/cardComments.js
index 974c5ec9..fcb97104 100644
--- a/models/cardComments.js
+++ b/models/cardComments.js
@@ -67,6 +67,12 @@ CardComments.allow({
});
CardComments.helpers({
+ copy(newCardId) {
+ this.cardId = newCardId;
+ delete this._id;
+ CardComments.insert(this);
+ },
+
user() {
return Users.findOne(this.userId);
},
diff --git a/models/cards.js b/models/cards.js
index ff19a9a0..c733c7f8 100644
--- a/models/cards.js
+++ b/models/cards.js
@@ -246,7 +246,7 @@ Cards.attachSchema(new SimpleSchema({
* type of the card
*/
type: String,
- defaultValue: '',
+ defaultValue: 'cardType-card',
},
linkedId: {
/**
@@ -272,6 +272,31 @@ Cards.allow({
});
Cards.helpers({
+ copy() {
+ const oldId = this._id;
+ delete this._id;
+ const _id = Cards.insert(this);
+
+ // 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);
},
@@ -930,6 +955,10 @@ Cards.helpers({
return this.assignedBy;
}
},
+
+ isTemplateCard() {
+ return this.type === 'template-card';
+ },
});
Cards.mutations({
@@ -1230,7 +1259,7 @@ Cards.mutations({
function cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId) {
if ((_.contains(fieldNames, 'listId') && doc.listId !== oldListId) ||
- (_.contains(fieldNames, 'swimlaneId') && doc.swimlaneId !== oldSwimlaneId)){
+ (_.contains(fieldNames, 'swimlaneId') && doc.swimlaneId !== oldSwimlaneId)){
Activities.insert({
userId,
oldListId,
diff --git a/models/checklists.js b/models/checklists.js
index a372fafa..9e763f1a 100644
--- a/models/checklists.js
+++ b/models/checklists.js
@@ -48,6 +48,19 @@ Checklists.attachSchema(new SimpleSchema({
}));
Checklists.helpers({
+ copy(newCardId) {
+ const oldChecklistId = this._id;
+ this._id = null;
+ this.cardId = newCardId;
+ const newChecklistId = Checklists.insert(this);
+ ChecklistItems.find({checklistId: oldChecklistId}).forEach((item) => {
+ item._id = null;
+ item.checklistId = newChecklistId;
+ item.cardId = newCardId;
+ ChecklistItems.insert(item);
+ });
+ },
+
itemCount() {
return ChecklistItems.find({ checklistId: this._id }).count();
},
diff --git a/models/lists.js b/models/lists.js
index 54e7d037..d76c961c 100644
--- a/models/lists.js
+++ b/models/lists.js
@@ -27,6 +27,13 @@ Lists.attachSchema(new SimpleSchema({
*/
type: String,
},
+ swimlaneId: {
+ /**
+ * the swimlane associated to this list. Used for templates
+ */
+ type: String,
+ defaultValue: '',
+ },
createdAt: {
/**
* creation date
@@ -107,6 +114,13 @@ Lists.attachSchema(new SimpleSchema({
'saddlebrown', 'paleturquoise', 'mistyrose', 'indigo',
],
},
+ type: {
+ /**
+ * The type of list
+ */
+ type: String,
+ defaultValue: 'list',
+ },
}));
Lists.allow({
@@ -123,6 +137,37 @@ Lists.allow({
});
Lists.helpers({
+ copy(swimlaneId) {
+ const oldId = this._id;
+ const oldSwimlaneId = this.swimlaneId || null;
+ let _id = null;
+ existingListWithSameName = Lists.findOne({
+ boardId: this.boardId,
+ title: this.title,
+ archived: false,
+ });
+ if (existingListWithSameName) {
+ _id = existingListWithSameName._id;
+ } else {
+ delete this._id;
+ delete this.swimlaneId;
+ _id = Lists.insert(this);
+ }
+
+ // Copy all cards in list
+ Cards.find({
+ swimlaneId: oldSwimlaneId,
+ listId: oldId,
+ archived: false,
+ }).forEach((card) => {
+ card.type = 'cardType-card';
+ card.listId = _id;
+ card.boardId = this.boardId;
+ card.swimlaneId = swimlaneId;
+ card.copy();
+ });
+ },
+
cards(swimlaneId) {
const selector = {
listId: this._id,
@@ -169,6 +214,10 @@ Lists.helpers({
return this.color;
return '';
},
+
+ isTemplateList() {
+ return this.type === 'template-list';
+ },
});
Lists.mutations({
@@ -177,10 +226,20 @@ Lists.mutations({
},
archive() {
+ if (this.isTemplateList()) {
+ this.cards().forEach((card) => {
+ return card.archive();
+ });
+ }
return { $set: { archived: true } };
},
restore() {
+ if (this.isTemplateList()) {
+ this.allCards().forEach((card) => {
+ return card.restore();
+ });
+ }
return { $set: { archived: false } };
},
diff --git a/models/swimlanes.js b/models/swimlanes.js
index e2c3925c..a3427fc6 100644
--- a/models/swimlanes.js
+++ b/models/swimlanes.js
@@ -78,6 +78,13 @@ Swimlanes.attachSchema(new SimpleSchema({
}
},
},
+ type: {
+ /**
+ * The type of swimlane
+ */
+ type: String,
+ defaultValue: 'swimlane',
+ },
}));
Swimlanes.allow({
@@ -94,6 +101,28 @@ Swimlanes.allow({
});
Swimlanes.helpers({
+ copy(oldBoardId) {
+ const oldId = this._id;
+ delete this._id;
+ const _id = Swimlanes.insert(this);
+
+ const query = {
+ swimlaneId: {$in: [oldId, '']},
+ archived: false,
+ };
+ if (oldBoardId) {
+ query.boardId = oldBoardId;
+ }
+
+ // Copy all lists in swimlane
+ Lists.find(query).forEach((list) => {
+ list.type = 'list';
+ list.swimlaneId = oldId;
+ list.boardId = this.boardId;
+ list.copy(_id);
+ });
+ },
+
cards() {
return Cards.find(Filter.mongoSelector({
swimlaneId: this._id,
@@ -101,6 +130,18 @@ Swimlanes.helpers({
}), { sort: ['sort'] });
},
+ lists() {
+ return Lists.find(Filter.mongoSelector({
+ boardId: this.boardId,
+ swimlaneId: {$in: [this._id, '']},
+ archived: false,
+ }), { sort: ['sort'] });
+ },
+
+ allLists() {
+ return Lists.find({ swimlaneId: this._id });
+ },
+
allCards() {
return Cards.find({ swimlaneId: this._id });
},
@@ -114,6 +155,29 @@ Swimlanes.helpers({
return this.color;
return '';
},
+
+ isTemplateSwimlane() {
+ return this.type === 'template-swimlane';
+ },
+
+ isTemplateContainer() {
+ return this.type === 'template-container';
+ },
+
+ isListTemplatesSwimlane() {
+ const user = Users.findOne(Meteor.userId());
+ return user.profile.listTemplatesSwimlaneId === this._id;
+ },
+
+ isCardTemplatesSwimlane() {
+ const user = Users.findOne(Meteor.userId());
+ return user.profile.cardTemplatesSwimlaneId === this._id;
+ },
+
+ isBoardTemplatesSwimlane() {
+ const user = Users.findOne(Meteor.userId());
+ return user.profile.boardTemplatesSwimlaneId === this._id;
+ },
});
Swimlanes.mutations({
@@ -122,10 +186,20 @@ Swimlanes.mutations({
},
archive() {
+ if (this.isTemplateSwimlane()) {
+ this.lists().forEach((list) => {
+ return list.archive();
+ });
+ }
return { $set: { archived: true } };
},
restore() {
+ if (this.isTemplateSwimlane()) {
+ this.allLists().forEach((list) => {
+ return list.restore();
+ });
+ }
return { $set: { archived: false } };
},
diff --git a/models/users.js b/models/users.js
index c6c0f857..7152d133 100644
--- a/models/users.js
+++ b/models/users.js
@@ -159,6 +159,34 @@ Users.attachSchema(new SimpleSchema({
'board-view-cal',
],
},
+ 'profile.templatesBoardId': {
+ /**
+ * Reference to the templates board
+ */
+ type: String,
+ defaultValue: '',
+ },
+ 'profile.cardTemplatesSwimlaneId': {
+ /**
+ * Reference to the card templates swimlane Id
+ */
+ type: String,
+ defaultValue: '',
+ },
+ 'profile.listTemplatesSwimlaneId': {
+ /**
+ * Reference to the list templates swimlane Id
+ */
+ type: String,
+ defaultValue: '',
+ },
+ 'profile.boardTemplatesSwimlaneId': {
+ /**
+ * Reference to the board templates swimlane Id
+ */
+ type: String,
+ defaultValue: '',
+ },
services: {
/**
* services field of the user
@@ -328,6 +356,14 @@ Users.helpers({
const profile = this.profile || {};
return profile.language || 'en';
},
+
+ getTemplatesBoardId() {
+ return this.profile.templatesBoardId;
+ },
+
+ getTemplatesBoardSlug() {
+ return Boards.findOne(this.profile.templatesBoardId).slug;
+ },
});
Users.mutations({
@@ -701,6 +737,52 @@ if (Meteor.isServer) {
Lists.insert({title: TAPi18n.__(title), boardId, sort: titleIndex}, fakeUser);
});
});
+
+ Boards.insert({
+ title: TAPi18n.__('templates'),
+ permission: 'private',
+ type: 'template-container',
+ }, fakeUser, (err, boardId) => {
+
+ // Insert the reference to our templates board
+ Users.update(fakeUserId.get(), {$set: {'profile.templatesBoardId': boardId}});
+
+ // Insert the card templates swimlane
+ Swimlanes.insert({
+ title: TAPi18n.__('card-templates-swimlane'),
+ boardId,
+ sort: 1,
+ type: 'template-container',
+ }, fakeUser, (err, swimlaneId) => {
+
+ // Insert the reference to out card templates swimlane
+ Users.update(fakeUserId.get(), {$set: {'profile.cardTemplatesSwimlaneId': swimlaneId}});
+ });
+
+ // Insert the list templates swimlane
+ Swimlanes.insert({
+ title: TAPi18n.__('list-templates-swimlane'),
+ boardId,
+ sort: 2,
+ type: 'template-container',
+ }, fakeUser, (err, swimlaneId) => {
+
+ // Insert the reference to out list templates swimlane
+ Users.update(fakeUserId.get(), {$set: {'profile.listTemplatesSwimlaneId': swimlaneId}});
+ });
+
+ // Insert the board templates swimlane
+ Swimlanes.insert({
+ title: TAPi18n.__('board-templates-swimlane'),
+ boardId,
+ sort: 3,
+ type: 'template-container',
+ }, fakeUser, (err, swimlaneId) => {
+
+ // Insert the reference to out board templates swimlane
+ Users.update(fakeUserId.get(), {$set: {'profile.boardTemplatesSwimlaneId': swimlaneId}});
+ });
+ });
});
});
}