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 ;