diff options
author | Maxime Quandalle <maxime@quandalle.com> | 2015-05-12 19:20:58 +0200 |
---|---|---|
committer | Maxime Quandalle <maxime@quandalle.com> | 2015-05-12 19:33:50 +0200 |
commit | 2dbea30842ec63a68055245fe26633bb7913daf3 (patch) | |
tree | e9143893a3d3bf4ad34dd3a97d6f3466561c8756 /collections/boards.js | |
download | wekan-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/boards.js')
-rw-r--r-- | collections/boards.js | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/collections/boards.js b/collections/boards.js new file mode 100644 index 00000000..e406b10c --- /dev/null +++ b/collections/boards.js @@ -0,0 +1,251 @@ +Boards = new Mongo.Collection('boards'); + +Boards.attachSchema(new SimpleSchema({ + title: { + type: String + }, + slug: { + type: String + }, + archived: { + type: Boolean + }, + createdAt: { + type: Date, + denyUpdate: true + }, + // XXX Inconsistent field naming + modifiedAt: { + type: Date, + denyInsert: true, + optional: true + }, + // De-normalized number of users that have starred this board + stars: { + type: Number + }, + // De-normalized label system + 'labels.$._id': { + // We don't specify that this field must be unique in the board because that + // will cause performance penalties and is not necessary since this field is + // always set on the server. + // XXX Actually if we create a new label, the `_id` is set on the client + // without being overwritten by the server, could it be a problem? + type: String + }, + 'labels.$.name': { + type: String, + optional: true + }, + 'labels.$.color': { + type: String, + allowedValues: [ + 'green', 'yellow', 'orange', 'red', 'purple', + 'blue', 'sky', 'lime', 'pink', 'black' + ] + }, + // XXX We might want to maintain more informations under the member sub- + // documents like de-normalized meta-data (the date the member joined the + // board, the number of contributions, etc.). + 'members.$.userId': { + type: String + }, + 'members.$.isAdmin': { + type: Boolean + }, + 'members.$.isActive': { + type: Boolean + }, + permission: { + type: String, + allowedValues: ['public', 'private'] + }, + color: { + type: String, + allowedValues: ['nephritis', 'pomegranate', 'belize', + 'wisteria', 'midnight', 'pumpkin'] + } +})); + +if (Meteor.isServer) { + Boards.allow({ + insert: Meteor.userId, + update: allowIsBoardAdmin, + remove: allowIsBoardAdmin, + fetch: ['members'] + }); + + // The number of users that have starred this board is managed by trusted code + // and the user is not allowed to update it + Boards.deny({ + update: function(userId, board, fieldNames) { + return _.contains(fieldNames, 'stars'); + }, + fetch: [] + }); + + // We can't remove a member if it is the last administrator + Boards.deny({ + update: function(userId, doc, fieldNames, modifier) { + if (! _.contains(fieldNames, 'members')) + return false; + + // We only care in case of a $pull operation, ie remove a member + if (! _.isObject(modifier.$pull && modifier.$pull.members)) + return false; + + // If there is more than one admin, it's ok to remove anyone + var nbAdmins = _.filter(doc.members, function(member) { + return member.isAdmin; + }).length; + if (nbAdmins > 1) + return false; + + // If all the previous conditions where verified, we can't remove + // a user if it's an admin + var removedMemberId = modifier.$pull.members.userId; + return !! _.findWhere(doc.members, { + userId: removedMemberId, + isAdmin: true + }); + }, + fetch: ['members'] + }); +} + +Boards.helpers({ + isPublic: function() { + return this.permission === 'public'; + }, + lists: function() { + return Lists.find({ boardId: this._id, archived: false }, + { sort: { sort: 1 }}); + }, + activities: function() { + return Activities.find({ boardId: this._id }, { sort: { createdAt: -1 }}); + }, + absoluteUrl: function() { + return Router.path('Board', { boardId: this._id, slug: this.slug }); + }, + colorClass: function() { + return 'board-color-' + this.color; + } +}); + +Boards.before.insert(function(userId, doc) { + // XXX We need to improve slug management. Only the id should be necessary + // to identify a board in the code. + // XXX If the board title is updated, the slug should also be updated. + // In some cases (Chinese and Japanese for instance) the `getSlug` function + // return an empty string. This is causes bugs in our application so we set + // a default slug in this case. + doc.slug = getSlug(doc.title) || 'board'; + doc.createdAt = new Date(); + doc.archived = false; + doc.members = [{ + userId: userId, + isAdmin: true, + isActive: true + }]; + doc.stars = 0; + doc.color = Boards.simpleSchema()._schema.color.allowedValues[0]; + + // Handle labels + var colors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues; + var defaultLabelsColors = _.clone(colors).splice(0, 6); + doc.labels = []; + _.each(defaultLabelsColors, function(val) { + doc.labels.push({ + _id: Random.id(6), + name: '', + color: val + }); + }); + + // We randomly chose one of the default background colors for the board + if (Meteor.isClient) { + doc.background = { + type: 'color', + color: Random.choice(Boards.simpleSchema()._schema.color.allowedValues) + }; + } +}); + +Boards.before.update(function(userId, doc, fieldNames, modifier) { + modifier.$set = modifier.$set || {}; + modifier.$set.modifiedAt = new Date(); +}); + +if (Meteor.isServer) { + // Let MongoDB ensure that a member is not included twice in the same board + Meteor.startup(function() { + Boards._collection._ensureIndex({ + _id: 1, + 'members.userId': 1 + }, { unique: true }); + }); + + // Genesis: the first activity of the newly created board + Boards.after.insert(function(userId, doc) { + Activities.insert({ + type: 'board', + activityTypeId: doc._id, + activityType: 'createBoard', + boardId: doc._id, + userId: userId + }); + }); + + // If the user remove one label from a board, we cant to remove reference of + // this label in any card of this board. + Boards.after.update(function(userId, doc, fieldNames, modifier) { + if (! _.contains(fieldNames, 'labels') || + ! modifier.$pull || + ! modifier.$pull.labels || + ! modifier.$pull.labels._id) + return; + + var removedLabelId = modifier.$pull.labels._id; + Cards.update( + { boardId: doc._id }, + { + $pull: { + labels: removedLabelId + } + }, + { multi: true } + ); + }); + + // Add a new activity if we add or remove a member to the board + Boards.after.update(function(userId, doc, fieldNames, modifier) { + if (! _.contains(fieldNames, 'members')) + return; + + var memberId; + + // Say hello to the new member + if (modifier.$push && modifier.$push.members) { + memberId = modifier.$push.members.userId; + Activities.insert({ + type: 'member', + activityType: 'addBoardMember', + boardId: doc._id, + userId: userId, + memberId: memberId + }); + } + + // Say goodbye to the former member + if (modifier.$pull && modifier.$pull.members) { + memberId = modifier.$pull.members.userId; + Activities.insert({ + type: 'member', + activityType: 'removeBoardMember', + boardId: doc._id, + userId: userId, + memberId: memberId + }); + } + }); +} |