summaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
Diffstat (limited to 'models')
-rw-r--r--models/activities.js3
-rw-r--r--models/boards.js102
-rw-r--r--models/cards.js131
-rw-r--r--models/export.js2
4 files changed, 238 insertions, 0 deletions
diff --git a/models/activities.js b/models/activities.js
index f64b53f8..5b54759c 100644
--- a/models/activities.js
+++ b/models/activities.js
@@ -44,6 +44,9 @@ Activities.helpers({
checklistItem() {
return ChecklistItems.findOne(this.checklistItemId);
},
+ subtasks() {
+ return Cards.findOne(this.subtaskId);
+ },
customField() {
return CustomFields.findOne(this.customFieldId);
},
diff --git a/models/boards.js b/models/boards.js
index 3b6c280b..76a8f704 100644
--- a/models/boards.js
+++ b/models/boards.js
@@ -151,6 +151,32 @@ Boards.attachSchema(new SimpleSchema({
type: String,
optional: true,
},
+ subtasksDefaultBoardId: {
+ type: String,
+ optional: true,
+ defaultValue: null,
+ },
+ subtasksDefaultListId: {
+ type: String,
+ optional: true,
+ defaultValue: null,
+ },
+ allowsSubtasks: {
+ 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',
+ },
}));
@@ -194,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;
@@ -284,6 +314,61 @@ 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 === undefined)) {
+ 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 === undefined)) {
+ this.subtasksDefaultListId = Lists.insert({
+ title: TAPi18n.__('queue'),
+ boardId: this._id,
+ });
+ Boards.update(this._id, {$set: {
+ subtasksDefaultListId: this.subtasksDefaultListId,
+ }});
+ }
+ return this.subtasksDefaultListId;
+ },
+
+ 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;
+ },
cardsInInterval(start, end) {
return Cards.find({
@@ -313,6 +398,7 @@ Boards.helpers({
});
+
Boards.mutations({
archive() {
return { $set: { archived: true } };
@@ -434,6 +520,22 @@ Boards.mutations({
},
};
},
+
+ setAllowsSubtasks(allowsSubtasks) {
+ return { $set: { allowsSubtasks } };
+ },
+
+ setSubtasksDefaultBoardId(subtasksDefaultBoardId) {
+ return { $set: { subtasksDefaultBoardId } };
+ },
+
+ setSubtasksDefaultListId(subtasksDefaultListId) {
+ return { $set: { subtasksDefaultListId } };
+ },
+
+ setPresentParentTask(presentParentTask) {
+ return { $set: { presentParentTask } };
+ },
});
if (Meteor.isServer) {
diff --git a/models/cards.js b/models/cards.js
index 00ec14c2..b6a7b4c6 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,
},
@@ -122,6 +127,12 @@ Cards.attachSchema(new SimpleSchema({
type: Number,
decimal: true,
},
+ subtaskSort: {
+ type: Number,
+ decimal: true,
+ defaultValue: -1,
+ optional: true,
+ },
}));
Cards.allow({
@@ -215,6 +226,42 @@ 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);
},
@@ -271,14 +318,90 @@ Cards.helpers({
}
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 === '';
+ },
});
Cards.mutations({
+ applyToChildren(funct) {
+ Cards.find({ parentId: this._id }).forEach((card) => {
+ funct(card);
+ });
+ },
+
archive() {
+ this.applyToChildren((card) => { return card.archive(); });
return {$set: {archived: true}};
},
restore() {
+ this.applyToChildren((card) => { return card.restore(); });
return {$set: {archived: false}};
},
@@ -422,6 +545,11 @@ Cards.mutations({
unsetSpentTime() {
return {$unset: {spentTime: '', isOvertime: false}};
},
+
+ setParentId(parentId) {
+ return {$set: {parentId}};
+ },
+
});
@@ -513,6 +641,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..8c4c29d4 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(...Cards.find({ parentid: card._id }).fetch());
});
// [Old] for attachments we only export IDs and absolute url to original doc