summaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
Diffstat (limited to 'models')
-rw-r--r--models/activities.js7
-rw-r--r--models/boards.js2
-rw-r--r--models/cardComments.js6
-rw-r--r--models/cards.js54
-rw-r--r--models/checklists.js164
-rw-r--r--models/lists.js11
-rw-r--r--models/users.js78
7 files changed, 304 insertions, 18 deletions
diff --git a/models/activities.js b/models/activities.js
index aa2ea3ec..7d262ec6 100644
--- a/models/activities.js
+++ b/models/activities.js
@@ -35,6 +35,9 @@ Activities.helpers({
attachment() {
return Attachments.findOne(this.attachmentId);
},
+ checklist() {
+ return Checklists.findOne(this.checklistId);
+ },
});
Activities.before.insert((userId, doc) => {
@@ -102,6 +105,10 @@ if (Meteor.isServer) {
const attachment = activity.attachment();
params.attachment = attachment._id;
}
+ if (activity.checklistId) {
+ const checklist = activity.checklist();
+ params.checklist = checklist.title;
+ }
if (board) {
const watchingUsers = _.pluck(_.where(board.watchers, {level: 'watching'}), 'userId');
const trackingUsers = _.pluck(_.where(board.watchers, {level: 'tracking'}), 'userId');
diff --git a/models/boards.js b/models/boards.js
index 3051ef1e..14943d61 100644
--- a/models/boards.js
+++ b/models/boards.js
@@ -249,7 +249,7 @@ Boards.mutations({
return { $set: { title }};
},
- setDesciption(description) {
+ setDescription(description) {
return { $set: {description} };
},
diff --git a/models/cardComments.js b/models/cardComments.js
index ce6edf3c..070c148e 100644
--- a/models/cardComments.js
+++ b/models/cardComments.js
@@ -57,6 +57,12 @@ CardComments.helpers({
CardComments.hookOptions.after.update = { fetchPrevious: false };
if (Meteor.isServer) {
+ // Comments are often fetched within a card, so we create an index to make these
+ // queries more efficient.
+ Meteor.startup(() => {
+ CardComments._collection._ensureIndex({ cardId: 1, createdAt: -1 });
+ });
+
CardComments.after.insert((userId, doc) => {
Activities.insert({
userId,
diff --git a/models/cards.js b/models/cards.js
index 84fbb6c2..f6bd0b06 100644
--- a/models/cards.js
+++ b/models/cards.js
@@ -56,6 +56,14 @@ Cards.attachSchema(new SimpleSchema({
type: [String],
optional: true,
},
+ startAt: {
+ type: Date,
+ optional: true,
+ },
+ dueAt: {
+ type: Date,
+ optional: true,
+ },
// XXX Should probably be called `authorId`. Is it even needed since we have
// the `members` field?
userId: {
@@ -133,6 +141,36 @@ Cards.helpers({
return cover && cover.url() && cover;
},
+ checklists() {
+ return Checklists.find({ cardId: this._id }, { sort: { createdAt: 1 }});
+ },
+
+ checklistItemCount() {
+ const checklists = this.checklists().fetch();
+ return checklists.map((checklist) => {
+ return checklist.itemCount();
+ }).reduce((prev, next) => {
+ return prev + next;
+ }, 0);
+ },
+
+ checklistFinishedCount() {
+ const checklists = this.checklists().fetch();
+ return checklists.map((checklist) => {
+ return checklist.finishedCount();
+ }).reduce((prev, next) => {
+ return prev + next;
+ }, 0);
+ },
+
+ checklistFinished() {
+ return this.hasChecklist() && this.checklistItemCount() === this.checklistFinishedCount();
+ },
+
+ hasChecklist() {
+ return this.checklistItemCount() !== 0;
+ },
+
absoluteUrl() {
const board = this.board();
return FlowRouter.url('card', {
@@ -207,6 +245,22 @@ Cards.mutations({
unsetCover() {
return { $unset: { coverId: '' }};
},
+
+ setStart(startAt) {
+ return { $set: { startAt }};
+ },
+
+ unsetStart() {
+ return { $unset: { startAt: '' }};
+ },
+
+ setDue(dueAt) {
+ return { $set: { dueAt }};
+ },
+
+ unsetDue() {
+ return { $unset: { dueAt: '' }};
+ },
});
if (Meteor.isServer) {
diff --git a/models/checklists.js b/models/checklists.js
new file mode 100644
index 00000000..35be4dcc
--- /dev/null
+++ b/models/checklists.js
@@ -0,0 +1,164 @@
+Checklists = new Mongo.Collection('checklists');
+
+Checklists.attachSchema(new SimpleSchema({
+ cardId: {
+ type: String,
+ },
+ title: {
+ type: String,
+ },
+ items: {
+ type: [Object],
+ defaultValue: [],
+ },
+ 'items.$._id': {
+ type: String,
+ },
+ 'items.$.title': {
+ type: String,
+ },
+ 'items.$.isFinished': {
+ type: Boolean,
+ defaultValue: false,
+ },
+ finishedAt: {
+ type: Date,
+ optional: true,
+ },
+ createdAt: {
+ type: Date,
+ denyUpdate: false,
+ },
+}));
+
+Checklists.helpers({
+ itemCount () {
+ return this.items.length;
+ },
+ finishedCount () {
+ return this.items.filter((item) => {
+ return item.isFinished;
+ }).length;
+ },
+ isFinished () {
+ return 0 !== this.itemCount() && this.itemCount() === this.finishedCount();
+ },
+ getItem (_id) {
+ return _.findWhere(this.items, { _id });
+ },
+ itemIndex(itemId) {
+ return _.pluck(this.items, '_id').indexOf(itemId);
+ },
+});
+
+Checklists.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'],
+});
+
+Checklists.before.insert((userId, doc) => {
+ doc.createdAt = new Date();
+ if (!doc.userId) {
+ doc.userId = userId;
+ }
+});
+
+Checklists.mutations({
+ //for checklist itself
+ setTitle(title){
+ return { $set: { title }};
+ },
+ //for items in checklist
+ addItem(title) {
+ const itemCount = this.itemCount();
+ const _id = `${this._id}${itemCount}`;
+ return { $addToSet: {items: {_id, title, isFinished: false}} };
+ },
+ removeItem(itemId) {
+ return {$pull: {items: {_id : itemId}}};
+ },
+ editItem(itemId, title) {
+ if (this.getItem(itemId)) {
+ const itemIndex = this.itemIndex(itemId);
+ return {
+ $set: {
+ [`items.${itemIndex}.title`]: title,
+ },
+ };
+ }
+ return {};
+ },
+ finishItem(itemId) {
+ if (this.getItem(itemId)) {
+ const itemIndex = this.itemIndex(itemId);
+ return {
+ $set: {
+ [`items.${itemIndex}.isFinished`]: true,
+ },
+ };
+ }
+ return {};
+ },
+ resumeItem(itemId) {
+ if (this.getItem(itemId)) {
+ const itemIndex = this.itemIndex(itemId);
+ return {
+ $set: {
+ [`items.${itemIndex}.isFinished`]: false,
+ },
+ };
+ }
+ return {};
+ },
+ toggleItem(itemId) {
+ const item = this.getItem(itemId);
+ if (item) {
+ const itemIndex = this.itemIndex(itemId);
+ return {
+ $set: {
+ [`items.${itemIndex}.isFinished`]: !item.isFinished,
+ },
+ };
+ }
+ return {};
+ },
+});
+
+if (Meteor.isServer) {
+ Checklists.after.insert((userId, doc) => {
+ Activities.insert({
+ userId,
+ activityType: 'addChecklist',
+ cardId: doc.cardId,
+ boardId: Cards.findOne(doc.cardId).boardId,
+ checklistId: doc._id,
+ });
+ });
+
+ //TODO: so there will be no activity for adding item into checklist, maybe will be implemented in the future.
+ // Checklists.after.update((userId, doc) => {
+ // console.log('update:', doc)
+ // Activities.insert({
+ // userId,
+ // activityType: 'addChecklist',
+ // boardId: doc.boardId,
+ // cardId: doc.cardId,
+ // checklistId: doc._id,
+ // });
+ // });
+
+ Checklists.before.remove((userId, doc) => {
+ const activity = Activities.findOne({ checklistId: doc._id });
+ if (activity) {
+ Activities.remove(activity._id);
+ }
+ });
+}
diff --git a/models/lists.js b/models/lists.js
index 9ae2e4f7..682fb096 100644
--- a/models/lists.js
+++ b/models/lists.js
@@ -105,6 +105,17 @@ if (Meteor.isServer) {
});
});
+ Lists.before.remove((userId, doc) => {
+ Activities.insert({
+ userId,
+ type: 'list',
+ activityType: 'removeList',
+ boardId: doc.boardId,
+ listId: doc._id,
+ title: doc.title,
+ });
+ });
+
Lists.after.update((userId, doc) => {
if (doc.archived) {
Activities.insert({
diff --git a/models/users.js b/models/users.js
index 790ee0a1..58513231 100644
--- a/models/users.js
+++ b/models/users.js
@@ -1,3 +1,7 @@
+// Sandstorm context is detected using the METEOR_SETTINGS environment variable
+// in the package definition.
+const isSandstorm = Meteor.settings && Meteor.settings.public &&
+ Meteor.settings.public.sandstorm;
Users = Meteor.users;
Users.attachSchema(new SimpleSchema({
@@ -55,6 +59,10 @@ Users.attachSchema(new SimpleSchema({
type: String,
optional: true,
},
+ 'profile.hiddenSystemMessages': {
+ type: Boolean,
+ optional: true,
+ },
'profile.initials': {
type: String,
optional: true,
@@ -71,6 +79,10 @@ Users.attachSchema(new SimpleSchema({
type: [String],
optional: true,
},
+ 'profile.showCardsCountAt': {
+ type: Number,
+ optional: true,
+ },
'profile.starredBoards': {
type: [String],
optional: true,
@@ -147,6 +159,11 @@ Users.helpers({
return _.contains(notifications, activityId);
},
+ hasHiddenSystemMessages() {
+ const profile = this.profile || {};
+ return profile.hiddenSystemMessages || false;
+ },
+
getEmailBuffer() {
const {emailBuffer = []} = this.profile;
return emailBuffer;
@@ -167,6 +184,11 @@ Users.helpers({
}
},
+ getLimitToShowCardsCount() {
+ const profile = this.profile || {};
+ return profile.showCardsCountAt;
+ },
+
getName() {
const profile = this.profile || {};
return profile.fullname || this.username;
@@ -227,6 +249,14 @@ Users.mutations({
this.addTag(tag);
},
+ toggleSystem(value = false) {
+ return {
+ $set: {
+ 'profile.hiddenSystemMessages': !value,
+ },
+ };
+ },
+
addNotification(activityId) {
return {
$addToSet: {
@@ -262,6 +292,10 @@ Users.mutations({
setAvatarUrl(avatarUrl) {
return { $set: { 'profile.avatarUrl': avatarUrl }};
},
+
+ setShowCardsCountAt(limit) {
+ return { $set: { 'profile.showCardsCountAt': limit } };
+ },
});
Meteor.methods({
@@ -274,6 +308,14 @@ Meteor.methods({
Users.update(this.userId, {$set: { username }});
}
},
+ toggleSystemMessages() {
+ const user = Meteor.user();
+ user.toggleSystem(user.hasHiddenSystemMessages());
+ },
+ changeLimitToShowCardsCount(limit) {
+ check(limit, Number);
+ Meteor.user().setShowCardsCountAt(limit);
+ },
});
if (Meteor.isServer) {
@@ -394,24 +436,26 @@ if (Meteor.isServer) {
return fakeUserId.get() || getUserId();
};
- Users.after.insert((userId, doc) => {
- const fakeUser = {
- extendAutoValueContext: {
- userId: doc._id,
- },
- };
-
- fakeUserId.withValue(doc._id, () => {
- // Insert the Welcome Board
- Boards.insert({
- title: TAPi18n.__('welcome-board'),
- permission: 'private',
- }, fakeUser, (err, boardId) => {
-
- ['welcome-list1', 'welcome-list2'].forEach((title) => {
- Lists.insert({ title: TAPi18n.__(title), boardId }, fakeUser);
+ if (!isSandstorm) {
+ Users.after.insert((userId, doc) => {
+ const fakeUser = {
+ extendAutoValueContext: {
+ userId: doc._id,
+ },
+ };
+
+ fakeUserId.withValue(doc._id, () => {
+ // Insert the Welcome Board
+ Boards.insert({
+ title: TAPi18n.__('welcome-board'),
+ permission: 'private',
+ }, fakeUser, (err, boardId) => {
+
+ ['welcome-list1', 'welcome-list2'].forEach((title) => {
+ Lists.insert({ title: TAPi18n.__(title), boardId }, fakeUser);
+ });
});
});
});
- });
+ }
}