From c0719694ba7e24233f97281dd73fa12fd2f5a482 Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 31 Oct 2018 12:59:52 +0200 Subject: [PATCH] panel actions improvement --- client/res/templates/record/bottom.tpl | 26 +- client/res/templates/record/panel-actions.tpl | 22 ++ client/res/templates/record/side.tpl | 26 +- client/src/utils.js | 5 +- client/src/views/record/detail-bottom.js | 171 +------------ client/src/views/record/detail-side.js | 165 +----------- client/src/views/record/detail.js | 4 +- client/src/views/record/panel-actions.js | 69 +++++ client/src/views/record/panels-container.js | 238 ++++++++++++++++++ .../src/views/record/panels/relationship.js | 18 +- 10 files changed, 357 insertions(+), 387 deletions(-) create mode 100644 client/res/templates/record/panel-actions.tpl create mode 100644 client/src/views/record/panel-actions.js create mode 100644 client/src/views/record/panels-container.js diff --git a/client/res/templates/record/bottom.tpl b/client/res/templates/record/bottom.tpl index 8196cdf7a4..0ac0a982ae 100644 --- a/client/res/templates/record/bottom.tpl +++ b/client/res/templates/record/bottom.tpl @@ -1,31 +1,7 @@ {{#each panelList}}
-
- {{#if buttonList}} - {{#each buttonList}} - - {{/each}} - {{/if}} - {{#if actionList}} - - - {{/if}} -
+
{{{var actionsViewKey ../this}}}

{{#unless notRefreshable}} diff --git a/client/res/templates/record/panel-actions.tpl b/client/res/templates/record/panel-actions.tpl new file mode 100644 index 0000000000..c51344be5c --- /dev/null +++ b/client/res/templates/record/panel-actions.tpl @@ -0,0 +1,22 @@ +{{#each buttonList}} + +{{/each}} + +{{#if actionList}} + + +{{/if}} diff --git a/client/res/templates/record/side.tpl b/client/res/templates/record/side.tpl index 78969efec4..851c1115d3 100644 --- a/client/res/templates/record/side.tpl +++ b/client/res/templates/record/side.tpl @@ -2,31 +2,7 @@
{{#if label}}
-
- {{#if buttonList}} - {{#each buttonList}} - - {{/each}} - {{/if}} - {{#if actionList}} - - - {{/if}} -
+
{{{var ../actionsViewKey ../../this}}}

{{#unless notRefreshable}} diff --git a/client/src/utils.js b/client/src/utils.js index 5f764b6479..b25c229ffa 100644 --- a/client/src/utils.js +++ b/client/src/utils.js @@ -27,9 +27,10 @@ ************************************************************************/ Espo.define('utils', [], function () { + var Utils = Espo.utils = Espo.Utils = { - checkActionAccess: function (acl, obj, item) { + checkActionAccess: function (acl, obj, item, isPrecise) { var hasAccess = true; if (item.acl) { if (!item.aclScope) { @@ -37,7 +38,7 @@ Espo.define('utils', [], function () { if (typeof obj == 'string' || obj instanceof String) { hasAccess = acl.check(obj, item.acl); } else { - hasAccess = acl.checkModel(obj, item.acl); + hasAccess = acl.checkModel(obj, item.acl, isPrecise); } } else { hasAccess = acl.check(item.scope, item.acl); diff --git a/client/src/views/record/detail-bottom.js b/client/src/views/record/detail-bottom.js index ee28246d3a..e629086b54 100644 --- a/client/src/views/record/detail-bottom.js +++ b/client/src/views/record/detail-bottom.js @@ -26,7 +26,7 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -Espo.define('views/record/detail-bottom', ['view'], function (Dep) { +define('views/record/detail-bottom', 'views/record/panels-container', function (Dep) { return Dep.extend({ @@ -42,95 +42,6 @@ Espo.define('views/record/detail-bottom', ['view'], function (Dep) { portalLayoutDisabled: false, - data: function () { - return { - panelList: this.panelList, - scope: this.scope, - entityType: this.entityType - }; - }, - - events: { - 'click .action': function (e) { - var $target = $(e.currentTarget); - var action = $target.data('action'); - var panel = $target.data('panel'); - var data = $target.data(); - if (action) { - var method = 'action' + Espo.Utils.upperCaseFirst(action); - var d = _.clone(data); - delete d['action']; - delete d['panel']; - var view = this.getView(panel); - if (view && typeof view[method] == 'function') { - view[method].call(view, d, e); - } - } - } - }, - - showPanel: function (name, callback) { - this.recordHelper.setPanelStateParam(name, 'hidden', false); - - var isFound = false; - this.panelList.forEach(function (d) { - if (d.name == name) { - d.hidden = false; - isFound = true; - } - }, this); - if (!isFound) return; - - if (this.isRendered()) { - var view = this.getView(name); - if (view) { - view.$el.closest('.panel').removeClass('hidden'); - view.disabled = false; - view.trigger('show'); - } - if (callback) { - callback.call(this); - } - } else { - if (callback) { - this.once('after:render', function () { - callback.call(this); - }, this); - } - } - }, - - hidePanel: function (name, callback) { - this.recordHelper.setPanelStateParam(name, 'hidden', true); - - var isFound = false; - this.panelList.forEach(function (d) { - if (d.name == name) { - d.hidden = true; - isFound = true; - } - }, this); - if (!isFound) return; - - if (this.isRendered()) { - var view = this.getView(name); - if (view) { - view.$el.closest('.panel').addClass('hidden'); - view.disabled = true; - view.trigger('hide'); - } - if (callback) { - callback.call(this); - } - } else { - if (callback) { - this.once('after:render', function () { - callback.call(this); - }, this); - } - } - }, - setupPanels: function () { var scope = this.scope; @@ -165,41 +76,6 @@ Espo.define('views/record/detail-bottom', ['view'], function (Dep) { } }, - setupPanelViews: function () { - this.panelList.forEach(function (p) { - var name = p.name; - this.createView(name, p.view, { - model: this.model, - panelName: name, - el: this.options.el + ' .panel[data-name="' + name + '"] > .panel-body', - defs: p, - mode: this.mode, - recordHelper: this.recordHelper, - inlineEditDisabled: this.inlineEditDisabled, - readOnly: this.readOnly, - disabled: p.hidden || false, - recordViewObject: this.recordViewObject - }, function (view) { - if ('getActionList' in view) { - p.actionList = this.filterActions(view.getActionList()); - } - if ('getButtonList' in view) { - p.buttonList = this.filterActions(view.getButtonList()); - } - - if (view.titleHtml) { - p.titleHtml = view.titleHtml; - } else { - if (p.label) { - p.title = this.translate(p.label, 'labels', this.scope); - } else { - p.title = view.title; - } - } - }, this); - }, this); - }, - init: function () { this.recordHelper = this.options.recordHelper; this.scope = this.entityType = this.model.name; @@ -279,12 +155,20 @@ Espo.define('views/record/detail-bottom', ['view'], function (Dep) { return order1 - order2; }); + this.panelList.forEach(function (item) { + item.actionsViewKey = item.name + 'Actions'; + }, this); + this.setupPanelViews(); this.wait(false); }.bind(this)); }, + setReadOnly: function () { + this.readOnly = true; + }, + loadRelationshipsLayout: function (callback) { var layoutName = 'relationships'; if (this.getUser().isPortal() && !this.portalLayoutDisabled) { @@ -353,42 +237,5 @@ Espo.define('views/record/detail-bottom', ['view'], function (Dep) { this.panelList.push(p); }, this); }, - - filterActions: function (actions) { - var filtered = []; - actions.forEach(function (item) { - if (Espo.Utils.checkActionAccess(this.getAcl(), this.model, item)) { - filtered.push(item); - } - }.bind(this)); - return filtered; - }, - - getFieldViews: function (withHidden) { - var fields = {}; - this.panelList.forEach(function (p) { - var panelView = this.getView(p.name); - if ((!panelView.disabled || withHidden) && 'getFieldViews' in panelView) { - fields = _.extend(fields, panelView.getFieldViews()); - } - }, this); - return fields; - }, - - getFields: function () { - return this.getFieldViews(); - }, - - fetch: function () { - var data = {}; - - this.panelList.forEach(function (p) { - var panelView = this.getView(p.name); - if (!panelView.disabled && 'fetch' in panelView) { - data = _.extend(data, panelView.fetch()); - } - }, this); - return data; - } }); }); diff --git a/client/src/views/record/detail-side.js b/client/src/views/record/detail-side.js index 4e45345d50..0b45377290 100644 --- a/client/src/views/record/detail-side.js +++ b/client/src/views/record/detail-side.js @@ -26,7 +26,7 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -Espo.define('views/record/detail-side', ['view'], function (Dep) { +Espo.define('views/record/detail-side', 'views/record/panels-container', function (Dep) { return Dep.extend({ @@ -59,33 +59,6 @@ Espo.define('views/record/detail-side', ['view'], function (Dep) { } }, - data: function () { - return { - panelList: this.panelList, - scope: this.scope, - entityType: this.entityType - }; - }, - - events: { - 'click .action': function (e) { - var $target = $(e.currentTarget); - var action = $target.data('action'); - var panel = $target.data('panel'); - var data = $target.data(); - if (action) { - var method = 'action' + Espo.Utils.upperCaseFirst(action); - var d = _.clone(data); - delete d['action']; - delete d['panel']; - var view = this.getView(panel); - if (view && typeof view[method] == 'function') { - view[method].call(view, d); - } - } - }, - }, - init: function () { this.panelList = this.options.panelList || this.panelList; this.scope = this.entityType = this.options.model.name; @@ -145,6 +118,10 @@ Espo.define('views/record/detail-side', ['view'], function (Dep) { return item; }, this); + this.panelList.forEach(function (item) { + item.actionsViewKey = item.name + 'Actions'; + }, this); + this.wait(true); this.getHelper().layoutManager.get(this.scope, 'sidePanels' + Espo.Utils.upperCaseFirst(this.type), function (layoutData) { if (layoutData) { @@ -247,137 +224,5 @@ Espo.define('views/record/detail-side', ['view'], function (Dep) { this.panelList.unshift(defaultPanelDefs); }, - setupPanelViews: function () { - this.panelList.forEach(function (p) { - var o = { - model: this.options.model, - el: this.options.el + ' .panel[data-name="' + p.name + '"] > .panel-body', - readOnly: this.readOnly, - inlineEditDisabled: this.inlineEditDisabled, - mode: this.mode, - recordHelper: this.recordHelper, - defs: p, - disabled: p.hidden || false, - recordViewObject: this.recordViewObject - }; - o = _.extend(o, p.options); - this.createView(p.name, p.view, o, function (view) { - if ('getButtonList' in view) { - p.buttonList = this.filterActions(view.getButtonList()); - } - if ('getActionList' in view) { - p.actionList = this.filterActions(view.getActionList()); - } - if (p.label) { - p.title = this.translate(p.label, 'labels', this.scope); - } else { - p.title = view.title; - } - if (view.titleHtml) { - p.titleHtml = view.titleHtml; - } - }, this); - }, this); - }, - - getFieldViews: function (withHidden) { - var fields = {}; - this.panelList.forEach(function (p) { - var panelView = this.getView(p.name); - if ((!panelView.disabled || withHidden) && 'getFieldViews' in panelView) { - fields = _.extend(fields, panelView.getFieldViews()); - } - }, this); - return fields; - }, - - getFields: function () { - return this.getFieldViews(); - }, - - fetch: function () { - var data = {}; - - this.panelList.forEach(function (p) { - var panelView = this.getView(p.name); - if (!panelView.disabled && 'fetch' in panelView) { - data = _.extend(data, panelView.fetch()); - } - }, this); - return data; - }, - - filterActions: function (actions) { - var filtered = []; - actions.forEach(function (item) { - if (Espo.Utils.checkActionAccess(this.getAcl(), this.model, item)) { - filtered.push(item); - } - }, this); - return filtered; - }, - - showPanel: function (name, callback) { - this.recordHelper.setPanelStateParam(name, 'hidden', false); - - var isFound = false; - this.panelList.forEach(function (d) { - if (d.name == name) { - d.hidden = false; - isFound = true; - } - }, this); - if (!isFound) return; - - if (this.isRendered()) { - var view = this.getView(name); - if (view) { - view.$el.closest('.panel').removeClass('hidden'); - view.disabled = false; - view.trigger('show'); - } - if (callback) { - callback.call(this); - } - } else { - if (callback) { - this.once('after:render', function () { - callback.call(this); - }, this); - } - } - }, - - hidePanel: function (name, callback) { - this.recordHelper.setPanelStateParam(name, 'hidden', true); - - var isFound = false; - this.panelList.forEach(function (d) { - if (d.name == name) { - d.hidden = true; - isFound = true; - } - }, this); - if (!isFound) return; - - if (this.isRendered()) { - var view = this.getView(name); - if (view) { - view.$el.closest('.panel').addClass('hidden'); - view.disabled = true; - view.trigger('hide'); - } - if (callback) { - callback.call(this); - } - } else { - if (callback) { - this.once('after:render', function () { - callback.call(this); - }, this); - } - } - } - }); }); diff --git a/client/src/views/record/detail.js b/client/src/views/record/detail.js index b9fda94c9d..f6b8ceb4fd 100644 --- a/client/src/views/record/detail.js +++ b/client/src/views/record/detail.js @@ -992,12 +992,12 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'], var bottomView = this.getView('bottom'); if (bottomView && 'setNotReadOnly' in bottomView) { - bottomView.setNotReadOnly(); + bottomView.setNotReadOnly(onlyNotSetAsReadOnly); } var sideView = this.getView('side'); if (sideView && 'setNotReadOnly' in sideView) { - sideView.setNotReadOnly(); + sideView.setNotReadOnly(onlyNotSetAsReadOnly); } this.getFieldList().forEach(function (field) { diff --git a/client/src/views/record/panel-actions.js b/client/src/views/record/panel-actions.js new file mode 100644 index 0000000000..3de66664a3 --- /dev/null +++ b/client/src/views/record/panel-actions.js @@ -0,0 +1,69 @@ +/************************************************************************ + * This file is part of EspoCRM. + * + * EspoCRM - Open Source CRM application. + * Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko + * Website: http://www.espocrm.com + * + * EspoCRM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EspoCRM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EspoCRM. If not, see http://www.gnu.org/licenses/. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +define('views/record/panel-actions', 'view', function (Dep) { + + return Dep.extend({ + + template: 'record/panel-actions', + + data: function () { + return { + defs: this.options.defs, + buttonList: this.getButtonList(), + actionList: this.getActionList(), + entityType: this.options.entityType, + scope: this.options.scope + }; + }, + + setup: function () { + this.buttonList = this.options.defs.buttonList; + this.actionList = this.options.defs.actionList; + }, + + getButtonList: function () { + var list = []; + this.buttonList.forEach(function (item) { + if (item.hidden) return; + list.push(item); + }, this); + return list; + }, + + getActionList: function () { + var list = []; + this.actionList.forEach(function (item) { + if (item.hidden) return; + list.push(item); + }, this); + return list; + }, + + }); +}); diff --git a/client/src/views/record/panels-container.js b/client/src/views/record/panels-container.js new file mode 100644 index 0000000000..0945455d55 --- /dev/null +++ b/client/src/views/record/panels-container.js @@ -0,0 +1,238 @@ +/************************************************************************ + * This file is part of EspoCRM. + * + * EspoCRM - Open Source CRM application. + * Copyright (C) 2014-2018 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko + * Website: http://www.espocrm.com + * + * EspoCRM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EspoCRM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EspoCRM. If not, see http://www.gnu.org/licenses/. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +define('views/record/panels-container', 'view', function (Dep) { + + return Dep.extend({ + + data: function () { + return { + panelList: this.panelList, + scope: this.scope, + entityType: this.entityType + }; + }, + + events: { + 'click .action': function (e) { + var $target = $(e.currentTarget); + var action = $target.data('action'); + var panel = $target.data('panel'); + var data = $target.data(); + if (action) { + var method = 'action' + Espo.Utils.upperCaseFirst(action); + var d = _.clone(data); + delete d['action']; + delete d['panel']; + var view = this.getView(panel); + if (view && typeof view[method] == 'function') { + view[method].call(view, d, e); + } + } + } + }, + + setReadOnly: function () { + this.readOnly = true; + }, + + setNotReadOnly: function (onlyNotSetAsReadOnly) { + this.readOnly = false; + + if (onlyNotSetAsReadOnly) { + this.panelList.forEach(function (item) { + this.applyAccessToActions(item.buttonList); + this.applyAccessToActions(item.actionList); + + if (this.isRendered()) { + var actionsView = this.getView(item.actionsViewKey); + if (actionsView) { + actionsView.reRender(); + } + } + }, this); + } + }, + + applyAccessToActions: function (actionList) { + actionList.forEach(function (item) { + if (Espo.Utils.checkActionAccess(this.getAcl(), this.model, item, true)) { + if (item.isHiddenByAcl) { + item.isHiddenByAcl = false; + item.hidden = false; + } + } else { + if (!item.hidden) { + item.isHiddenByAcl = true; + item.hidden = true; + } + } + }, this); + }, + + setupPanelViews: function () { + this.panelList.forEach(function (p) { + + var name = p.name; + var options = { + model: this.model, + panelName: name, + el: this.options.el + ' .panel[data-name="' + name + '"] > .panel-body', + defs: p, + mode: this.mode, + recordHelper: this.recordHelper, + inlineEditDisabled: this.inlineEditDisabled, + readOnly: this.readOnly, + disabled: p.hidden || false, + recordViewObject: this.recordViewObject + }; + options = _.extend(options, p.options); + this.createView(name, p.view, options, function (view) { + if ('getActionList' in view) { + p.actionList = view.getActionList(); + this.applyAccessToActions(p.actionList); + } + if ('getButtonList' in view) { + p.buttonList = view.getButtonList(); + this.applyAccessToActions(p.buttonList); + } + + if (view.titleHtml) { + p.titleHtml = view.titleHtml; + } else { + if (p.label) { + p.title = this.translate(p.label, 'labels', this.scope); + } else { + p.title = view.title; + } + } + + this.createView(name + 'Actions', 'views/record/panel-actions', { + el: this.getSelector() + '.panel[data-name="'+p.name+'"] > .panel-heading > .panel-actions-container', + model: this.model, + defs: p, + scope: this.scope, + entityType: this.entityType + }); + }, this); + }, this); + }, + + setupPanels: function () {}, + + getFieldViews: function (withHidden) { + var fields = {}; + this.panelList.forEach(function (p) { + var panelView = this.getView(p.name); + if ((!panelView.disabled || withHidden) && 'getFieldViews' in panelView) { + fields = _.extend(fields, panelView.getFieldViews()); + } + }, this); + return fields; + }, + + getFields: function () { + return this.getFieldViews(); + }, + + fetch: function () { + var data = {}; + + this.panelList.forEach(function (p) { + var panelView = this.getView(p.name); + if (!panelView.disabled && 'fetch' in panelView) { + data = _.extend(data, panelView.fetch()); + } + }, this); + return data; + }, + + showPanel: function (name, callback) { + this.recordHelper.setPanelStateParam(name, 'hidden', false); + + var isFound = false; + this.panelList.forEach(function (d) { + if (d.name == name) { + d.hidden = false; + isFound = true; + } + }, this); + if (!isFound) return; + + if (this.isRendered()) { + var view = this.getView(name); + if (view) { + view.$el.closest('.panel').removeClass('hidden'); + view.disabled = false; + view.trigger('show'); + } + if (callback) { + callback.call(this); + } + } else { + if (callback) { + this.once('after:render', function () { + callback.call(this); + }, this); + } + } + }, + + hidePanel: function (name, callback) { + this.recordHelper.setPanelStateParam(name, 'hidden', true); + + var isFound = false; + this.panelList.forEach(function (d) { + if (d.name == name) { + d.hidden = true; + isFound = true; + } + }, this); + if (!isFound) return; + + if (this.isRendered()) { + var view = this.getView(name); + if (view) { + view.$el.closest('.panel').addClass('hidden'); + view.disabled = true; + view.trigger('hide'); + } + if (callback) { + callback.call(this); + } + } else { + if (callback) { + this.once('after:render', function () { + callback.call(this); + }, this); + } + } + } + + }); +}); diff --git a/client/src/views/record/panels/relationship.js b/client/src/views/record/panels/relationship.js index 14b6abe950..af6775254a 100644 --- a/client/src/views/record/panels/relationship.js +++ b/client/src/views/record/panels/relationship.js @@ -61,13 +61,11 @@ Espo.define('views/record/panels/relationship', ['views/record/panels/bottom', ' var url = this.url = this.url || this.model.name + '/' + this.model.id + '/' + this.link; - if (!this.readOnly && !this.defs.readOnly) { - if (!('create' in this.defs)) { - this.defs.create = true; - } - if (!('select' in this.defs)) { - this.defs.select = true; - } + if (!('create' in this.defs)) { + this.defs.create = true; + } + if (!('select' in this.defs)) { + this.defs.select = true; } if (!('view' in this.defs)) { @@ -98,8 +96,7 @@ Espo.define('views/record/panels/relationship', ['views/record/panels/bottom', ' title: 'Create', action: this.defs.createAction || 'createRelated', link: this.link, - acl: 'create', - aclScope: this.scope, + acl: 'edit', html: '', data: { link: this.link, @@ -129,8 +126,7 @@ Espo.define('views/record/panels/relationship', ['views/record/panels/bottom', ' label: 'Select', action: this.defs.selectAction || 'selectRelated', data: data, - acl: 'edit', - aclScope: this.model.name + acl: 'edit' }); }