summaryrefslogtreecommitdiffstats
path: root/collections/cards.js
diff options
context:
space:
mode:
authorMaxime Quandalle <maxime@quandalle.com>2015-05-12 19:20:58 +0200
committerMaxime Quandalle <maxime@quandalle.com>2015-05-12 19:33:50 +0200
commit2dbea30842ec63a68055245fe26633bb7913daf3 (patch)
treee9143893a3d3bf4ad34dd3a97d6f3466561c8756 /collections/cards.js
downloadwekan-2dbea30842ec63a68055245fe26633bb7913daf3.tar.gz
wekan-2dbea30842ec63a68055245fe26633bb7913daf3.tar.bz2
wekan-2dbea30842ec63a68055245fe26633bb7913daf3.zip
Renaissance
_,,ad8888888888bba,_ ,ad88888I888888888888888ba, ,88888888I88888888888888888888a, ,d888888888I8888888888888888888888b, d88888PP"""" ""YY88888888888888888888b, ,d88"'__,,--------,,,,.;ZZZY8888888888888, ,8IIl'" ;;l"ZZZIII8888888888, ,I88l;' ;lZZZZZ888III8888888, ,II88Zl;. ;llZZZZZ888888I888888, ,II888Zl;. .;;;;;lllZZZ888888I8888b ,II8888Z;; `;;;;;''llZZ8888888I8888, II88888Z;' .;lZZZ8888888I888b II88888Z; _,aaa, .,aaaaa,__.l;llZZZ88888888I888 II88888IZZZZZZZZZ, .ZZZZZZZZZZZZZZ;llZZ88888888I888, II88888IZZ<'(@@>Z| |ZZZ<'(@@>ZZZZ;;llZZ888888888I88I ,II88888; `""" ;| |ZZ; `""" ;;llZ8888888888I888 II888888l `;; .;llZZ8888888888I888, ,II888888Z; ;;; .;;llZZZ8888888888I888I III888888Zl; .., `;; ,;;lllZZZ88888888888I888 II88888888Z;;...;(_ _) ,;;;llZZZZ88888888888I888, II88888888Zl;;;;;' `--'Z;. .,;;;;llZZZZ88888888888I888b ]I888888888Z;;;;' ";llllll;..;;;lllZZZZ88888888888I8888, II888888888Zl.;;"Y88bd888P";;,..;lllZZZZZ88888888888I8888I II8888888888Zl;.; `"PPP";;;,..;lllZZZZZZZ88888888888I88888 II888888888888Zl;;. `;;;l;;;;lllZZZZZZZZW88888888888I88888 `II8888888888888Zl;. ,;;lllZZZZZZZZWMZ88888888888I88888 II8888888888888888ZbaalllZZZZZZZZZWWMZZZ8888888888I888888, `II88888888888888888b"WWZZZZZWWWMMZZZZZZI888888888I888888b `II88888888888888888;ZZMMMMMMZZZZZZZZllI888888888I8888888 `II8888888888888888 `;lZZZZZZZZZZZlllll888888888I8888888, II8888888888888888, `;lllZZZZllllll;;.Y88888888I8888888b, ,II8888888888888888b .;;lllllll;;;.;..88888888I88888888b, II888888888888888PZI;. .`;;;.;;;..; ...88888888I8888888888, II888888888888PZ;;';;. ;. .;. .;. .. Y8888888I88888888888b, ,II888888888PZ;;' `8888888I8888888888888b, II888888888' 888888I8888888888888888 ,II888888888 ,888888I8888888888888888 ,d88888888888 d888888I8888888888ZZZZZZ ,ad888888888888I 8888888I8888ZZZZZZZZZZZZ 888888888888888' 888888IZZZZZZZZZZZZZZZZZ 8888888888P'8P' Y888ZZZZZZZZZZZZZZZZZZZZ 888888888, " ,ZZZZZZZZZZZZZZZZZZZZZZZ 8888888888, ,ZZZZZZZZZZZZZZZZZZZZZZZZZZ 888888888888a, _ ,ZZZZZZZZZZZZZZZZZZZZ88888888 888888888888888ba,_d' ,ZZZZZZZZZZZZZZZZZ8888888888888 8888888888888888888888bbbaaa,,,______,ZZZZZZZZZZZZZZZ88888888888888888 88888888888888888888888888888888888ZZZZZZZZZZZZZZZ88888888888888888888 8888888888888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888 888888888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888888888 8888888888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888888 88888888888888888888888888888ZZZZZZZZZZZZZZ888888888888888888888888888 8888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888 Normand 8 88888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888 Veilleux 8 8888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888888888888
Diffstat (limited to 'collections/cards.js')
-rw-r--r--collections/cards.js287
1 files changed, 287 insertions, 0 deletions
diff --git a/collections/cards.js b/collections/cards.js
new file mode 100644
index 00000000..538b6af4
--- /dev/null
+++ b/collections/cards.js
@@ -0,0 +1,287 @@
+Cards = new Mongo.Collection('cards');
+CardComments = new Mongo.Collection('card_comments');
+
+// XXX To improve pub/sub performances a card document should include a
+// de-normalized number of comments so we don't have to publish the whole list
+// of comments just to display the number of them in the board view.
+Cards.attachSchema(new SimpleSchema({
+ title: {
+ type: String
+ },
+ archived: {
+ type: Boolean
+ },
+ listId: {
+ type: String
+ },
+ // The system could work without this `boardId` information (we could deduce
+ // the board identifier from the card), but it would make the system more
+ // difficult to manage and less efficient.
+ boardId: {
+ type: String
+ },
+ coverId: {
+ type: String,
+ optional: true
+ },
+ createdAt: {
+ type: Date,
+ denyUpdate: true
+ },
+ dateLastActivity: {
+ type: Date
+ },
+ description: {
+ type: String,
+ optional: true
+ },
+ labelIds: {
+ type: [String],
+ optional: true
+ },
+ members: {
+ type: [String],
+ optional: true
+ },
+ // XXX Should probably be called `authorId`. Is it even needed since we have
+ // the `members` field?
+ userId: {
+ type: String
+ },
+ sort: {
+ type: Number,
+ decimal: true
+ }
+}));
+
+CardComments.attachSchema(new SimpleSchema({
+ boardId: {
+ type: String
+ },
+ cardId: {
+ type: String
+ },
+ // XXX Rename in `content`? `text` is a bit vague...
+ text: {
+ type: String
+ },
+ // XXX We probably don't need this information here, since we already have it
+ // in the associated comment creation activity
+ createdAt: {
+ type: Date,
+ denyUpdate: false
+ },
+ // XXX Should probably be called `authorId`
+ userId: {
+ type: String
+ }
+}));
+
+if (Meteor.isServer) {
+ Cards.allow({
+ insert: function(userId, doc) {
+ return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
+ },
+ update: function(userId, doc) {
+ return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
+ },
+ remove: function(userId, doc) {
+ return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
+ },
+ fetch: ['boardId']
+ });
+
+ CardComments.allow({
+ insert: function(userId, doc) {
+ return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
+ },
+ update: function(userId, doc) {
+ return userId === doc.userId;
+ },
+ remove: function(userId, doc) {
+ return userId === doc.userId;
+ },
+ fetch: ['userId', 'boardId']
+ });
+}
+
+Cards.helpers({
+ list: function() {
+ return Lists.findOne(this.listId);
+ },
+ board: function() {
+ return Boards.findOne(this.boardId);
+ },
+ labels: function() {
+ var self = this;
+ var boardLabels = self.board().labels;
+ var cardLabels = _.filter(boardLabels, function(label) {
+ return _.contains(self.labelIds, label._id);
+ });
+ return cardLabels;
+ },
+ user: function() {
+ return Users.findOne(this.userId);
+ },
+ activities: function() {
+ return Activities.find({ type: 'card', cardId: this._id },
+ { sort: { createdAt: -1 }});
+ },
+ comments: function() {
+ return CardComments.find({ cardId: this._id }, { sort: { createdAt: -1 }});
+ },
+ attachments: function() {
+ return Attachments.find({ cardId: this._id }, { sort: { uploadedAt: -1 }});
+ },
+ cover: function() {
+ return Attachments.findOne(this.coverId);
+ },
+ absoluteUrl: function() {
+ var board = this.board();
+ return Router.path('Card', {
+ boardId: board._id,
+ slug: board.slug,
+ cardId: this._id
+ });
+ },
+ rootUrl: function() {
+ return Meteor.absoluteUrl(this.absoluteUrl().replace('/', ''));
+ }
+});
+
+CardComments.helpers({
+ user: function() {
+ return Users.findOne(this.userId);
+ }
+});
+
+CardComments.hookOptions.after.update = { fetchPrevious: false };
+Cards.before.insert(function(userId, doc) {
+ doc.createdAt = new Date();
+ doc.dateLastActivity = new Date();
+
+ // defaults
+ doc.archived = false;
+
+ // userId native set.
+ if (! doc.userId)
+ doc.userId = userId;
+});
+
+CardComments.before.insert(function(userId, doc) {
+ doc.createdAt = new Date();
+ doc.userId = userId;
+});
+
+if (Meteor.isServer) {
+ Cards.after.insert(function(userId, doc) {
+ Activities.insert({
+ type: 'card',
+ activityType: 'createCard',
+ boardId: doc.boardId,
+ listId: doc.listId,
+ cardId: doc._id,
+ userId: userId
+ });
+ });
+
+ // New activity for card (un)archivage
+ Cards.after.update(function(userId, doc, fieldNames) {
+ if (_.contains(fieldNames, 'archived')) {
+ if (doc.archived) {
+ Activities.insert({
+ type: 'card',
+ activityType: 'archivedCard',
+ boardId: doc.boardId,
+ listId: doc.listId,
+ cardId: doc._id,
+ userId: userId
+ });
+ } else {
+ Activities.insert({
+ type: 'card',
+ activityType: 'restoredCard',
+ boardId: doc.boardId,
+ listId: doc.listId,
+ cardId: doc._id,
+ userId: userId
+ });
+ }
+ }
+ });
+
+ // New activity for card moves
+ Cards.after.update(function(userId, doc, fieldNames) {
+ var oldListId = this.previous.listId;
+ if (_.contains(fieldNames, 'listId') && doc.listId !== oldListId) {
+ Activities.insert({
+ type: 'card',
+ activityType: 'moveCard',
+ listId: doc.listId,
+ oldListId: oldListId,
+ boardId: doc.boardId,
+ cardId: doc._id,
+ userId: userId
+ });
+ }
+ });
+
+ // Add a new activity if we add or remove a member to the card
+ Cards.before.update(function(userId, doc, fieldNames, modifier) {
+ if (! _.contains(fieldNames, 'members'))
+ return;
+ var memberId;
+ // Say hello to the new member
+ if (modifier.$addToSet && modifier.$addToSet.members) {
+ memberId = modifier.$addToSet.members;
+ if (! _.contains(doc.members, memberId)) {
+ Activities.insert({
+ type: 'card',
+ activityType: 'joinMember',
+ boardId: doc.boardId,
+ cardId: doc._id,
+ userId: userId,
+ memberId: memberId
+ });
+ }
+ }
+
+ // Say goodbye to the former member
+ if (modifier.$pull && modifier.$pull.members) {
+ memberId = modifier.$pull.members;
+ Activities.insert({
+ type: 'card',
+ activityType: 'unjoinMember',
+ boardId: doc.boardId,
+ cardId: doc._id,
+ userId: userId,
+ memberId: memberId
+ });
+ }
+ });
+
+ // Remove all activities associated with a card if we remove the card
+ Cards.after.remove(function(userId, doc) {
+ Activities.remove({
+ cardId: doc._id
+ });
+ });
+
+ CardComments.after.insert(function(userId, doc) {
+ Activities.insert({
+ type: 'comment',
+ activityType: 'addComment',
+ boardId: doc.boardId,
+ cardId: doc.cardId,
+ commentId: doc._id,
+ userId: userId
+ });
+ });
+
+ CardComments.after.remove(function(userId, doc) {
+ var activity = Activities.findOne({ commentId: doc._id });
+ if (activity) {
+ Activities.remove(activity._id);
+ }
+ });
+}