summaryrefslogtreecommitdiffstats
path: root/collections/cards.js
diff options
context:
space:
mode:
authorMaxime Quandalle <maxime@quandalle.com>2015-09-08 20:19:42 +0200
committerMaxime Quandalle <maxime@quandalle.com>2015-09-08 20:19:42 +0200
commit45b662a1ddb46a0f17fab7b2383c82aa1e1620ef (patch)
treecc7be215c7e7ebffd2597df70cf271b3dd435e1a /collections/cards.js
parentc04341f1ea5efe082bf7318cf9eb0e99b9b8374a (diff)
downloadwekan-45b662a1ddb46a0f17fab7b2383c82aa1e1620ef.tar.gz
wekan-45b662a1ddb46a0f17fab7b2383c82aa1e1620ef.tar.bz2
wekan-45b662a1ddb46a0f17fab7b2383c82aa1e1620ef.zip
Centralize all mutations at the model level
This commit uses a new package that I need to document. It tries to solve the long-standing debate in the Meteor community about allow/deny rules versus methods (RPC). This approach gives us both the centralized security rules of allow/deny and the white-list of allowed mutations similarly to Meteor methods. The idea to have static mutation descriptions is also inspired by Facebook's Relay/GraphQL. This will allow the development of a REST API using the high-level methods instead of the MongoDB queries to do the mapping between the HTTP requests and our collections.
Diffstat (limited to 'collections/cards.js')
-rw-r--r--collections/cards.js284
1 files changed, 0 insertions, 284 deletions
diff --git a/collections/cards.js b/collections/cards.js
deleted file mode 100644
index 97ba4e3c..00000000
--- a/collections/cards.js
+++ /dev/null
@@ -1,284 +0,0 @@
-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(userId, doc) {
- return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
- },
- update(userId, doc) {
- return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
- },
- remove(userId, doc) {
- return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
- },
- fetch: ['boardId'],
- });
-
- CardComments.allow({
- insert(userId, doc) {
- return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
- },
- update(userId, doc) {
- return userId === doc.userId;
- },
- remove(userId, doc) {
- return userId === doc.userId;
- },
- fetch: ['userId', 'boardId'],
- });
-}
-
-Cards.helpers({
- list() {
- return Lists.findOne(this.listId);
- },
- board() {
- return Boards.findOne(this.boardId);
- },
- labels() {
- const boardLabels = this.board().labels;
- const cardLabels = _.filter(boardLabels, (label) => {
- return _.contains(this.labelIds, label._id);
- });
- return cardLabels;
- },
- hasLabel(labelId) {
- return _.contains(this.labelIds, labelId);
- },
- user() {
- return Users.findOne(this.userId);
- },
- isAssigned(memberId) {
- return _.contains(this.members, memberId);
- },
- activities() {
- return Activities.find({ cardId: this._id }, { sort: { createdAt: -1 }});
- },
- comments() {
- return CardComments.find({ cardId: this._id }, { sort: { createdAt: -1 }});
- },
- attachments() {
- return Attachments.find({ cardId: this._id }, { sort: { uploadedAt: -1 }});
- },
- cover() {
- return Attachments.findOne(this.coverId);
- },
- absoluteUrl() {
- const board = this.board();
- return FlowRouter.path('card', {
- boardId: board._id,
- slug: board.slug,
- cardId: this._id,
- });
- },
- rootUrl() {
- return Meteor.absoluteUrl(this.absoluteUrl().replace('/', ''));
- },
-});
-
-CardComments.helpers({
- user() {
- return Users.findOne(this.userId);
- },
-});
-
-CardComments.hookOptions.after.update = { fetchPrevious: false };
-Cards.before.insert((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((userId, doc) => {
- doc.createdAt = new Date();
- doc.userId = userId;
-});
-
-if (Meteor.isServer) {
- Cards.after.insert((userId, doc) => {
- Activities.insert({
- userId,
- activityType: 'createCard',
- boardId: doc.boardId,
- listId: doc.listId,
- cardId: doc._id,
- });
- });
-
- // New activity for card (un)archivage
- Cards.after.update((userId, doc, fieldNames) => {
- if (_.contains(fieldNames, 'archived')) {
- if (doc.archived) {
- Activities.insert({
- userId,
- activityType: 'archivedCard',
- boardId: doc.boardId,
- listId: doc.listId,
- cardId: doc._id,
- });
- } else {
- Activities.insert({
- userId,
- activityType: 'restoredCard',
- boardId: doc.boardId,
- listId: doc.listId,
- cardId: doc._id,
- });
- }
- }
- });
-
- // New activity for card moves
- Cards.after.update(function(userId, doc, fieldNames) {
- const oldListId = this.previous.listId;
- if (_.contains(fieldNames, 'listId') && doc.listId !== oldListId) {
- Activities.insert({
- userId,
- oldListId,
- activityType: 'moveCard',
- listId: doc.listId,
- boardId: doc.boardId,
- cardId: doc._id,
- });
- }
- });
-
- // Add a new activity if we add or remove a member to the card
- Cards.before.update((userId, doc, fieldNames, modifier) => {
- if (!_.contains(fieldNames, 'members'))
- return;
- let memberId;
- // Say hello to the new member
- if (modifier.$addToSet && modifier.$addToSet.members) {
- memberId = modifier.$addToSet.members;
- if (!_.contains(doc.members, memberId)) {
- Activities.insert({
- userId,
- memberId,
- activityType: 'joinMember',
- boardId: doc.boardId,
- cardId: doc._id,
- });
- }
- }
-
- // Say goodbye to the former member
- if (modifier.$pull && modifier.$pull.members) {
- memberId = modifier.$pull.members;
- Activities.insert({
- userId,
- memberId,
- activityType: 'unjoinMember',
- boardId: doc.boardId,
- cardId: doc._id,
- });
- }
- });
-
- // Remove all activities associated with a card if we remove the card
- Cards.after.remove((userId, doc) => {
- Activities.remove({
- cardId: doc._id,
- });
- });
-
- CardComments.after.insert((userId, doc) => {
- Activities.insert({
- userId,
- activityType: 'addComment',
- boardId: doc.boardId,
- cardId: doc.cardId,
- commentId: doc._id,
- });
- });
-
- CardComments.after.remove((userId, doc) => {
- const activity = Activities.findOne({ commentId: doc._id });
- if (activity) {
- Activities.remove(activity._id);
- }
- });
-}