From 9b846b45bc343fdf8a94eca0b87ae64b4381e508 Mon Sep 17 00:00:00 2001 From: yuri Date: Tue, 27 Jan 2015 16:21:22 +0200 Subject: [PATCH] link manager dev --- .../Espo/Resources/i18n/en_US/Admin.json | 7 +- .../Resources/i18n/en_US/EntityManager.json | 5 + .../Resources/metadata/app/adminPanel.json | 5 - .../templates/admin/entity-manager/index.tpl | 8 +- .../templates/admin/field-manager/edit.tpl | 5 +- .../templates/admin/field-manager/index.tpl | 23 +- .../templates/admin/link-manager/index.tpl | 56 +++++ .../admin/link-manager/modals/edit.tpl | 38 +++ frontend/client/src/controllers/admin.js | 5 +- .../src/views/admin/entity-manager/index.js | 21 +- .../src/views/admin/field-manager/index.js | 18 -- .../src/views/admin/link-manager/index.js | 177 ++++++++++++++ .../views/admin/link-manager/modals/edit.js | 226 ++++++++++++++++++ 13 files changed, 530 insertions(+), 64 deletions(-) create mode 100644 frontend/client/res/templates/admin/link-manager/index.tpl create mode 100644 frontend/client/res/templates/admin/link-manager/modals/edit.tpl create mode 100644 frontend/client/src/views/admin/link-manager/index.js create mode 100644 frontend/client/src/views/admin/link-manager/modals/edit.js diff --git a/application/Espo/Resources/i18n/en_US/Admin.json b/application/Espo/Resources/i18n/en_US/Admin.json index 0bd4479ca9..a7fedfa1f0 100644 --- a/application/Espo/Resources/i18n/en_US/Admin.json +++ b/application/Espo/Resources/i18n/en_US/Admin.json @@ -24,7 +24,6 @@ "Email Templates": "Email Templates", "Import": "Import", "Layout Manager": "Layout Manager", - "Field Manager": "Field Manager", "User Interface": "User Interface", "Auth Tokens": "Auth Tokens", "Authentication": "Authentication", @@ -43,7 +42,8 @@ "Uninstalling...": "Uninstalling...", "Uninstalled": "Uninstalled", "Create Entity": "Create Entity", - "Edit Entity": "Edit Entity" + "Edit Entity": "Edit Entity", + "Create Link": "Create Link" }, "layouts": { "list": "List", @@ -130,8 +130,7 @@ "emailTemplates": "Templates for outbound emails.", "import": "Import data from CSV file.", "layoutManager": "Customize layouts (list, detail, edit, search, mass update).", - "fieldManager": "Create new fields or customize existing ones.", - "entityManager": "Create custom entities. Edit existing ones.", + "entityManager": "Create custom entities, edit existing ones. Manage field and relationships.", "userInterface": "Configure UI.", "authTokens": "Active auth sessions. IP address and last access date.", "authentication": "Authentication settings.", diff --git a/application/Espo/Resources/i18n/en_US/EntityManager.json b/application/Espo/Resources/i18n/en_US/EntityManager.json index 18d12c239e..ef29c51c87 100644 --- a/application/Espo/Resources/i18n/en_US/EntityManager.json +++ b/application/Espo/Resources/i18n/en_US/EntityManager.json @@ -16,6 +16,11 @@ "": "None", "Base": "Base", "Person": "Person" + }, + "linkType": { + "manyToMany": "Many-to-Many", + "oneToMany": "One-to-Many", + "manyToOne": "Many-to-One" } }, "messages": { diff --git a/application/Espo/Resources/metadata/app/adminPanel.json b/application/Espo/Resources/metadata/app/adminPanel.json index c9bab5db8e..3c079a5427 100644 --- a/application/Espo/Resources/metadata/app/adminPanel.json +++ b/application/Espo/Resources/metadata/app/adminPanel.json @@ -107,11 +107,6 @@ "label":"Layout Manager", "description":"layoutManager" }, - { - "url":"#Admin/fieldManager", - "label":"Field Manager", - "description":"fieldManager" - }, { "url":"#Admin/entityManager", "label":"Entity Manager", diff --git a/frontend/client/res/templates/admin/entity-manager/index.tpl b/frontend/client/res/templates/admin/entity-manager/index.tpl index 75d862b80d..07d901a3e0 100644 --- a/frontend/client/res/templates/admin/entity-manager/index.tpl +++ b/frontend/client/res/templates/admin/entity-manager/index.tpl @@ -4,11 +4,15 @@ - +
+ + + + {{#each scopeDataList}} @@ -30,7 +34,7 @@
{{translate 'name' scope='EntityManager' category='fields'}} {{translate 'label' scope='EntityManager' category='fields'}} {{translate 'type' scope='EntityManager' category='fields'}}    
{{#if customizable}} - {{translate 'Relationships' scope='EntityManager'}} + {{translate 'Relationships' scope='EntityManager'}} {{/if}} diff --git a/frontend/client/res/templates/admin/field-manager/edit.tpl b/frontend/client/res/templates/admin/field-manager/edit.tpl index 35a3bcac5b..516ab6ea6f 100644 --- a/frontend/client/res/templates/admin/field-manager/edit.tpl +++ b/frontend/client/res/templates/admin/field-manager/edit.tpl @@ -5,6 +5,8 @@
+
+
{{translate type scope='Admin' category='fieldTypes'}}
@@ -17,7 +19,6 @@
{{{label}}}
- {{#each params}} {{#unless hidden}}
@@ -27,4 +28,6 @@ {{/unless}} {{/each}}
+
+
diff --git a/frontend/client/res/templates/admin/field-manager/index.tpl b/frontend/client/res/templates/admin/field-manager/index.tpl index 0b315febaa..b32606926c 100644 --- a/frontend/client/res/templates/admin/field-manager/index.tpl +++ b/frontend/client/res/templates/admin/field-manager/index.tpl @@ -1,19 +1,20 @@ - +
-
- -
-
-

{{{content}}} -
+
diff --git a/frontend/client/res/templates/admin/link-manager/index.tpl b/frontend/client/res/templates/admin/link-manager/index.tpl new file mode 100644 index 0000000000..e5a57bdb17 --- /dev/null +++ b/frontend/client/res/templates/admin/link-manager/index.tpl @@ -0,0 +1,56 @@ + + +
+ +
+ + + + {{#each linkDataList}} + + + + + + + + + + {{/each}} +
+ {{translate entity category='scopeNames'}} + + {{nameForeign}} + + + {{translateOption type field='linkType' scope='EntityManager'}} + + + {{name}} + + {{translate foreignEntity category='scopeNames'}} + + {{#if isCustom}} + + {{translate 'Edit'}} + + {{/if}} + + {{#if isCustom}} + + {{translate 'Remove'}} + + {{/if}} +
+ + diff --git a/frontend/client/res/templates/admin/link-manager/modals/edit.tpl b/frontend/client/res/templates/admin/link-manager/modals/edit.tpl new file mode 100644 index 0000000000..f8b9d5a3b0 --- /dev/null +++ b/frontend/client/res/templates/admin/link-manager/modals/edit.tpl @@ -0,0 +1,38 @@ +
+
+ +
+ {{{name}}} +
+
+
+ +
+ {{{type}}} +
+
+
+
+
+ +
+ {{{labelSingular}}} +
+
+
+ +
+ {{{labelPlural}}} +
+
+
+
+ {{#if stream}} +
+ +
+ {{{stream}}} +
+
+ {{/if}} +
diff --git a/frontend/client/src/controllers/admin.js b/frontend/client/src/controllers/admin.js index cb54462693..1b6c870165 100644 --- a/frontend/client/src/controllers/admin.js +++ b/frontend/client/src/controllers/admin.js @@ -53,11 +53,10 @@ Espo.define('Controllers.Admin', 'Controller', function (Dep) { this.main('Admin.EntityManager.Index', {scope: scope}); }, - relationshipManager: function (options) { + linkManager: function (options) { var scope = options.scope || null; - var link = options.link || null; - this.main('Admin.RelationshipManager.Index', {scope: scope, link: link}); + this.main('Admin.LinkManager.Index', {scope: scope}); }, upgrade: function (options) { diff --git a/frontend/client/src/views/admin/entity-manager/index.js b/frontend/client/src/views/admin/entity-manager/index.js index a9d6f86081..53f69961ad 100644 --- a/frontend/client/src/views/admin/entity-manager/index.js +++ b/frontend/client/src/views/admin/entity-manager/index.js @@ -29,8 +29,6 @@ Espo.define('Views.Admin.EntityManager.Index', 'View', function (Dep) { scope: null, - type: null, - data: function () { return { scopeDataList: this.scopeDataList, @@ -44,7 +42,7 @@ Espo.define('Views.Admin.EntityManager.Index', 'View', function (Dep) { this.editEntity(scope); }, 'click button[data-action="createEntity"]': function (e) { - this.editEntity(); + this.createEntity(); }, 'click [data-action="removeEntity"]': function (e) { var scope = $(e.currentTarget).data('scope'); @@ -81,9 +79,6 @@ Espo.define('Views.Admin.EntityManager.Index', 'View', function (Dep) { this.setupScopeData(); - this.on('after:render', function () { - this.renderHeader(); - }); }, createEntity: function () { @@ -128,20 +123,6 @@ Espo.define('Views.Admin.EntityManager.Index', 'View', function (Dep) { }.bind(this)); }, - renderDefaultPage: function () { - $('#fields-header').html('').hide(); - $('#fields-content').html(this.translate('selectEntityType', 'messages', 'Admin')); - }, - - renderHeader: function () { - if (!this.scope) { - $('#scope-header').html(''); - return; - } - - $('#scope-header').show().html(this.getLanguage().translate(this.scope, 'scopeNames')); - }, - updatePageTitle: function () { this.setPageTitle(this.getLanguage().translate('Entity Manager', 'labels', 'Admin')); }, diff --git a/frontend/client/src/views/admin/field-manager/index.js b/frontend/client/src/views/admin/field-manager/index.js index 252b9c1bfb..8e389228db 100644 --- a/frontend/client/src/views/admin/field-manager/index.js +++ b/frontend/client/src/views/admin/field-manager/index.js @@ -76,7 +76,6 @@ Espo.define('Views.Admin.FieldManager.Index', 'View', function (Dep) { this.field = this.options.field || null; this.on('after:render', function () { - this.renderFieldsHeader(); if (!this.scope) { this.renderDefaultPage(); } else { @@ -100,7 +99,6 @@ Espo.define('Views.Admin.FieldManager.Index', 'View', function (Dep) { el: '#fields-content', scope: scope, }, function (view) { - this.renderFieldsHeader(); view.render(); this.notify(false); $(window).scrollTop(0); @@ -119,7 +117,6 @@ Espo.define('Views.Admin.FieldManager.Index', 'View', function (Dep) { scope: scope, field: field, }, function (view) { - this.renderFieldsHeader(); view.render(); this.notify(false); $(window).scrollTop(0); @@ -138,7 +135,6 @@ Espo.define('Views.Admin.FieldManager.Index', 'View', function (Dep) { scope: scope, type: type, }, function (view) { - this.renderFieldsHeader(); view.render(); this.notify(false); $(window).scrollTop(0); @@ -146,23 +142,9 @@ Espo.define('Views.Admin.FieldManager.Index', 'View', function (Dep) { }, renderDefaultPage: function () { - $('#fields-header').html('').hide(); $('#fields-content').html(this.translate('selectEntityType', 'messages', 'Admin')); }, - renderFieldsHeader: function () { - if (!this.scope) { - $('#fields-header').html(''); - return; - } - - if (this.field) { - $('#fields-header').show().html('' + this.getLanguage().translate(this.scope, 'scopeNamesPlural') + ' ยป ' + this.field); - } else { - $('#fields-header').show().html(this.getLanguage().translate(this.scope, 'scopeNamesPlural')); - } - }, - updatePageTitle: function () { this.setPageTitle(this.getLanguage().translate('Field Manager', 'labels', 'Admin')); }, diff --git a/frontend/client/src/views/admin/link-manager/index.js b/frontend/client/src/views/admin/link-manager/index.js new file mode 100644 index 0000000000..2e5f93445e --- /dev/null +++ b/frontend/client/src/views/admin/link-manager/index.js @@ -0,0 +1,177 @@ +/************************************************************************ + * This file is part of EspoCRM. + * + * EspoCRM - Open Source CRM application. + * Copyright (C) 2014-2015 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/. + ************************************************************************/ + +Espo.define('Views.Admin.LinkManager.Index', 'View', function (Dep) { + + return Dep.extend({ + + template: 'admin.link-manager.index', + + scopeDataList: null, + + scope: null, + + data: function () { + return { + linkDataList: this.linkDataList, + scope: this.scope, + }; + }, + + events: { + 'click a[data-action="editLink"]': function (e) { + var link = $(e.currentTarget).data('link'); + this.editLink(link); + }, + 'click button[data-action="createLink"]': function (e) { + this.createLink(); + }, + 'click [data-action="removeLink"]': function (e) { + var link = $(e.currentTarget).data('link'); + if (confirm(this.translate('confirmation', 'messages'))) { + this.removeLink(link); + } + } + }, + + setupLinkData: function () { + this.linkDataList = []; + + var links = this.getMetadata().get('entityDefs.' + this.scope + '.links'); + var linkList = Object.keys(links).sort(function (v1, v2) { + return v1.localeCompare(v2); + }.bind(this)); + + linkList.forEach(function (name) { + var d = links[name]; + + if (!d.foreign) return; + if (!d.entity) return; + + var foreignType = this.getMetadata().get('entityDefs.' + d.entity + '.links.' + d.foreign + '.type'); + + var type; + + if (d.type == 'hasMany') { + if (foreignType == 'hasMany') { + type = 'manyToMany'; + } else if (foreignType == 'belongsTo') { + type = 'oneToMany'; + } else { + return; + } + } else if (d.type == 'belongsTo') { + if (foreignType == 'hasMany') { + type = 'manyToOne'; + } else { + return; + } + } else { + return; + } + + this.linkDataList.push({ + name: name, + isCustom: d.isCustom, + customizable: d.customizable, + type: type, + foreignEntity: d.entity, + entity: this.scope, + nameForeign: d.foreign, + label: this.getLanguage().translate(name, 'links', this.scope), + labelForeign: this.getLanguage().translate(d.foreign, 'links', d.entity) + }); + + }, this); + }, + + setup: function () { + this.scope = this.options.scope || null; + + this.setupLinkData(); + + this.on('after:render', function () { + this.renderHeader(); + }); + }, + + createLink: function () { + this.createView('edit', 'Admin.LinkManager.Modals.Edit', { + scope: this.scope + }, function (view) { + view.render(); + + this.listenTo(view, 'after:save', function () { + this.setupLinkData(); + this.render(); + }, this); + }.bind(this)); + }, + + editLink: function (link) { + this.createView('edit', 'Admin.LinkManager.Modals.Edit', { + scope: this.scope, + link: link + }, function (view) { + view.render(); + + this.listenTo(view, 'after:save', function () { + this.setupLinkData(); + this.render(); + }, this); + }.bind(this)); + }, + + removeEntity: function (link) { + $.ajax({ + url: 'EntityManager/action/removeLink', + type: 'POST', + data: JSON.stringify({ + scope: this.scope, + link: link + }) + }).done(function () { + this.$el.find('table tr[data-link="'+link+'"]').remove(); + this.getMetadata().load(function () { + this.getConfig().load(function () { + this.setupLinkData(); + this.render(); + }.bind(this)); + }.bind(this)); + }.bind(this)); + }, + + renderHeader: function () { + if (!this.scope) { + $('#scope-header').html(''); + return; + } + + $('#scope-header').show().html(this.getLanguage().translate(this.scope, 'scopeNames')); + }, + + updatePageTitle: function () { + this.setPageTitle(this.getLanguage().translate('Entity Manager', 'labels', 'Admin')); + }, + }); +}); + + diff --git a/frontend/client/src/views/admin/link-manager/modals/edit.js b/frontend/client/src/views/admin/link-manager/modals/edit.js new file mode 100644 index 0000000000..7e51238cd2 --- /dev/null +++ b/frontend/client/src/views/admin/link-manager/modals/edit.js @@ -0,0 +1,226 @@ +/************************************************************************ + * This file is part of EspoCRM. + * + * EspoCRM - Open Source CRM application. + * Copyright (C) 2014-2015 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/. + ************************************************************************/ + +Espo.define('Views.Admin.LinkManager.Modals.Edit', 'Views.Modal', function (Dep) { + + return Dep.extend({ + + cssName: 'edit', + + template: 'admin.link-manager.modals.edit', + + setup: function () { + + this.buttons = [ + { + name: 'save', + label: 'Save', + style: 'danger', + onClick: function (dialog) { + this.save(); + }.bind(this) + }, + { + name: 'cancel', + label: 'Cancel', + onClick: function (dialog) { + dialog.close(); + } + } + ]; + + var scope = this.scope = this.options.scope || false; + + var header = 'Create Entity'; + if (scope) { + header = 'Edit Entity'; + } + + this.header = this.translate(header, 'labels', 'Admin'); + + var model = this.model = new Espo.Model(); + model.name = 'EntityManager'; + + this.hasStreamField = true; + if (scope) { + this.hasStreamField = this.getMetadata().get('scopes.' + scope + '.customizable') || false; + } + + if (scope) { + this.model.set('name', scope); + this.model.set('labelSingular', this.translate(scope, 'scopeNames')); + this.model.set('labelPlural', this.translate(scope, 'scopeNamesPlural')); + this.model.set('type', this.getMetadata().get('scopes.' + scope + '.type') || ''); + this.model.set('stream', this.getMetadata().get('scopes.' + scope + '.stream') || false); + } + + this.createView('type', 'Fields.Enum', { + model: model, + mode: 'edit', + el: this.options.el + ' .field-type', + defs: { + name: 'type', + params: { + required: true, + options: ['Base', 'Person'] + } + }, + readOnly: scope != false + }); + + if (this.hasStreamField) { + this.createView('stream', 'Fields.Bool', { + model: model, + mode: 'edit', + el: this.options.el + ' .field-stream', + defs: { + name: 'stream' + } + }); + } + + this.createView('name', 'Fields.Varchar', { + model: model, + mode: 'edit', + el: this.options.el + ' .field-name', + defs: { + name: 'name', + params: { + required: true + } + }, + readOnly: scope != false + }); + this.createView('labelSingular', 'Fields.Varchar', { + model: model, + mode: 'edit', + el: this.options.el + ' .field-labelSingular', + defs: { + name: 'labelSingular', + params: { + required: true + } + } + }); + this.createView('labelPlural', 'Fields.Varchar', { + model: model, + mode: 'edit', + el: this.options.el + ' .field-labelPlural', + defs: { + name: 'labelPlural', + params: { + required: true + } + } + }); + }, + + afterRender: function () { + this.getView('name').on('change', function (m) { + var name = this.model.get('name'); + + name = name.charAt(0).toUpperCase() + name.slice(1); + + this.model.set('labelSingular', name); + this.model.set('labelPlural', name + 's') ; + if (name) { + name = name.replace(/\-/g, ' ').replace(/_/g, ' ').replace(/[^\w\s]/gi, '').replace(/ (.)/g, function (match, g) { + return g.toUpperCase(); + }).replace(' ', ''); + if (name.length) { + name = name.charAt(0).toUpperCase() + name.slice(1); + } + } + this.model.set('name', name); + }, this); + }, + + save: function () { + var arr = [ + 'name', + 'type', + 'labelSingular', + 'labelPlural', + 'stream' + ]; + + var notValid = false; + + arr.forEach(function (item) { + if (!this.hasView(item)) return; + if (this.getView(item).mode != 'edit') return; + this.getView(item).fetchToModel(); + }, this); + + arr.forEach(function (item) { + if (!this.hasView(item)) return; + if (this.getView(item).mode != 'edit') return; + notValid = this.getView(item).validate() || notValid; + }, this); + + if (notValid) { + return; + } + + this.$el.find('button[data-name="save"]').addClass('disabled'); + + var url = 'EntityManager/action/createEntity'; + if (this.scope) { + url = 'EntityManager/action/updateEntity'; + } + + var name = this.model.get('name'); + + $.ajax({ + url: url, + type: 'POST', + data: JSON.stringify({ + name: name, + labelSingular: this.model.get('labelSingular'), + labelPlural: this.model.get('labelPlural'), + type: this.model.get('type'), + stream: this.model.get('stream') + }), + error: function () { + this.$el.find('button[data-name="save"]').removeClass('disabled'); + }.bind(this) + }).done(function () { + if (this.scope) { + Espo.Ui.success(this.translate('Saved')); + } else { + Espo.Ui.success(this.translate('entityCreated', 'messages', 'EntityManager')); + } + var global = ((this.getLanguage().data || {}) || {}).Global; + (global.scopeNames || {})[name] = this.model.get('labelSingular'); + (global.scopeNamesPlural || {})[name] = this.model.get('labelPlural'); + + this.getMetadata().load(function () { + this.getConfig().load(function () { + this.trigger('after:save'); + this.close(); + }.bind(this)); + }.bind(this)); + }.bind(this)); + }, + + }); +}); +