123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617 |
- import { ReactiveCache } from '/imports/reactiveCache';
- CustomFields = new Mongo.Collection('customFields');
- /**
- * A custom field on a card in the board
- */
- CustomFields.attachSchema(
- new SimpleSchema({
- boardIds: {
- /**
- * the ID of the board
- */
- type: [String],
- },
- name: {
- /**
- * name of the custom field
- */
- type: String,
- },
- type: {
- /**
- * type of the custom field
- */
- type: String,
- allowedValues: [
- 'text',
- 'number',
- 'date',
- 'dropdown',
- 'checkbox',
- 'currency',
- 'stringtemplate',
- ],
- },
- settings: {
- /**
- * settings of the custom field
- */
- type: Object,
- },
- 'settings.currencyCode': {
- type: String,
- optional: true,
- },
- 'settings.dropdownItems': {
- /**
- * list of drop down items objects
- */
- type: [Object],
- optional: true,
- },
- 'settings.dropdownItems.$': {
- type: new SimpleSchema({
- _id: {
- /**
- * ID of the drop down item
- */
- type: String,
- },
- name: {
- /**
- * name of the drop down item
- */
- type: String,
- },
- }),
- },
- 'settings.stringtemplateFormat': {
- type: String,
- optional: true,
- },
- 'settings.stringtemplateSeparator': {
- type: String,
- optional: true,
- },
- showOnCard: {
- /**
- * should we show on the cards this custom field
- */
- type: Boolean,
- defaultValue: false,
- },
- automaticallyOnCard: {
- /**
- * should the custom fields automatically be added on cards?
- */
- type: Boolean,
- defaultValue: false,
- },
- alwaysOnCard: {
- /**
- * should the custom field be automatically added to all cards?
- */
- type: Boolean,
- defaultValue: false,
- },
- showLabelOnMiniCard: {
- /**
- * should the label of the custom field be shown on minicards?
- */
- type: Boolean,
- defaultValue: false,
- },
- showSumAtTopOfList: {
- /**
- * should the sum of the custom fields be shown at top of list?
- */
- type: Boolean,
- defaultValue: false,
- },
- createdAt: {
- type: Date,
- optional: true,
- // eslint-disable-next-line consistent-return
- autoValue() {
- if (this.isInsert) {
- return new Date();
- } else if (this.isUpsert) {
- return { $setOnInsert: new Date() };
- } else {
- this.unset();
- }
- },
- },
- modifiedAt: {
- type: Date,
- denyUpdate: false,
- // eslint-disable-next-line consistent-return
- autoValue() {
- if (this.isInsert || this.isUpsert || this.isUpdate) {
- return new Date();
- } else {
- this.unset();
- }
- },
- },
- }),
- );
- CustomFields.addToAllCards = cf => {
- Cards.update(
- {
- boardId: { $in: cf.boardIds },
- customFields: { $not: { $elemMatch: { _id: cf._id } } },
- },
- {
- $push: { customFields: { _id: cf._id, value: null } },
- },
- { multi: true },
- );
- };
- CustomFields.mutations({
- addBoard(boardId) {
- if (boardId) {
- return {
- $push: {
- boardIds: boardId,
- },
- };
- } else {
- return null;
- }
- },
- });
- CustomFields.allow({
- insert(userId, doc) {
- return allowIsAnyBoardMember(
- userId,
- ReactiveCache.getBoards({
- _id: { $in: doc.boardIds },
- }),
- );
- },
- update(userId, doc) {
- return allowIsAnyBoardMember(
- userId,
- ReactiveCache.getBoards({
- _id: { $in: doc.boardIds },
- }),
- );
- },
- remove(userId, doc) {
- return allowIsAnyBoardMember(
- userId,
- ReactiveCache.getBoards({
- _id: { $in: doc.boardIds },
- }),
- );
- },
- fetch: ['userId', 'boardIds'],
- });
- // not sure if we need this?
- //CustomFields.hookOptions.after.update = { fetchPrevious: false };
- function customFieldCreation(userId, doc) {
- Activities.insert({
- userId,
- activityType: 'createCustomField',
- boardId: doc.boardIds[0], // We are creating a customField, it has only one boardId
- customFieldId: doc._id,
- });
- }
- function customFieldDeletion(userId, doc) {
- Activities.insert({
- userId,
- activityType: 'deleteCustomField',
- boardId: doc.boardIds[0], // We are creating a customField, it has only one boardId
- customFieldId: doc._id,
- });
- }
- // This has some bug, it does not show edited customField value at Outgoing Webhook,
- // instead it shows undefined, and no listId and swimlaneId.
- function customFieldEdit(userId, doc) {
- const card = ReactiveCache.getCard(doc.cardId);
- const customFieldValue = ReactiveCache.getActivity({ customFieldId: doc._id }).value;
- Activities.insert({
- userId,
- activityType: 'setCustomField',
- boardId: doc.boardIds[0], // We are creating a customField, it has only one boardId
- customFieldId: doc._id,
- customFieldValue,
- listId: doc.listId,
- swimlaneId: doc.swimlaneId,
- });
- }
- if (Meteor.isServer) {
- Meteor.startup(() => {
- CustomFields._collection.createIndex({ modifiedAt: -1 });
- CustomFields._collection.createIndex({ boardIds: 1 });
- });
- CustomFields.after.insert((userId, doc) => {
- customFieldCreation(userId, doc);
- if (doc.alwaysOnCard) {
- CustomFields.addToAllCards(doc);
- }
- });
- CustomFields.before.update((userId, doc, fieldNames, modifier) => {
- if (_.contains(fieldNames, 'boardIds') && modifier.$pull) {
- Cards.update(
- { boardId: modifier.$pull.boardIds, 'customFields._id': doc._id },
- { $pull: { customFields: { _id: doc._id } } },
- { multi: true },
- );
- customFieldEdit(userId, doc);
- Activities.remove({
- customFieldId: doc._id,
- boardId: modifier.$pull.boardIds,
- listId: doc.listId,
- swimlaneId: doc.swimlaneId,
- });
- } else if (_.contains(fieldNames, 'boardIds') && modifier.$push) {
- Activities.insert({
- userId,
- activityType: 'createCustomField',
- boardId: modifier.$push.boardIds,
- customFieldId: doc._id,
- });
- }
- });
- CustomFields.after.update((userId, doc) => {
- if (doc.alwaysOnCard) {
- CustomFields.addToAllCards(doc);
- }
- });
- CustomFields.before.remove((userId, doc) => {
- customFieldDeletion(userId, doc);
- Activities.remove({
- customFieldId: doc._id,
- });
- Cards.update(
- { boardId: { $in: doc.boardIds }, 'customFields._id': doc._id },
- { $pull: { customFields: { _id: doc._id } } },
- { multi: true },
- );
- });
- }
- //CUSTOM FIELD REST API
- if (Meteor.isServer) {
- /**
- * @operation get_all_custom_fields
- * @summary Get the list of Custom Fields attached to a board
- *
- * @param {string} boardID the ID of the board
- * @return_type [{_id: string,
- * name: string,
- * type: string}]
- */
- JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', function(
- req,
- res,
- ) {
- const paramBoardId = req.params.boardId;
- Authentication.checkBoardAccess(req.userId, paramBoardId);
- JsonRoutes.sendResult(res, {
- code: 200,
- data: ReactiveCache.getCustomFields({ boardIds: { $in: [paramBoardId] } }).map(
- function(cf) {
- return {
- _id: cf._id,
- name: cf.name,
- type: cf.type,
- };
- },
- ),
- });
- });
- /**
- * @operation get_custom_field
- * @summary Get a Custom Fields attached to a board
- *
- * @param {string} boardID the ID of the board
- * @param {string} customFieldId the ID of the custom field
- * @return_type [{_id: string,
- * boardIds: string}]
- */
- JsonRoutes.add(
- 'GET',
- '/api/boards/:boardId/custom-fields/:customFieldId',
- function(req, res) {
- const paramBoardId = req.params.boardId;
- const paramCustomFieldId = req.params.customFieldId;
- Authentication.checkBoardAccess(req.userId, paramBoardId);
- JsonRoutes.sendResult(res, {
- code: 200,
- data: ReactiveCache.getCustomField({
- _id: paramCustomFieldId,
- boardIds: { $in: [paramBoardId] },
- }),
- });
- },
- );
- /**
- * @operation new_custom_field
- * @summary Create a Custom Field
- *
- * @param {string} boardID the ID of the board
- * @param {string} name the name of the custom field
- * @param {string} type the type of the custom field
- * @param {string} settings the settings object of the custom field
- * @param {boolean} showOnCard should we show the custom field on cards?
- * @param {boolean} automaticallyOnCard should the custom fields automatically be added on cards?
- * @param {boolean} showLabelOnMiniCard should the label of the custom field be shown on minicards?
- * @param {boolean} showSumAtTopOfList should the sum of the custom fields be shown at top of list?
- * @return_type {_id: string}
- */
- JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function(
- req,
- res,
- ) {
- const paramBoardId = req.params.boardId;
- Authentication.checkBoardAccess(req.userId, paramBoardId);
- const board = ReactiveCache.getBoard(paramBoardId);
- const id = CustomFields.direct.insert({
- name: req.body.name,
- type: req.body.type,
- settings: req.body.settings,
- showOnCard: req.body.showOnCard,
- automaticallyOnCard: req.body.automaticallyOnCard,
- showLabelOnMiniCard: req.body.showLabelOnMiniCard,
- showSumAtTopOfList: req.body.showSumAtTopOfList,
- boardIds: [board._id],
- });
- const customField = ReactiveCache.getCustomField({
- _id: id,
- boardIds: { $in: [paramBoardId] },
- });
- customFieldCreation(req.body.authorId, customField);
- JsonRoutes.sendResult(res, {
- code: 200,
- data: {
- _id: id,
- },
- });
- });
- /**
- * @operation edit_custom_field
- * @summary Update a Custom Field
- *
- * @param {string} name the name of the custom field
- * @param {string} type the type of the custom field
- * @param {string} settings the settings object of the custom field
- * @param {boolean} showOnCard should we show the custom field on cards
- * @param {boolean} automaticallyOnCard should the custom fields automatically be added on cards
- * @param {boolean} showLabelOnMiniCard should the label of the custom field be shown on minicards
- * @param {boolean} showSumAtTopOfList should the sum of the custom fields be shown at top of list
- * @return_type {_id: string}
- */
- JsonRoutes.add(
- 'PUT',
- '/api/boards/:boardId/custom-fields/:customFieldId',
- (req, res) => {
- const paramBoardId = req.params.boardId;
- const paramFieldId = req.params.customFieldId;
- Authentication.checkBoardAccess(req.userId, paramBoardId);
- if (req.body.hasOwnProperty('name')) {
- CustomFields.direct.update(
- { _id: paramFieldId },
- { $set: { name: req.body.name } },
- );
- }
- if (req.body.hasOwnProperty('type')) {
- CustomFields.direct.update(
- { _id: paramFieldId },
- { $set: { type: req.body.type } },
- );
- }
- if (req.body.hasOwnProperty('settings')) {
- CustomFields.direct.update(
- { _id: paramFieldId },
- { $set: { settings: req.body.settings } },
- );
- }
- if (req.body.hasOwnProperty('showOnCard')) {
- CustomFields.direct.update(
- { _id: paramFieldId },
- { $set: { showOnCard: req.body.showOnCard } },
- );
- }
- if (req.body.hasOwnProperty('automaticallyOnCard')) {
- CustomFields.direct.update(
- { _id: paramFieldId },
- { $set: { automaticallyOnCard: req.body.automaticallyOnCard } },
- );
- }
- if (req.body.hasOwnProperty('alwaysOnCard')) {
- CustomFields.direct.update(
- { _id: paramFieldId },
- { $set: { alwaysOnCard: req.body.alwaysOnCard } },
- );
- }
- if (req.body.hasOwnProperty('showLabelOnMiniCard')) {
- CustomFields.direct.update(
- { _id: paramFieldId },
- { $set: { showLabelOnMiniCard: req.body.showLabelOnMiniCard } },
- );
- }
- if (req.body.hasOwnProperty('showSumAtTopOfList')) {
- CustomFields.direct.update(
- { _id: paramFieldId },
- { $set: { showSumAtTopOfList: req.body.showSumAtTopOfList } },
- );
- }
- JsonRoutes.sendResult(res, {
- code: 200,
- data: { _id: paramFieldId },
- });
- },
- );
- /**
- * @operation add_custom_field_dropdown_items
- * @summary Update a Custom Field's dropdown items
- *
- * @param {string} [items] names of the custom field
- * @return_type {_id: string}
- */
- JsonRoutes.add(
- 'POST',
- '/api/boards/:boardId/custom-fields/:customFieldId/dropdown-items',
- (req, res) => {
- const paramBoardId = req.params.boardId;
- const paramCustomFieldId = req.params.customFieldId;
- Authentication.checkBoardAccess(req.userId, paramBoardId);
- const paramItems = req.body.items;
- if (req.body.hasOwnProperty('items')) {
- if (Array.isArray(paramItems)) {
- CustomFields.direct.update(
- { _id: paramCustomFieldId },
- {
- $push: {
- 'settings.dropdownItems': {
- $each: paramItems
- .filter(name => typeof name === 'string')
- .map(name => ({
- _id: Random.id(6),
- name,
- })),
- },
- },
- },
- );
- }
- }
- JsonRoutes.sendResult(res, {
- code: 200,
- data: { _id: paramCustomFieldId },
- });
- },
- );
- /**
- * @operation edit_custom_field_dropdown_item
- * @summary Update a Custom Field's dropdown item
- *
- * @param {string} name names of the custom field
- * @return_type {_id: string}
- */
- JsonRoutes.add(
- 'PUT',
- '/api/boards/:boardId/custom-fields/:customFieldId/dropdown-items/:dropdownItemId',
- (req, res) => {
- const paramBoardId = req.params.boardId;
- const paramDropdownItemId = req.params.dropdownItemId;
- const paramCustomFieldId = req.params.customFieldId;
- Authentication.checkBoardAccess(req.userId, paramBoardId);
- const paramName = req.body.name;
- if (req.body.hasOwnProperty('name')) {
- CustomFields.direct.update(
- {
- _id: paramCustomFieldId,
- 'settings.dropdownItems._id': paramDropdownItemId,
- },
- {
- $set: {
- 'settings.dropdownItems.$': {
- _id: paramDropdownItemId,
- name: paramName,
- },
- },
- },
- );
- }
- JsonRoutes.sendResult(res, {
- code: 200,
- data: { _id: paramDropdownItemId },
- });
- },
- );
- /**
- * @operation delete_custom_field_dropdown_item
- * @summary Update a Custom Field's dropdown items
- *
- * @param {string} itemId ID of the dropdown item
- * @return_type {_id: string}
- */
- JsonRoutes.add(
- 'DELETE',
- '/api/boards/:boardId/custom-fields/:customFieldId/dropdown-items/:dropdownItemId',
- (req, res) => {
- const paramBoardId = req.params.boardId;
- paramCustomFieldId = req.params.customFieldId;
- paramDropdownItemId = req.params.dropdownItemId;
- Authentication.checkBoardAccess(req.userId, paramBoardId);
- CustomFields.direct.update(
- { _id: paramCustomFieldId },
- {
- $pull: {
- 'settings.dropdownItems': { _id: paramDropdownItemId },
- },
- },
- );
- JsonRoutes.sendResult(res, {
- code: 200,
- data: { _id: paramCustomFieldId },
- });
- },
- );
- /**
- * @operation delete_custom_field
- * @summary Delete a Custom Fields attached to a board
- *
- * @description The Custom Field can't be retrieved after this operation
- *
- * @param {string} boardID the ID of the board
- * @param {string} customFieldId the ID of the custom field
- * @return_type {_id: string}
- */
- JsonRoutes.add(
- 'DELETE',
- '/api/boards/:boardId/custom-fields/:customFieldId',
- function(req, res) {
- const paramBoardId = req.params.boardId;
- Authentication.checkBoardAccess(req.userId, paramBoardId);
- const id = req.params.customFieldId;
- CustomFields.remove({ _id: id, boardIds: { $in: [paramBoardId] } });
- JsonRoutes.sendResult(res, {
- code: 200,
- data: {
- _id: id,
- },
- });
- },
- );
- }
- export default CustomFields;
|