diff --git a/application/Espo/Controllers/EntityManager.php b/application/Espo/Controllers/EntityManager.php index 76d46d2747..8c4fd69eb2 100644 --- a/application/Espo/Controllers/EntityManager.php +++ b/application/Espo/Controllers/EntityManager.php @@ -87,6 +87,11 @@ class EntityManager extends \Espo\Core\Controllers\Base $params['color'] = $data['color']; } + $params['kanbanViewMode'] = !empty($data['kanbanViewMode']); + if (!empty($data['kanbanStatusIgnoreList'])) { + $params['kanbanStatusIgnoreList'] = $data['kanbanStatusIgnoreList']; + } + $result = $this->getContainer()->get('entityManagerUtil')->create($name, $type, $params); if ($result) { diff --git a/application/Espo/Core/Utils/EntityManager.php b/application/Espo/Core/Utils/EntityManager.php index e4d2a940e7..a85f441ba3 100644 --- a/application/Espo/Core/Utils/EntityManager.php +++ b/application/Espo/Core/Utils/EntityManager.php @@ -283,6 +283,10 @@ class EntityManager $scopesData['isNotRemovable'] = true; } + if (!empty($params['kanbanStatusIgnoreList'])) { + $scopesData['kanbanStatusIgnoreList'] = $params['kanbanStatusIgnoreList']; + } + $this->getMetadata()->set('scopes', $name, $scopesData); $filePath = $templatePath . "/Metadata/{$type}/entityDefs.json"; @@ -306,6 +310,10 @@ class EntityManager if (array_key_exists('color', $params)) { $clientDefsData['color'] = $params['color']; }; + + if (!empty($params['kanbanViewMode'])) { + $clientDefsData['kanbanViewMode'] = true; + } $this->getMetadata()->set('clientDefs', $name, $clientDefsData); $this->getBaseLanguage()->set('Global', 'scopeNames', $name, $labelSingular); @@ -391,6 +399,18 @@ class EntityManager $this->getMetadata()->set('entityDefs', $name, $entityDefsData); } + if (array_key_exists('kanbanStatusIgnoreList', $data)) { + $scopeData['kanbanStatusIgnoreList'] = $data['kanbanStatusIgnoreList']; + $this->getMetadata()->set('scopes', $name, $scopeData); + } + + if (array_key_exists('kanbanViewMode', $data)) { + $clientDefsData = [ + 'kanbanViewMode' => $data['kanbanViewMode'] + ]; + $this->getMetadata()->set('clientDefs', $name, $clientDefsData); + } + if (array_key_exists('color', $data)) { $clientDefsData = [ 'color' => $data['color'] diff --git a/application/Espo/Resources/i18n/en_US/EntityManager.json b/application/Espo/Resources/i18n/en_US/EntityManager.json index 99aea3ed5f..fd26ce1147 100644 --- a/application/Espo/Resources/i18n/en_US/EntityManager.json +++ b/application/Espo/Resources/i18n/en_US/EntityManager.json @@ -29,7 +29,9 @@ "auditedForeign": "Foreign Audited", "statusField": "Status Field", "beforeSaveCustomScript": "Before Save Custom Script", - "color": "Color" + "color": "Color", + "kanbanViewMode": "Kanban View", + "kanbanStatusIgnoreList": "Ignored groups in Kanban view" }, "options": { "type": { diff --git a/client/res/templates/admin/entity-manager/modals/edit-entity.tpl b/client/res/templates/admin/entity-manager/modals/edit-entity.tpl index 415ebe4b6d..41916230c5 100644 --- a/client/res/templates/admin/entity-manager/modals/edit-entity.tpl +++ b/client/res/templates/admin/entity-manager/modals/edit-entity.tpl @@ -69,14 +69,27 @@ {{{textFilterFields}}} - {{#if stream}}
{{{statusField}}}
- {{/if}} + + +
+
+ +
+ {{{kanbanViewMode}}} +
+
+
+ +
+ {{{kanbanStatusIgnoreList}}} +
+
{{/unless}} diff --git a/client/src/views/admin/entity-manager/modals/edit-entity.js b/client/src/views/admin/entity-manager/modals/edit-entity.js index b5ae02034b..0f39b2a9b0 100644 --- a/client/src/views/admin/entity-manager/modals/edit-entity.js +++ b/client/src/views/admin/entity-manager/modals/edit-entity.js @@ -91,6 +91,9 @@ Espo.define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'mo this.model.set('statusField', this.getMetadata().get('scopes.' + scope + '.statusField') || null); this.model.set('color', this.getMetadata().get(['clientDefs', scope, 'color']) || null); + + this.model.set('kanbanViewMode', this.getMetadata().get(['clientDefs', scope, 'kanbanViewMode']) || false); + this.model.set('kanbanStatusIgnoreList', this.getMetadata().get(['scopes', scope, 'kanbanStatusIgnoreList']) || []); } this.createView('type', 'views/fields/enum', { @@ -222,6 +225,15 @@ Espo.define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'mo } }); + this.createView('kanbanViewMode', 'views/fields/bool', { + model: model, + mode: 'edit', + el: this.options.el + ' .field[data-name="kanbanViewMode"]', + defs: { + name: 'kanbanViewMode' + } + }); + var optionList = Object.keys(fieldDefs).filter(function (item) { if (!~['varchar', 'text', 'phone', 'email', 'personName', 'number'].indexOf(this.getMetadata().get(['entityDefs', scope, 'fields', item, 'type']))) { return false; @@ -253,39 +265,53 @@ Espo.define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'mo }); - if (this.hasStreamField) { - var enumFieldList = Object.keys(fieldDefs).filter(function (item) { - if (fieldDefs[item].disabled) return; - if (fieldDefs[item].type == 'enum') { - return true; + var enumFieldList = Object.keys(fieldDefs).filter(function (item) { + if (fieldDefs[item].disabled) return; + if (fieldDefs[item].type == 'enum') { + return true; + } + return; + }, this).sort(function (v1, v2) { + return this.translate(v1, 'fields', scope).localeCompare(this.translate(v2, 'fields', scope)); + }.bind(this)); + + var translatedStatusFields = {}; + enumFieldList.forEach(function (item) { + translatedStatusFields[item] = this.translate(item, 'fields', scope); + }, this); + enumFieldList.unshift(''); + translatedStatusFields[''] = '-' + this.translate('None') + '-'; + + this.createView('statusField', 'views/fields/enum', { + model: model, + mode: 'edit', + el: this.options.el + ' .field[data-name="statusField"]', + defs: { + name: 'statusField', + params: { + options: enumFieldList } - return; - }, this).sort(function (v1, v2) { - return this.translate(v1, 'fields', scope).localeCompare(this.translate(v2, 'fields', scope)); - }.bind(this)); + }, + tooltip: true, + tooltipText: this.translate('statusField', 'tooltips', 'EntityManager'), + translatedOptions: translatedStatusFields + }); - var translatedStatusFields = {}; - enumFieldList.forEach(function (item) { - translatedStatusFields[item] = this.translate(item, 'fields', scope); - }, this); - enumFieldList.unshift(''); - translatedStatusFields[''] = '-' + this.translate('None') + '-'; + var statusOptionList = []; + var translatedStatusOptions = {}; - this.createView('statusField', 'views/fields/enum', { - model: model, - mode: 'edit', - el: this.options.el + ' .field[data-name="statusField"]', - defs: { - name: 'statusField', - params: { - options: enumFieldList - } - }, - tooltip: true, - tooltipText: this.translate('statusField', 'tooltips', 'EntityManager'), - translatedOptions: translatedStatusFields - }); - } + this.createView('kanbanStatusIgnoreList', 'views/fields/multi-enum', { + model: model, + mode: 'edit', + el: this.options.el + ' .field[data-name="kanbanStatusIgnoreList"]', + defs: { + name: 'kanbanStatusIgnoreList', + params: { + options: statusOptionList + } + }, + translatedOptions: translatedStatusOptions + }); } this.model.fetchedAttributes = this.model.getClonedAttributes(); }, @@ -325,22 +351,62 @@ Espo.define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'mo this.model.set('name', name); }, this); - this.manageStreamField(); - this.listenTo(this.model, 'change:stream', function () { - this.manageStreamField(); - }, this); + if (!this.isNew) { + this.manageKanbanFields({}); + this.listenTo(this.model, 'change:statusField', function (m, value, o) { + this.manageKanbanFields(o); + }, this); + + this.manageKanbanViewModeField(); + this.listenTo(this.model, 'change:kanbanViewMode', function () { + this.manageKanbanViewModeField(); + }, this); + } if (this.isNew) { this.hideField('disabled'); } }, - manageStreamField: function () { - if (this.model.get('stream')) { - this.showField('statusField'); - } else { - this.hideField('statusField'); + manageKanbanFields: function (o) { + if (o.ui) { + this.model.set('kanbanStatusIgnoreList', []); } + if (this.model.get('statusField')) { + this.setKanbanStatusIgnoreListOptions(); + this.showField('kanbanViewMode'); + if (this.model.get('kanbanViewMode')) { + this.showField('kanbanStatusIgnoreList'); + } else { + this.hideField('kanbanStatusIgnoreList'); + } + } else { + this.hideField('kanbanViewMode'); + this.hideField('kanbanStatusIgnoreList'); + } + }, + + manageKanbanViewModeField: function () { + if (this.model.get('kanbanViewMode')) { + this.showField('kanbanStatusIgnoreList'); + } else { + this.hideField('kanbanStatusIgnoreList'); + } + }, + + setKanbanStatusIgnoreListOptions: function () { + var statusField = this.model.get('statusField'); + var fieldView = this.getView('kanbanStatusIgnoreList'); + + var optionList = this.getMetadata().get(['entityDefs', this.scope, 'fields', statusField, 'options']) || []; + var translation = this.getMetadata().get(['entityDefs', this.scope, 'fields', statusField, 'translation']) || this.scope + '.options.' + statusField; + + fieldView.params.options = optionList; + fieldView.params.translation = translation; + + fieldView.setupTranslation(); + + fieldView.setOptionList(optionList); }, actionSave: function () { @@ -357,6 +423,8 @@ Espo.define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'mo if (this.scope) { arr.push('sortBy'); arr.push('sortDirection'); + arr.push('kanbanViewMode'); + arr.push('kanbanStatusIgnoreList'); } var notValid = false; @@ -405,6 +473,8 @@ Espo.define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'mo if (this.scope) { data.sortBy = this.model.get('sortBy'); data.sortDirection = this.model.get('sortDirection'); + data.kanbanViewMode = this.model.get('kanbanViewMode'); + data.kanbanStatusIgnoreList = this.model.get('kanbanStatusIgnoreList'); } if (!this.isNew) { diff --git a/client/src/views/fields/array.js b/client/src/views/fields/array.js index f9eb91258c..882092b7c6 100644 --- a/client/src/views/fields/array.js +++ b/client/src/views/fields/array.js @@ -105,32 +105,7 @@ Espo.define('views/fields/array', ['views/fields/base', 'lib!Selectize'], functi } if (!this.translatedOptions) { - var t = {}; - if (this.params.translation) { - var data = this.getLanguage().data; - var arr = this.params.translation.split('.'); - var pointer = this.getLanguage().data; - arr.forEach(function (key) { - if (key in pointer) { - pointer = pointer[key]; - t = pointer; - } - }, this); - } else { - t = this.translate(this.name, 'options', this.model.name); - } - this.translatedOptions = null; - var translatedOptions = {}; - if (this.params.options) { - this.params.options.forEach(function (o) { - if (typeof t === 'object' && o in t) { - translatedOptions[o] = t[o]; - } else { - translatedOptions[o] = o; - } - }.bind(this)); - this.translatedOptions = translatedOptions; - } + this.setupTranslation(); } if (this.options.customOptionList) { @@ -138,6 +113,35 @@ Espo.define('views/fields/array', ['views/fields/base', 'lib!Selectize'], functi } }, + setupTranslation: function () { + var t = {}; + if (this.params.translation) { + var data = this.getLanguage().data; + var arr = this.params.translation.split('.'); + var pointer = this.getLanguage().data; + arr.forEach(function (key) { + if (key in pointer) { + pointer = pointer[key]; + t = pointer; + } + }, this); + } else { + t = this.translate(this.name, 'options', this.model.name); + } + this.translatedOptions = null; + var translatedOptions = {}; + if (this.params.options) { + this.params.options.forEach(function (o) { + if (typeof t === 'object' && o in t) { + translatedOptions[o] = t[o]; + } else { + translatedOptions[o] = o; + } + }.bind(this)); + this.translatedOptions = translatedOptions; + } + }, + setupOptions: function () { }, diff --git a/client/src/views/fields/enum.js b/client/src/views/fields/enum.js index 132fb6d13c..a6800911e7 100644 --- a/client/src/views/fields/enum.js +++ b/client/src/views/fields/enum.js @@ -80,6 +80,28 @@ Espo.define('views/fields/enum', ['views/fields/base', 'lib!Selectize'], functio this.translatedOptions = this.params.translatedOptions; } + this.setupTranslation(); + + if (this.translatedOptions === null) { + this.translatedOptions = this.getLanguage().translate(this.name, 'options', this.model.name) || {}; + if (this.translatedOptions === this.name) { + this.translatedOptions = null; + } + } + + if (this.params.isSorted && this.translatedOptions) { + this.params.options = Espo.Utils.clone(this.params.options); + this.params.options = this.params.options.sort(function (v1, v2) { + return (this.translatedOptions[v1] || v1).localeCompare(this.translatedOptions[v2] || v2); + }.bind(this)); + } + + if (this.options.customOptionList) { + this.setOptionList(this.options.customOptionList); + } + }, + + setupTranslation: function () { if (this.params.translation) { var translationObj; var data = this.getLanguage().data; @@ -111,24 +133,6 @@ Espo.define('views/fields/enum', ['views/fields/base', 'lib!Selectize'], functio this.translatedOptions = translatedOptions; } } - - if (this.translatedOptions === null) { - this.translatedOptions = this.getLanguage().translate(this.name, 'options', this.model.name) || {}; - if (this.translatedOptions === this.name) { - this.translatedOptions = null; - } - } - - if (this.params.isSorted && this.translatedOptions) { - this.params.options = Espo.Utils.clone(this.params.options); - this.params.options = this.params.options.sort(function (v1, v2) { - return (this.translatedOptions[v1] || v1).localeCompare(this.translatedOptions[v2] || v2); - }.bind(this)); - } - - if (this.options.customOptionList) { - this.setOptionList(this.options.customOptionList); - } }, setupOptions: function () {