From ade3c02122d262c72bd7c4fd1cbcab8e136184ba Mon Sep 17 00:00:00 2001 From: Pouyan Savoli Date: Fri, 25 Aug 2017 02:59:20 +0200 Subject: Create custom fields creation UI added to Board Menu, Model in progress --- models/activities.js | 8 ++++ models/customFields.js | 116 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 models/customFields.js (limited to 'models') diff --git a/models/activities.js b/models/activities.js index 4ddcfa72..237283f8 100644 --- a/models/activities.js +++ b/models/activities.js @@ -41,6 +41,9 @@ Activities.helpers({ checklistItem() { return Checklists.findOne(this.checklistId).getItem(this.checklistItemId); }, + customField() { + return CustomFields.findOne(this.customFieldId); + }, }); Activities.before.insert((userId, doc) => { @@ -57,6 +60,7 @@ if (Meteor.isServer) { Activities._collection._ensureIndex({ boardId: 1, createdAt: -1 }); Activities._collection._ensureIndex({ commentId: 1 }, { partialFilterExpression: { commentId: { $exists: true } } }); Activities._collection._ensureIndex({ attachmentId: 1 }, { partialFilterExpression: { attachmentId: { $exists: true } } }); + Activities._collection._ensureIndex({ customFieldId: 1 }, { partialFilterExpression: { customFieldId: { $exists: true } } }); }); Activities.after.insert((userId, doc) => { @@ -123,6 +127,10 @@ if (Meteor.isServer) { const checklistItem = activity.checklistItem(); params.checklistItem = checklistItem.title; } + if (activity.customFieldId) { + const customField = activity.customField(); + params.customField = customField.name; + } if (board) { const watchingUsers = _.pluck(_.where(board.watchers, {level: 'watching'}), 'userId'); const trackingUsers = _.pluck(_.where(board.watchers, {level: 'tracking'}), 'userId'); diff --git a/models/customFields.js b/models/customFields.js new file mode 100644 index 00000000..75ee55e8 --- /dev/null +++ b/models/customFields.js @@ -0,0 +1,116 @@ +CustomFields = new Mongo.Collection('customFields'); + +CustomFields.attachSchema(new SimpleSchema({ + boardId: { + type: String, + }, + name: { + type: String, + }, + type: { + type: String, + }, + showOnCard: { + type: Boolean, + } +})); + +CustomFields.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'], +}); + +// not sure if we need this? +//CustomFields.hookOptions.after.update = { fetchPrevious: false }; + +function customFieldCreation(userId, doc){ + Activities.insert({ + userId, + activityType: 'createCustomField', + boardId: doc.boardId, + customFieldId: doc._id, + }); +} + +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 }); + }); + + CustomFields.after.insert((userId, doc) => { + customFieldCreation(userId, doc); + }); + + CustomFields.after.remove((userId, doc) => { + const activity = Activities.findOne({ customFieldId: doc._id }); + if (activity) { + Activities.remove(activity._id); + } + }); +} + +//CUSTOM FIELD REST API +if (Meteor.isServer) { + JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', function (req, res, next) { + Authentication.checkUserId( req.userId); + const paramBoardId = req.params.boardId; + JsonRoutes.sendResult(res, { + code: 200, + data: CustomFields.find({ boardId: paramBoardId }) + }); + }); + + JsonRoutes.add('GET', '/api/boards/:boardId/comments/:customFieldId', function (req, res, next) { + Authentication.checkUserId( req.userId); + const paramBoardId = req.params.boardId; + const paramCustomFieldId = req.params.customFieldId; + JsonRoutes.sendResult(res, { + code: 200, + data: CustomFields.findOne({ _id: paramCustomFieldId, boardId: paramBoardId }), + }); + }); + + JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function (req, res, next) { + Authentication.checkUserId( req.userId); + const paramBoardId = req.params.boardId; + const id = CustomFields.direct.insert({ + name: req.body.name, + type: req.body.type, + showOnCard: req.body.showOnCard, + boardId: paramBoardId, + }); + + const customField = CustomFields.findOne({_id: id, cardId:paramCardId, boardId: paramBoardId }); + customFieldCreation(req.body.authorId, customField); + + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: id, + }, + }); + }); + + JsonRoutes.add('DELETE', '/api/boards/:boardId/custom-fields/:customFieldId', function (req, res, next) { + Authentication.checkUserId( req.userId); + const paramBoardId = req.params.boardId; + const id = req.params.customFieldId; + CustomFields.remove({ _id: id, boardId: paramBoardId }); + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: id, + }, + }); + }); +} -- cgit v1.2.3-1-g7c22 From afd87e3caa1fedbe8fe5dbaefa485fee1ed85c71 Mon Sep 17 00:00:00 2001 From: Pouyan Savoli Date: Sun, 27 Aug 2017 22:31:24 +0200 Subject: many custom fields model and UI enhancements --- models/boards.js | 4 ++++ models/customFields.js | 21 +++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'models') diff --git a/models/boards.js b/models/boards.js index 8a7844e2..98c0e46d 100644 --- a/models/boards.js +++ b/models/boards.js @@ -235,6 +235,10 @@ Boards.helpers({ return `board-color-${this.color}`; }, + customFields() { + return CustomFields.find({ boardId: this._id }, { sort: { name: 1 } }); + }, + // XXX currently mutations return no value so we have an issue when using addLabel in import // XXX waiting on https://github.com/mquandalle/meteor-collection-mutations/issues/1 to remove... pushLabel(name, color) { diff --git a/models/customFields.js b/models/customFields.js index 75ee55e8..5e76db35 100644 --- a/models/customFields.js +++ b/models/customFields.js @@ -25,7 +25,7 @@ CustomFields.allow({ remove(userId, doc) { return allowIsBoardMember(userId, Boards.findOne(doc.boardId)); }, - fetch: ['boardId'], + fetch: ['userId', 'boardId'], }); // not sure if we need this? @@ -41,21 +41,18 @@ function customFieldCreation(userId, doc){ } 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 }); - }); + /*Meteor.startup(() => { + CustomFields._collection._ensureIndex({ boardId: 1}); + });*/ CustomFields.after.insert((userId, doc) => { customFieldCreation(userId, doc); }); CustomFields.after.remove((userId, doc) => { - const activity = Activities.findOne({ customFieldId: doc._id }); - if (activity) { - Activities.remove(activity._id); - } + Activities.remove({ + customFieldId: doc._id, + }); }); } @@ -70,7 +67,7 @@ if (Meteor.isServer) { }); }); - JsonRoutes.add('GET', '/api/boards/:boardId/comments/:customFieldId', function (req, res, next) { + JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields/:customFieldId', function (req, res, next) { Authentication.checkUserId( req.userId); const paramBoardId = req.params.boardId; const paramCustomFieldId = req.params.customFieldId; @@ -90,7 +87,7 @@ if (Meteor.isServer) { boardId: paramBoardId, }); - const customField = CustomFields.findOne({_id: id, cardId:paramCardId, boardId: paramBoardId }); + const customField = CustomFields.findOne({_id: id, boardId: paramBoardId }); customFieldCreation(req.body.authorId, customField); JsonRoutes.sendResult(res, { -- cgit v1.2.3-1-g7c22 From d87191f17ee1cd49def9ca5a4d3d1568b041e6a2 Mon Sep 17 00:00:00 2001 From: Pouyan Savoli Date: Wed, 30 Aug 2017 02:54:54 +0200 Subject: card model and card ui preparation for custom fields --- models/cards.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index 0a440697..6896970c 100644 --- a/models/cards.js +++ b/models/cards.js @@ -38,6 +38,21 @@ Cards.attachSchema(new SimpleSchema({ } }, }, + customFields: { + type: [Object], + optional: true, + }, + 'customFields.$': { + type: new SimpleSchema({ + _id: { + type: String, + }, + value: { + type: Match.OneOf(String,Number,Boolean,Date), + optional: true, + }, + }) + }, dateLastActivity: { type: Date, autoValue() { @@ -238,6 +253,24 @@ Cards.mutations({ } }, + assignCustomField(customFieldId) { + console.log("assignCustomField", customFieldId); + return {$push: {customFields: {_id: customFieldId, value: null}}}; + }, + + unassignCustomField(customFieldId) { + console.log("unassignCustomField", customFieldId); + return {$pull: {customFields: {_id: customFieldId}}}; + }, + + toggleCustomField(customFieldId) { + if (this.customFields && this.customFields[customFieldId]) { + return this.unassignCustomField(customFieldId); + } else { + return this.assignCustomField(customFieldId); + } + }, + setCover(coverId) { return {$set: {coverId}}; }, -- cgit v1.2.3-1-g7c22 From 733b14dcd8f4b94047ffd444e7ab7ded49c245c0 Mon Sep 17 00:00:00 2001 From: Pouyan Savoli Date: Wed, 30 Aug 2017 03:23:57 +0200 Subject: card model and card ui preparation for custom fields #2 --- models/cards.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index 6896970c..670a47cb 100644 --- a/models/cards.js +++ b/models/cards.js @@ -186,6 +186,10 @@ Cards.helpers({ return this.checklistItemCount() !== 0; }, + customFieldIndex(customFieldId) { + return _.pluck(this.customFields, '_id').indexOf(customFieldId); + }, + absoluteUrl() { const board = this.board(); return FlowRouter.url('card', { @@ -255,7 +259,7 @@ Cards.mutations({ assignCustomField(customFieldId) { console.log("assignCustomField", customFieldId); - return {$push: {customFields: {_id: customFieldId, value: null}}}; + return {$addToSet: {customFields: {_id: customFieldId, value: null}}}; }, unassignCustomField(customFieldId) { @@ -264,7 +268,7 @@ Cards.mutations({ }, toggleCustomField(customFieldId) { - if (this.customFields && this.customFields[customFieldId]) { + if (this.customFields && this.customFieldIndex(customFieldId) > -1) { return this.unassignCustomField(customFieldId); } else { return this.assignCustomField(customFieldId); -- cgit v1.2.3-1-g7c22 From 6ff89b43b61ace7ea26d50f091ea6b714fa79c84 Mon Sep 17 00:00:00 2001 From: Pouyan Savoli Date: Tue, 5 Sep 2017 02:34:18 +0200 Subject: show custom fields on cards but still with dummy value --- models/cards.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index 670a47cb..fe86e642 100644 --- a/models/cards.js +++ b/models/cards.js @@ -190,6 +190,27 @@ Cards.helpers({ return _.pluck(this.customFields, '_id').indexOf(customFieldId); }, + // customFields with definitions + customFieldsWD() { + + // get all definitions + const definitions = CustomFields.find({ + boardId: this.boardId, + }).fetch(); + + // match right definition to each field + return this.customFields.map((customField) => { + return { + _id: customField._id, + value: customField.value, + definition: definitions.find((definition) => { + return definition._id == customField._id; + }) + } + }); + + }, + absoluteUrl() { const board = this.board(); return FlowRouter.url('card', { -- cgit v1.2.3-1-g7c22 From caad952bc1b29bb925c1347a14daa5d1ec8ada81 Mon Sep 17 00:00:00 2001 From: Pouyan Savoli Date: Thu, 14 Sep 2017 00:50:05 +0200 Subject: text custom fields are now editable using inlinedForm --- models/cards.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index fe86e642..17abf430 100644 --- a/models/cards.js +++ b/models/cards.js @@ -279,12 +279,10 @@ Cards.mutations({ }, assignCustomField(customFieldId) { - console.log("assignCustomField", customFieldId); return {$addToSet: {customFields: {_id: customFieldId, value: null}}}; }, unassignCustomField(customFieldId) { - console.log("unassignCustomField", customFieldId); return {$pull: {customFields: {_id: customFieldId}}}; }, @@ -296,6 +294,16 @@ Cards.mutations({ } }, + setCustomField(customFieldId, value) { + // todo + const index = this.customFieldIndex(customFieldId); + if (index > -1) { + var update = {$set: {}}; + update.$set["customFields." + index + ".value"] = value; + return update; + } + }, + setCover(coverId) { return {$set: {coverId}}; }, -- cgit v1.2.3-1-g7c22 From 3753337d60e17e5e72ec071aa4a1b28e36297d15 Mon Sep 17 00:00:00 2001 From: Pouyan Savoli Date: Mon, 18 Sep 2017 00:46:17 +0200 Subject: dropdown items --- models/customFields.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'models') diff --git a/models/customFields.js b/models/customFields.js index 5e76db35..8b0abef4 100644 --- a/models/customFields.js +++ b/models/customFields.js @@ -9,6 +9,24 @@ CustomFields.attachSchema(new SimpleSchema({ }, type: { type: String, + allowedValues: ['text', 'number', 'checkbox', 'date', 'dropdown'] + }, + settings: { + type: Object, + }, + 'settings.dropdownItems': { + type: [Object], + optional: true + }, + 'settings.dropdownItems.$': { + type: new SimpleSchema({ + _id: { + type: String, + }, + name: { + type: String, + }, + }) }, showOnCard: { type: Boolean, @@ -83,6 +101,7 @@ if (Meteor.isServer) { const id = CustomFields.direct.insert({ name: req.body.name, type: req.body.type, + settings: req.body.settings, showOnCard: req.body.showOnCard, boardId: paramBoardId, }); -- cgit v1.2.3-1-g7c22 From 9184bf394629607bbaed8120004d90a21510315b Mon Sep 17 00:00:00 2001 From: IgnatzHome Date: Thu, 17 May 2018 21:06:52 +0200 Subject: removing checkbox type --- models/customFields.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'models') diff --git a/models/customFields.js b/models/customFields.js index 8b0abef4..57875b6e 100644 --- a/models/customFields.js +++ b/models/customFields.js @@ -9,7 +9,7 @@ CustomFields.attachSchema(new SimpleSchema({ }, type: { type: String, - allowedValues: ['text', 'number', 'checkbox', 'date', 'dropdown'] + allowedValues: ['text', 'number', 'date', 'dropdown'] }, settings: { type: Object, -- cgit v1.2.3-1-g7c22 From d6cfac0122dc5e6819c82f73c728488e0600cb70 Mon Sep 17 00:00:00 2001 From: Ignatz Date: Fri, 18 May 2018 10:24:51 +0200 Subject: linter corrections --- models/cards.js | 17 ++++++++++------- models/customFields.js | 18 +++++++++--------- 2 files changed, 19 insertions(+), 16 deletions(-) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index 8b917ee3..d3a741bb 100644 --- a/models/cards.js +++ b/models/cards.js @@ -51,10 +51,10 @@ Cards.attachSchema(new SimpleSchema({ type: String, }, value: { - type: Match.OneOf(String,Number,Boolean,Date), + type: Match.OneOf(String, Number, Boolean, Date), optional: true, }, - }) + }), }, dateLastActivity: { type: Date, @@ -225,9 +225,9 @@ Cards.helpers({ _id: customField._id, value: customField.value, definition: definitions.find((definition) => { - return definition._id == customField._id; - }) - } + return definition._id === customField._id; + }), + }; }); }, @@ -331,10 +331,13 @@ Cards.mutations({ // todo const index = this.customFieldIndex(customFieldId); if (index > -1) { - var update = {$set: {}}; - update.$set["customFields." + index + ".value"] = value; + const update = {$set: {}}; + update.$set['customFields.${index}.value'] = value; return update; } + // TODO + // Ignatz 18.05.2018: Return null to silence ESLint. No Idea if that is correct + return null; }, setCover(coverId) { diff --git a/models/customFields.js b/models/customFields.js index 57875b6e..6c5fe7c4 100644 --- a/models/customFields.js +++ b/models/customFields.js @@ -9,14 +9,14 @@ CustomFields.attachSchema(new SimpleSchema({ }, type: { type: String, - allowedValues: ['text', 'number', 'date', 'dropdown'] + allowedValues: ['text', 'number', 'date', 'dropdown'], }, settings: { type: Object, }, 'settings.dropdownItems': { type: [Object], - optional: true + optional: true, }, 'settings.dropdownItems.$': { type: new SimpleSchema({ @@ -26,11 +26,11 @@ CustomFields.attachSchema(new SimpleSchema({ name: { type: String, }, - }) + }), }, showOnCard: { type: Boolean, - } + }, })); CustomFields.allow({ @@ -76,16 +76,16 @@ if (Meteor.isServer) { //CUSTOM FIELD REST API if (Meteor.isServer) { - JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', function (req, res, next) { + JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', function (req, res) { Authentication.checkUserId( req.userId); const paramBoardId = req.params.boardId; JsonRoutes.sendResult(res, { code: 200, - data: CustomFields.find({ boardId: paramBoardId }) + data: CustomFields.find({ boardId: paramBoardId }), }); }); - JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields/:customFieldId', function (req, res, next) { + JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields/:customFieldId', function (req, res) { Authentication.checkUserId( req.userId); const paramBoardId = req.params.boardId; const paramCustomFieldId = req.params.customFieldId; @@ -95,7 +95,7 @@ if (Meteor.isServer) { }); }); - JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function (req, res, next) { + JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function (req, res) { Authentication.checkUserId( req.userId); const paramBoardId = req.params.boardId; const id = CustomFields.direct.insert({ @@ -117,7 +117,7 @@ if (Meteor.isServer) { }); }); - JsonRoutes.add('DELETE', '/api/boards/:boardId/custom-fields/:customFieldId', function (req, res, next) { + JsonRoutes.add('DELETE', '/api/boards/:boardId/custom-fields/:customFieldId', function (req, res) { Authentication.checkUserId( req.userId); const paramBoardId = req.params.boardId; const id = req.params.customFieldId; -- cgit v1.2.3-1-g7c22 From 838578657da4ed46be0e32091120c7554b07c102 Mon Sep 17 00:00:00 2001 From: Ignatz Date: Fri, 18 May 2018 10:43:43 +0200 Subject: template literals correction --- models/cards.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'models') diff --git a/models/cards.js b/models/cards.js index d3a741bb..c77cd682 100644 --- a/models/cards.js +++ b/models/cards.js @@ -332,7 +332,7 @@ Cards.mutations({ const index = this.customFieldIndex(customFieldId); if (index > -1) { const update = {$set: {}}; - update.$set['customFields.${index}.value'] = value; + update.$set[`customFields.${index}.value`] = value; return update; } // TODO -- cgit v1.2.3-1-g7c22 From 9b465bc98bd0e0125fa1d99369f1ed7ee47d9e63 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Fri, 18 May 2018 15:20:20 +0300 Subject: Fix lint errors back to eslint requirements. --- models/attachments.js | 158 +++++++++++++++++++++++++------------------------- 1 file changed, 79 insertions(+), 79 deletions(-) (limited to 'models') diff --git a/models/attachments.js b/models/attachments.js index 5e5c4926..91dd0dbc 100644 --- a/models/attachments.js +++ b/models/attachments.js @@ -1,90 +1,90 @@ - Attachments = new FS.Collection('attachments', { - stores: [ +Attachments = new FS.Collection('attachments', { + stores: [ - // XXX Add a new store for cover thumbnails so we don't load big images in - // the general board view - new FS.Store.GridFS('attachments', { - // If the uploaded document is not an image we need to enforce browser - // download instead of execution. This is particularly important for HTML - // files that the browser will just execute if we don't serve them with the - // appropriate `application/octet-stream` MIME header which can lead to user - // data leaks. I imagine other formats (like PDF) can also be attack vectors. - // See https://github.com/wekan/wekan/issues/99 - // XXX Should we use `beforeWrite` option of CollectionFS instead of - // collection-hooks? - // We should use `beforeWrite`. - beforeWrite: (fileObj) => { - if (!fileObj.isImage()) { - return { - type: 'application/octet-stream', - }; - } - return {}; - }, - }), - ], - }); - - - if (Meteor.isServer) { - Attachments.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)); - }, - // We authorize the attachment download either: - // - if the board is public, everyone (even unconnected) can download it - // - if the board is private, only board members can download it - download(userId, doc) { - const board = Boards.findOne(doc.boardId); - if (board.isPublic()) { - return true; - } else { - return board.hasMember(userId); + // XXX Add a new store for cover thumbnails so we don't load big images in + // the general board view + new FS.Store.GridFS('attachments', { + // If the uploaded document is not an image we need to enforce browser + // download instead of execution. This is particularly important for HTML + // files that the browser will just execute if we don't serve them with the + // appropriate `application/octet-stream` MIME header which can lead to user + // data leaks. I imagine other formats (like PDF) can also be attack vectors. + // See https://github.com/wekan/wekan/issues/99 + // XXX Should we use `beforeWrite` option of CollectionFS instead of + // collection-hooks? + // We should use `beforeWrite`. + beforeWrite: (fileObj) => { + if (!fileObj.isImage()) { + return { + type: 'application/octet-stream', + }; } + return {}; }, + }), + ], +}); - fetch: ['boardId'], - }); - } - - // XXX Enforce a schema for the Attachments CollectionFS - if (Meteor.isServer) { - Attachments.files.after.insert((userId, doc) => { - // If the attachment doesn't have a source field - // or its source is different than import - if (!doc.source || doc.source !== 'import') { - // Add activity about adding the attachment - Activities.insert({ - userId, - type: 'card', - activityType: 'addAttachment', - attachmentId: doc._id, - boardId: doc.boardId, - cardId: doc.cardId, - }); +if (Meteor.isServer) { + Attachments.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)); + }, + // We authorize the attachment download either: + // - if the board is public, everyone (even unconnected) can download it + // - if the board is private, only board members can download it + download(userId, doc) { + const board = Boards.findOne(doc.boardId); + if (board.isPublic()) { + return true; } else { - // Don't add activity about adding the attachment as the activity - // be imported and delete source field - Attachments.update({ - _id: doc._id, - }, { - $unset: { - source: '', - }, - }); + return board.hasMember(userId); } - }); + }, + + fetch: ['boardId'], + }); +} - Attachments.files.after.remove((userId, doc) => { - Activities.remove({ +// XXX Enforce a schema for the Attachments CollectionFS + +if (Meteor.isServer) { + Attachments.files.after.insert((userId, doc) => { + // If the attachment doesn't have a source field + // or its source is different than import + if (!doc.source || doc.source !== 'import') { + // Add activity about adding the attachment + Activities.insert({ + userId, + type: 'card', + activityType: 'addAttachment', attachmentId: doc._id, + boardId: doc.boardId, + cardId: doc.cardId, }); + } else { + // Don't add activity about adding the attachment as the activity + // be imported and delete source field + Attachments.update({ + _id: doc._id, + }, { + $unset: { + source: '', + }, + }); + } + }); + + Attachments.files.after.remove((userId, doc) => { + Activities.remove({ + attachmentId: doc._id, }); - } + }); +} -- cgit v1.2.3-1-g7c22