From c3037b155fc0de1ef44d1d1c1fc65c25790ca3ad Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Sun, 17 Jun 2018 22:46:03 +0300 Subject: Added subtasks model and APIs for it --- models/subtasks.js | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 models/subtasks.js (limited to 'models') diff --git a/models/subtasks.js b/models/subtasks.js new file mode 100644 index 00000000..6c42072e --- /dev/null +++ b/models/subtasks.js @@ -0,0 +1,139 @@ +Subtasks = new Mongo.Collection('subtasks'); + +Subtasks.attachSchema(new SimpleSchema({ + title: { + type: String, + }, + sort: { + type: Number, + decimal: true, + }, + isFinished: { + type: Boolean, + defaultValue: false, + }, + cardId: { + type: String, + }, +})); + +Subtasks.allow({ + insert(userId, doc) { + return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId)); + }, + update(userId, doc) { + return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId)); + }, + remove(userId, doc) { + return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId)); + }, + fetch: ['userId', 'cardId'], +}); + +Subtasks.before.insert((userId, doc) => { + if (!doc.userId) { + doc.userId = userId; + } +}); + +// Mutations +Subtasks.mutations({ + setTitle(title) { + return { $set: { title } }; + }, + toggleItem() { + return { $set: { isFinished: !this.isFinished } }; + }, + move(sortIndex) { + const mutatedFields = { + sort: sortIndex, + }; + + return {$set: mutatedFields}; + }, +}); + +// Activities helper +function itemCreation(userId, doc) { + const card = Cards.findOne(doc.cardId); + const boardId = card.boardId; + Activities.insert({ + userId, + activityType: 'addSubtaskItem', + cardId: doc.cardId, + boardId, + subtaskItemId: doc._id, + }); +} + +function itemRemover(userId, doc) { + Activities.remove({ + subtaskItemId: doc._id, + }); +} + +// Activities +if (Meteor.isServer) { + Meteor.startup(() => { + Subtasks._collection._ensureIndex({ cardId: 1 }); + }); + + Subtasks.after.insert((userId, doc) => { + itemCreation(userId, doc); + }); + + Subtasks.after.remove((userId, doc) => { + itemRemover(userId, doc); + }); +} + +// APIs +if (Meteor.isServer) { + JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/subtasks/:itemId', function (req, res) { + Authentication.checkUserId( req.userId); + const paramItemId = req.params.itemId; + const subtaskItem = Subtasks.findOne({ _id: paramItemId }); + if (subtaskItem) { + JsonRoutes.sendResult(res, { + code: 200, + data: subtaskItem, + }); + } else { + JsonRoutes.sendResult(res, { + code: 500, + }); + } + }); + + JsonRoutes.add('PUT', '/api/boards/:boardId/cards/:cardId/subtasks/:itemId', function (req, res) { + Authentication.checkUserId( req.userId); + + const paramItemId = req.params.itemId; + + if (req.body.hasOwnProperty('isFinished')) { + Subtasks.direct.update({_id: paramItemId}, {$set: {isFinished: req.body.isFinished}}); + } + if (req.body.hasOwnProperty('title')) { + Subtasks.direct.update({_id: paramItemId}, {$set: {title: req.body.title}}); + } + + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: paramItemId, + }, + }); + }); + + JsonRoutes.add('DELETE', '/api/boards/:boardId/cards/:cardId/subtasks/:itemId', function (req, res) { + Authentication.checkUserId( req.userId); + const paramItemId = req.params.itemId; + Subtasks.direct.remove({ _id: paramItemId }); + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: paramItemId, + }, + }); + }); +} -- cgit v1.2.3-1-g7c22 From b627ced605f0ab98eb2977420da954f31df4f592 Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Sun, 17 Jun 2018 22:55:01 +0300 Subject: Some inspiration from checklists to subtasks --- models/subtasks.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'models') diff --git a/models/subtasks.js b/models/subtasks.js index 6c42072e..e842d11d 100644 --- a/models/subtasks.js +++ b/models/subtasks.js @@ -4,6 +4,29 @@ Subtasks.attachSchema(new SimpleSchema({ title: { type: String, }, + startAt: { // this is a predicted time + type: Date, + optional: true, + }, + endAt: { // this is a predicted time + type: Date, + optional: true, + }, + finishedAt: { // The date & time when it is marked as being done + type: Date, + optional: true, + }, + createdAt: { + type: Date, + denyUpdate: false, + autoValue() { // eslint-disable-line consistent-return + if (this.isInsert) { + return new Date(); + } else { + this.unset(); + } + }, + }, sort: { type: Number, decimal: true, @@ -17,6 +40,16 @@ Subtasks.attachSchema(new SimpleSchema({ }, })); +Subtasks.helpers({ + isFinished() { + return 0 !== this.itemCount() && this.itemCount() === this.finishedCount(); + }, + itemIndex(itemId) { + const items = self.findOne({_id : this._id}).items; + return _.pluck(items, '_id').indexOf(itemId); + }, +}); + Subtasks.allow({ insert(userId, doc) { return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId)); @@ -31,6 +64,7 @@ Subtasks.allow({ }); Subtasks.before.insert((userId, doc) => { + doc.createdAt = new Date(); if (!doc.userId) { doc.userId = userId; } -- cgit v1.2.3-1-g7c22 From d59583915cca24d53a11251c54ca7caf6b5edb4e Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Mon, 18 Jun 2018 23:25:56 +0300 Subject: Initial implementation for subtasks --- models/activities.js | 3 +++ models/cards.js | 24 ++++++++++++++++++++++++ models/export.js | 2 ++ models/subtasks.js | 8 +------- 4 files changed, 30 insertions(+), 7 deletions(-) (limited to 'models') diff --git a/models/activities.js b/models/activities.js index f64b53f8..1ff0a299 100644 --- a/models/activities.js +++ b/models/activities.js @@ -44,6 +44,9 @@ Activities.helpers({ checklistItem() { return ChecklistItems.findOne(this.checklistItemId); }, + subtasks() { + return Subtasks.findOne(this.subtaskId); + }, customField() { return CustomFields.findOne(this.customFieldId); }, diff --git a/models/cards.js b/models/cards.js index 00ec14c2..6edffb79 100644 --- a/models/cards.js +++ b/models/cards.js @@ -215,6 +215,27 @@ Cards.helpers({ return this.checklistItemCount() !== 0; }, + subtasks() { + return Subtasks.find({cardId: this._id}, {sort: { sort: 1 } }); + }, + + subtasksCount() { + return Subtasks.find({cardId: this._id}).count(); + }, + + subtasksFinishedCount() { + return Subtasks.find({cardId: this._id, isFinished: true}).count(); + }, + + subtasksFinished() { + const finishCount = this.subtasksFinishedCount(); + return finishCount > 0 && this.subtasksCount() === finishCount; + }, + + hasSubtasks() { + return this.subtasksCount() !== 0; + }, + customFieldIndex(customFieldId) { return _.pluck(this.customFields, '_id').indexOf(customFieldId); }, @@ -513,6 +534,9 @@ function cardRemover(userId, doc) { Checklists.remove({ cardId: doc._id, }); + Subtasks.remove({ + cardId: doc._id, + }); CardComments.remove({ cardId: doc._id, }); diff --git a/models/export.js b/models/export.js index aff66801..778633f9 100644 --- a/models/export.js +++ b/models/export.js @@ -58,9 +58,11 @@ class Exporter { result.activities = Activities.find(byBoard, noBoardId).fetch(); result.checklists = []; result.checklistItems = []; + result.subtaskItems = []; result.cards.forEach((card) => { result.checklists.push(...Checklists.find({ cardId: card._id }).fetch()); result.checklistItems.push(...ChecklistItems.find({ cardId: card._id }).fetch()); + result.subtaskItems.push(...Subtasks.find({ cardId: card._id }).fetch()); }); // [Old] for attachments we only export IDs and absolute url to original doc diff --git a/models/subtasks.js b/models/subtasks.js index e842d11d..3f8b932c 100644 --- a/models/subtasks.js +++ b/models/subtasks.js @@ -41,13 +41,7 @@ Subtasks.attachSchema(new SimpleSchema({ })); Subtasks.helpers({ - isFinished() { - return 0 !== this.itemCount() && this.itemCount() === this.finishedCount(); - }, - itemIndex(itemId) { - const items = self.findOne({_id : this._id}).items; - return _.pluck(items, '_id').indexOf(itemId); - }, + // ... }); Subtasks.allow({ -- cgit v1.2.3-1-g7c22 From 879a84184ff2f9b8719f5ac1e25d35e0fa5f52fb Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Tue, 19 Jun 2018 00:42:54 +0300 Subject: added ability to create a tree of cards --- models/cards.js | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index 6edffb79..c5d7cdf9 100644 --- a/models/cards.js +++ b/models/cards.js @@ -15,6 +15,11 @@ Cards.attachSchema(new SimpleSchema({ } }, }, + parentId: { + type: String, + optional: true, + defaultValue: '', + }, listId: { type: String, }, -- cgit v1.2.3-1-g7c22 From fd465fbb60bd92c169991e050b094904c2eec95e Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Tue, 19 Jun 2018 01:00:14 +0300 Subject: Helpers for dealing with trees of cards --- models/cards.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index c5d7cdf9..1e001501 100644 --- a/models/cards.js +++ b/models/cards.js @@ -297,14 +297,33 @@ Cards.helpers({ } return true; }, + + parentCard() { + if (this.parentId === '') { + return null; + } + return Cards.findOne(this.parentId); + }, + + isTopLevel() { + return this.parentId === ''; + }, }); Cards.mutations({ + applyToKids(funct) { + Cards.find({ parentId: this._id }).forEach((card) => { + funct(card); + }); + }, + archive() { + this.applyToKids((card) => { return card.archive(); }); return {$set: {archived: true}}; }, restore() { + this.applyToKids((card) => { return card.restore(); }); return {$set: {archived: false}}; }, -- cgit v1.2.3-1-g7c22 From adb7f5b2ca9e8394db314f7ff97d0d0f811c51c0 Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Sat, 23 Jun 2018 17:40:53 +0300 Subject: Switch from subtasks to cards in model --- models/cards.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index 1e001501..8cf0ef65 100644 --- a/models/cards.js +++ b/models/cards.js @@ -221,15 +221,15 @@ Cards.helpers({ }, subtasks() { - return Subtasks.find({cardId: this._id}, {sort: { sort: 1 } }); + return Cards.find({parentId: this._id}, {sort: { sort: 1 } }); }, subtasksCount() { - return Subtasks.find({cardId: this._id}).count(); + return Cards.find({parentId: this._id}).count(); }, subtasksFinishedCount() { - return Subtasks.find({cardId: this._id, isFinished: true}).count(); + return Cards.find({parentId: this._id, archived: true}).count(); }, subtasksFinished() { -- cgit v1.2.3-1-g7c22 From 5a023e431504f7573723db6e0d2262ecb1fc61c5 Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Sat, 23 Jun 2018 23:22:38 +0300 Subject: Can add cards as subtasks --- models/boards.js | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'models') diff --git a/models/boards.js b/models/boards.js index 911d82a1..6836a320 100644 --- a/models/boards.js +++ b/models/boards.js @@ -151,6 +151,16 @@ Boards.attachSchema(new SimpleSchema({ type: String, optional: true, }, + subtasksDefaultBoardId: { + type: String, + optional: true, + defaultValue: null, + }, + subtasksDefaultListId: { + type: String, + optional: true, + defaultValue: null, + }, })); @@ -284,8 +294,52 @@ Boards.helpers({ return Cards.find(query, projection); }, + // A board alwasy has another board where it deposits subtasks of thasks + // that belong to itself. + getDefaultSubtasksBoardId() { + if (this.subtasksDefaultBoardId === null) { + this.subtasksDefaultBoardId = Boards.insert({ + title: `^${this.title}^`, + permission: this.permission, + members: this.members, + color: this.color, + description: TAPi18n.__('default-subtasks-board', {board: this.title}), + }); + + Swimlanes.insert({ + title: TAPi18n.__('default'), + boardId: this.subtasksDefaultBoardId, + }); + Boards.update(this._id, {$set: { + subtasksDefaultBoardId: this.subtasksDefaultBoardId, + }}); + } + return this.subtasksDefaultBoardId; + }, + + getDefaultSubtasksBoard() { + return Boards.findOne(this.getDefaultSubtasksBoardId()); + }, + + getDefaultSubtasksListId() { + if (this.subtasksDefaultListId === null) { + this.subtasksDefaultListId = Lists.insert({ + title: TAPi18n.__('new'), + boardId: this._id, + }); + Boards.update(this._id, {$set: { + subtasksDefaultListId: this.subtasksDefaultListId, + }}); + } + return this.subtasksDefaultListId; + }, + + getDefaultSubtasksList() { + return Lists.findOne(this.getDefaultSubtasksListId()); + }, }); + Boards.mutations({ archive() { return { $set: { archived: true } }; -- cgit v1.2.3-1-g7c22 From 6ab1cbb341da44cbaffc296ea177a8cd146e5244 Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Sat, 23 Jun 2018 23:31:44 +0300 Subject: Take archived status into consideration for subtasks --- models/cards.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index 8cf0ef65..ca0e16be 100644 --- a/models/cards.js +++ b/models/cards.js @@ -221,15 +221,30 @@ Cards.helpers({ }, subtasks() { - return Cards.find({parentId: this._id}, {sort: { sort: 1 } }); + 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}).count(); + return Cards.find({ + parentId: this._id, + archived: false, + }).count(); }, subtasksFinishedCount() { - return Cards.find({parentId: this._id, archived: true}).count(); + return Cards.find({ + parentId: this._id, + archived: true}).count(); }, subtasksFinished() { -- cgit v1.2.3-1-g7c22 From 4ac6a507cdd3e7a4610c8961e0a9f76f945a5e6d Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Sun, 24 Jun 2018 00:21:23 +0300 Subject: Get rid of old implementation for substacks --- models/activities.js | 2 +- models/cards.js | 6 ++ models/export.js | 2 +- models/subtasks.js | 167 --------------------------------------------------- 4 files changed, 8 insertions(+), 169 deletions(-) delete mode 100644 models/subtasks.js (limited to 'models') diff --git a/models/activities.js b/models/activities.js index 1ff0a299..5b54759c 100644 --- a/models/activities.js +++ b/models/activities.js @@ -45,7 +45,7 @@ Activities.helpers({ return ChecklistItems.findOne(this.checklistItemId); }, subtasks() { - return Subtasks.findOne(this.subtaskId); + return Cards.findOne(this.subtaskId); }, customField() { return CustomFields.findOne(this.customFieldId); diff --git a/models/cards.js b/models/cards.js index ca0e16be..4ec3ea7c 100644 --- a/models/cards.js +++ b/models/cards.js @@ -127,6 +127,12 @@ Cards.attachSchema(new SimpleSchema({ type: Number, decimal: true, }, + subtaskSort: { + type: Number, + decimal: true, + defaultValue: -1, + optional: true, + }, })); Cards.allow({ diff --git a/models/export.js b/models/export.js index 778633f9..8c4c29d4 100644 --- a/models/export.js +++ b/models/export.js @@ -62,7 +62,7 @@ class Exporter { result.cards.forEach((card) => { result.checklists.push(...Checklists.find({ cardId: card._id }).fetch()); result.checklistItems.push(...ChecklistItems.find({ cardId: card._id }).fetch()); - result.subtaskItems.push(...Subtasks.find({ cardId: card._id }).fetch()); + result.subtaskItems.push(...Cards.find({ parentid: card._id }).fetch()); }); // [Old] for attachments we only export IDs and absolute url to original doc diff --git a/models/subtasks.js b/models/subtasks.js deleted file mode 100644 index 3f8b932c..00000000 --- a/models/subtasks.js +++ /dev/null @@ -1,167 +0,0 @@ -Subtasks = new Mongo.Collection('subtasks'); - -Subtasks.attachSchema(new SimpleSchema({ - title: { - type: String, - }, - startAt: { // this is a predicted time - type: Date, - optional: true, - }, - endAt: { // this is a predicted time - type: Date, - optional: true, - }, - finishedAt: { // The date & time when it is marked as being done - type: Date, - optional: true, - }, - createdAt: { - type: Date, - denyUpdate: false, - autoValue() { // eslint-disable-line consistent-return - if (this.isInsert) { - return new Date(); - } else { - this.unset(); - } - }, - }, - sort: { - type: Number, - decimal: true, - }, - isFinished: { - type: Boolean, - defaultValue: false, - }, - cardId: { - type: String, - }, -})); - -Subtasks.helpers({ - // ... -}); - -Subtasks.allow({ - insert(userId, doc) { - return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId)); - }, - update(userId, doc) { - return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId)); - }, - remove(userId, doc) { - return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId)); - }, - fetch: ['userId', 'cardId'], -}); - -Subtasks.before.insert((userId, doc) => { - doc.createdAt = new Date(); - if (!doc.userId) { - doc.userId = userId; - } -}); - -// Mutations -Subtasks.mutations({ - setTitle(title) { - return { $set: { title } }; - }, - toggleItem() { - return { $set: { isFinished: !this.isFinished } }; - }, - move(sortIndex) { - const mutatedFields = { - sort: sortIndex, - }; - - return {$set: mutatedFields}; - }, -}); - -// Activities helper -function itemCreation(userId, doc) { - const card = Cards.findOne(doc.cardId); - const boardId = card.boardId; - Activities.insert({ - userId, - activityType: 'addSubtaskItem', - cardId: doc.cardId, - boardId, - subtaskItemId: doc._id, - }); -} - -function itemRemover(userId, doc) { - Activities.remove({ - subtaskItemId: doc._id, - }); -} - -// Activities -if (Meteor.isServer) { - Meteor.startup(() => { - Subtasks._collection._ensureIndex({ cardId: 1 }); - }); - - Subtasks.after.insert((userId, doc) => { - itemCreation(userId, doc); - }); - - Subtasks.after.remove((userId, doc) => { - itemRemover(userId, doc); - }); -} - -// APIs -if (Meteor.isServer) { - JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/subtasks/:itemId', function (req, res) { - Authentication.checkUserId( req.userId); - const paramItemId = req.params.itemId; - const subtaskItem = Subtasks.findOne({ _id: paramItemId }); - if (subtaskItem) { - JsonRoutes.sendResult(res, { - code: 200, - data: subtaskItem, - }); - } else { - JsonRoutes.sendResult(res, { - code: 500, - }); - } - }); - - JsonRoutes.add('PUT', '/api/boards/:boardId/cards/:cardId/subtasks/:itemId', function (req, res) { - Authentication.checkUserId( req.userId); - - const paramItemId = req.params.itemId; - - if (req.body.hasOwnProperty('isFinished')) { - Subtasks.direct.update({_id: paramItemId}, {$set: {isFinished: req.body.isFinished}}); - } - if (req.body.hasOwnProperty('title')) { - Subtasks.direct.update({_id: paramItemId}, {$set: {title: req.body.title}}); - } - - JsonRoutes.sendResult(res, { - code: 200, - data: { - _id: paramItemId, - }, - }); - }); - - JsonRoutes.add('DELETE', '/api/boards/:boardId/cards/:cardId/subtasks/:itemId', function (req, res) { - Authentication.checkUserId( req.userId); - const paramItemId = req.params.itemId; - Subtasks.direct.remove({ _id: paramItemId }); - JsonRoutes.sendResult(res, { - code: 200, - data: { - _id: paramItemId, - }, - }); - }); -} -- cgit v1.2.3-1-g7c22 From 50f170b75d9d149bd333091fb5f7ba150c245b15 Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Sun, 24 Jun 2018 10:59:48 +0300 Subject: A better name for incoming subtasks --- models/boards.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'models') diff --git a/models/boards.js b/models/boards.js index 6836a320..6f0f7293 100644 --- a/models/boards.js +++ b/models/boards.js @@ -324,7 +324,7 @@ Boards.helpers({ getDefaultSubtasksListId() { if (this.subtasksDefaultListId === null) { this.subtasksDefaultListId = Lists.insert({ - title: TAPi18n.__('new'), + title: TAPi18n.__('queue'), boardId: this._id, }); Boards.update(this._id, {$set: { -- cgit v1.2.3-1-g7c22 From aead18eb58e230ed06ac090f7ea36c64fd597992 Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Sun, 24 Jun 2018 17:47:57 +0300 Subject: change all mentions of Kids => Children --- models/cards.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index 4ec3ea7c..2563fdf8 100644 --- a/models/cards.js +++ b/models/cards.js @@ -332,19 +332,19 @@ Cards.helpers({ }); Cards.mutations({ - applyToKids(funct) { + applyToChildren(funct) { Cards.find({ parentId: this._id }).forEach((card) => { funct(card); }); }, archive() { - this.applyToKids((card) => { return card.archive(); }); + this.applyToChildren((card) => { return card.archive(); }); return {$set: {archived: true}}; }, restore() { - this.applyToKids((card) => { return card.restore(); }); + this.applyToChildren((card) => { return card.restore(); }); return {$set: {archived: false}}; }, -- cgit v1.2.3-1-g7c22 From 04745f0c2fe83f044032713e1864c5ac00d38ac9 Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Mon, 25 Jun 2018 22:01:02 +0300 Subject: implement getDefaultSwimline for boards --- models/boards.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'models') diff --git a/models/boards.js b/models/boards.js index 6f0f7293..a8b7191e 100644 --- a/models/boards.js +++ b/models/boards.js @@ -337,6 +337,18 @@ Boards.helpers({ getDefaultSubtasksList() { return Lists.findOne(this.getDefaultSubtasksListId()); }, + + getDefaultSwimline() { + let result = Swimlanes.findOne({boardId: this._id}); + if (result === undefined) { + Swimlanes.insert({ + title: TAPi18n.__('default'), + boardId: this._id, + }); + result = Swimlanes.findOne({boardId: this._id}); + } + return result; + }, }); -- cgit v1.2.3-1-g7c22 From c9f70cf382707141561732a46dbd3e3e2f159e6e Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Mon, 25 Jun 2018 22:52:50 +0300 Subject: Check for null and undefined in board defaults --- models/boards.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'models') diff --git a/models/boards.js b/models/boards.js index a8b7191e..fce674ae 100644 --- a/models/boards.js +++ b/models/boards.js @@ -297,7 +297,7 @@ Boards.helpers({ // A board alwasy has another board where it deposits subtasks of thasks // that belong to itself. getDefaultSubtasksBoardId() { - if (this.subtasksDefaultBoardId === null) { + if ((this.subtasksDefaultBoardId === null) || (this.subtasksDefaultBoardId === undefined)) { this.subtasksDefaultBoardId = Boards.insert({ title: `^${this.title}^`, permission: this.permission, @@ -322,7 +322,7 @@ Boards.helpers({ }, getDefaultSubtasksListId() { - if (this.subtasksDefaultListId === null) { + if ((this.subtasksDefaultListId === null) || (this.subtasksDefaultListId === undefined)) { this.subtasksDefaultListId = Lists.insert({ title: TAPi18n.__('queue'), boardId: this._id, -- cgit v1.2.3-1-g7c22 From 94a52080cff14f7587c0ee837c1fca131cd6aff0 Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Mon, 25 Jun 2018 23:12:20 +0300 Subject: Board level settings for subtasks --- models/boards.js | 16 ++++++++++++++++ models/cards.js | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'models') diff --git a/models/boards.js b/models/boards.js index fce674ae..b5b0b0fc 100644 --- a/models/boards.js +++ b/models/boards.js @@ -161,6 +161,10 @@ Boards.attachSchema(new SimpleSchema({ optional: true, defaultValue: null, }, + allowsSubtasks: { + type: Boolean, + defaultValue: true, + }, })); @@ -473,6 +477,18 @@ Boards.mutations({ }, }; }, + + setAllowsSubtasks(allowsSubtasks) { + return { $set: { allowsSubtasks } }; + }, + + setSubtasksDefaultBoardId(subtasksDefaultBoardId) { + return { $set: { subtasksDefaultBoardId } }; + }, + + setSubtasksDefaultListId(subtasksDefaultListId) { + return { $set: { subtasksDefaultListId } }; + }, }); if (Meteor.isServer) { diff --git a/models/cards.js b/models/cards.js index 2563fdf8..8d7a93d0 100644 --- a/models/cards.js +++ b/models/cards.js @@ -258,7 +258,7 @@ Cards.helpers({ return finishCount > 0 && this.subtasksCount() === finishCount; }, - hasSubtasks() { + allowsSubtasks() { return this.subtasksCount() !== 0; }, -- cgit v1.2.3-1-g7c22 From c0ffd6c20f2a04bd1436ea2f0953f1c3c8afe145 Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Tue, 26 Jun 2018 02:13:31 +0300 Subject: Show parent in card (no links, yet) --- models/boards.js | 16 ++++++++++++++++ models/cards.js | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) (limited to 'models') diff --git a/models/boards.js b/models/boards.js index b5b0b0fc..2d80a56a 100644 --- a/models/boards.js +++ b/models/boards.js @@ -165,6 +165,18 @@ Boards.attachSchema(new SimpleSchema({ type: Boolean, defaultValue: true, }, + presentParentTask: { + type: String, + allowedValues: [ + 'prefix-with-full-path', + 'prefix-with-parent', + 'subtext-with-full-path', + 'subtext-with-parent', + 'no-parent', + ], + optional: true, + defaultValue: 'no-parent', + }, })); @@ -489,6 +501,10 @@ Boards.mutations({ setSubtasksDefaultListId(subtasksDefaultListId) { return { $set: { subtasksDefaultListId } }; }, + + setPresentParentTask(presentParentTask) { + return { $set: { presentParentTask } }; + }, }); if (Meteor.isServer) { diff --git a/models/cards.js b/models/cards.js index 8d7a93d0..323ec407 100644 --- a/models/cards.js +++ b/models/cards.js @@ -326,6 +326,59 @@ Cards.helpers({ return Cards.findOne(this.parentId); }, + parentCardName() { + if (this.parentId === '') { + return ''; + } + return Cards.findOne(this.parentId).title; + }, + + 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 === ''; }, -- cgit v1.2.3-1-g7c22 From b7d508e8c4cf858559e144053d119ceaebfa9697 Mon Sep 17 00:00:00 2001 From: Nicu Tofan Date: Tue, 26 Jun 2018 17:39:31 +0300 Subject: Added ability to change card's parent. --- models/boards.js | 4 ++++ models/cards.js | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'models') diff --git a/models/boards.js b/models/boards.js index 2d80a56a..c83050c0 100644 --- a/models/boards.js +++ b/models/boards.js @@ -220,6 +220,10 @@ 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; diff --git a/models/cards.js b/models/cards.js index 323ec407..b6a7b4c6 100644 --- a/models/cards.js +++ b/models/cards.js @@ -327,10 +327,14 @@ Cards.helpers({ }, parentCardName() { - if (this.parentId === '') { - return ''; + let result = ''; + if (this.parentId !== '') { + const card = Cards.findOne(this.parentId); + if (card) { + result = card.title; + } } - return Cards.findOne(this.parentId).title; + return result; }, parentListId() { @@ -541,6 +545,11 @@ Cards.mutations({ unsetSpentTime() { return {$unset: {spentTime: '', isOvertime: false}}; }, + + setParentId(parentId) { + return {$set: {parentId}}; + }, + }); -- cgit v1.2.3-1-g7c22