| 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 APIif (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: customFieldId },      });    },  );  /**   * @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;
 |