diff --git a/application/Espo/Controllers/EntityManager.php b/application/Espo/Controllers/EntityManager.php new file mode 100644 index 0000000000..19af966a68 --- /dev/null +++ b/application/Espo/Controllers/EntityManager.php @@ -0,0 +1,65 @@ +getUser()->isAdmin()) { + throw new Forbidden(); + } + } + + public function actionCreateEntity($params, $data, $request) + { + if (!$request->isPost()) { + throw new BadRequest(); + } + + if (empty($data['name']) || empty($data['type'])) { + throw new BadRequest(); + } + + $name = $data['name']; + $type = $data['type']; + + $params = array(); + + if (!empty($data['labelSingular'])) { + $params['labelSingular'] = $data['labelSingular']; + } + if (!empty($data['labelPlural'])) { + $params['labelPlural'] = $data['labelPlural']; + } + if (!empty($data['stream'])) { + $params['stream'] = $data['stream']; + } + + return $this->getContainer()->get('entityManagerUtil')->create($name, $type, $params); + } +} + diff --git a/application/Espo/Core/Loaders/EntityManagerUtil.php b/application/Espo/Core/Loaders/EntityManagerUtil.php new file mode 100644 index 0000000000..e6ad8e9152 --- /dev/null +++ b/application/Espo/Core/Loaders/EntityManagerUtil.php @@ -0,0 +1,38 @@ +getContainer()->get('metadata'), + $this->getContainer()->get('language'), + $this->getContainer()->get('fileManager') + ); + + return $entityManager; + } +} + diff --git a/application/Espo/Core/Templates/Controllers/Base.php b/application/Espo/Core/Templates/Controllers/Base.php new file mode 100644 index 0000000000..1f06cf5f79 --- /dev/null +++ b/application/Espo/Core/Templates/Controllers/Base.php @@ -0,0 +1,30 @@ +metadata = $metadata; + $this->language = $language; + $this->fileManager = $fileManager; + + $this->metadataUtils = new \Espo\Core\Utils\Metadata\Utils($this->metadata); + } + + protected function getMetadata() + { + return $this->metadata; + } + + protected function getLanguage() + { + return $this->language; + } + + protected function getFileManager() + { + return $this->fileManager; + } + + protected function getMetadataUtils() + { + return $this->metadataUtils; + } + + public function read($name, $scope) + { + $fieldDef = $this->getFieldDef($name, $scope); + + $fieldDef['label'] = $this->getLanguage()->translate($name, 'fields', $scope); + + return $fieldDef; + } + + public function create($name, $type) + { + if ($this->getMetadata()->get('scope.' . $name)) { + throw new Conflict('Entity ['.$name.'] already exists.'); + } + + $contents = "<" . "?" . "php\n". + "namespace Espo\Custom\Entities;\n". + "class {$name} extends \Espo\Core\Templates\Entities\\{$type}\n". + "{\n". + "}\n"; + + $filePath = "custom/Espo/Custom/Entities/{$name}.php"; + $this->getFileManager()->putContents($filePath, $contents); + + $contents = "<" . "?" . "php\n". + "namespace Espo\Custom\Controllers;\n". + "class {$name} extends \Espo\Core\Templates\Controllers\\{$type}\n". + "{\n". + "}\n"; + $filePath = "custom/Espo/Custom/Controllers/{$name}.php"; + $this->getFileManager()->putContents($filePath, $contents); + + $contents = "<" . "?" . "php\n". + "namespace Espo\Custom\Services;\n". + "class {$name} extends \Espo\Core\Templates\Services\\{$type}\n". + "{\n". + "}\n"; + $filePath = "custom/Espo/Custom/Services/{$name}.php"; + $this->getFileManager()->putContents($filePath, $contents); + + $contents = "<" . "?" . "php\n". + "namespace Espo\Custom\Repositories;\n". + "class {$name} extends \Espo\Core\Templates\Repositories\\{$type}\n". + "{\n". + "}\n"; + + $filePath = "custom/Espo/Custom/Repositories/{$name}.php"; + $this->getFileManager()->putContents($filePath, $contents); + + $scopeData = array( + 'entity' => true, + 'layouts' => true, + 'tab' => true, + 'acl' => true, + 'module' => 'Custom', + 'isCustom' => true, + 'customizable' => true, + 'importable' => true, + 'type' => $type + ); + $this->getMetadata()->set($scopeData, 'scopes', $name); + + $filePath = "application/Espo/Core/Templates/Metadata/{$type}/entityDefs.json"; + $entityDefsData = Json::decode($this->getFileManager()->getContents($filePath), true); + $this->getMetadata()->set($entityDefsData, 'entityDefs', $name); + + $filePath = "application/Espo/Core/Templates/Metadata/{$type}/clientDefs.json"; + $clientDefsData = Json::decode($this->getFileManager()->getContents($filePath), true); + $this->getMetadata()->set($clientDefsData, 'clientDefs', $name); + } + + public function update($name, $fieldDef, $scope) + { + /*Add option to metadata to identify the custom field*/ + if (!$this->isCore($name, $scope)) { + $fieldDef['isCustom'] = true; + } + + $res = true; + if (isset($fieldDef['label'])) { + $this->setLabel($name, $fieldDef['label'], $scope); + } + + if (isset($fieldDef['type']) && $fieldDef['type'] == 'enum') { + if (isset($fieldDef['translatedOptions'])) { + $this->setTranslatedOptions($name, $fieldDef['translatedOptions'], $scope); + } + } + + if (isset($fieldDef['label']) || isset($fieldDef['translatedOptions'])) { + $res &= $this->getLanguage()->save(); + } + + if ($this->isDefsChanged($name, $fieldDef, $scope)) { + $res &= $this->setEntityDefs($name, $fieldDef, $scope); + } + + return (bool) $res; + } + + public function delete($name, $scope) + { + if ($this->isCore($name, $scope)) { + throw new Error('Cannot delete core field ['.$name.'] in '.$scope); + } + + $unsets = array( + 'fields.'.$name, + 'links.'.$name, + ); + + $res = $this->getMetadata()->delete($unsets, $this->metadataType, $scope); + $res &= $this->deleteLabel($name, $scope); + + return (bool) $res; + } + + protected function setEntityDefs($name, $fieldDef, $scope) + { + $fieldDef = $this->normalizeDefs($name, $fieldDef, $scope); + + $data = Json::encode($fieldDef); + $res = $this->getMetadata()->set($data, $this->metadataType, $scope); + + return $res; + } + + protected function setTranslatedOptions($name, $value, $scope) + { + return $this->getLanguage()->set($name, $value, 'options', $scope); + } + + protected function setLabel($name, $value, $scope) + { + return $this->getLanguage()->set($name, $value, 'fields', $scope); + } + + protected function deleteLabel($name, $scope) + { + $this->getLanguage()->delete($name, 'fields', $scope); + return $this->getLanguage()->save(); + } + + protected function getFieldDef($name, $scope) + { + return $this->getMetadata()->get($this->metadataType.'.'.$scope.'.fields.'.$name); + } + + protected function getLinkDef($name, $scope) + { + return $this->getMetadata()->get($this->metadataType.'.'.$scope.'.links.'.$name); + } + + /** + * Prepare input fieldDefs, remove unnecessary fields + * + * @param string $fieldName + * @param array $fieldDef + * @param string $scope + * @return array + */ + protected function prepareFieldDef($name, $fieldDef, $scope) + { + $unnecessaryFields = array( + 'name', + 'label', + ); + + foreach ($unnecessaryFields as $fieldName) { + if (isset($fieldDef[$fieldName])) { + unset($fieldDef[$fieldName]); + } + } + + if (isset($fieldDef['linkDefs'])) { + $linkDefs = $fieldDef['linkDefs']; + unset($fieldDef['linkDefs']); + } + + $currentOptionList = array_keys((array) $this->getFieldDef($name, $scope)); + foreach ($fieldDef as $defName => $defValue) { + if ( (!isset($defValue) || $defValue === '') && !in_array($defName, $currentOptionList) ) { + unset($fieldDef[$defName]); + } + } + + return $fieldDef; + } + + /** + * Add all needed block for a field defenition + * + * @param string $fieldName + * @param array $fieldDef + * @param string $scope + * @return array + */ + protected function normalizeDefs($fieldName, array $fieldDef, $scope) + { + $fieldDef = $this->prepareFieldDef($fieldName, $fieldDef, $scope); + + $metaFieldDef = $this->getMetadataUtils()->getFieldDefsInFieldMeta($fieldDef); + if (isset($metaFieldDef)) { + $fieldDef = Util::merge($metaFieldDef, $fieldDef); + } + + $defs = array( + 'fields' => array( + $fieldName => $fieldDef, + ), + ); + + /** Save links for a field. */ + $metaLinkDef = $this->getMetadataUtils()->getLinkDefsInFieldMeta($scope, $fieldDef); + if (isset($linkDefs) || isset($metaLinkDef)) { + $linkDefs = Util::merge((array) $metaLinkDef, (array) $linkDefs); + $defs['links'] = array( + $fieldName => $linkDefs, + ); + } + + return $defs; + } + + /** + * Check if changed metadata defenition for a field except 'label' + * + * @return boolean + */ + protected function isDefsChanged($name, $fieldDef, $scope) + { + $fieldDef = $this->prepareFieldDef($name, $fieldDef, $scope); + $currentFieldDef = $this->getFieldDef($name, $scope); + + $this->isChanged = Util::isEquals($fieldDef, $currentFieldDef) ? false : true; + + return $this->isChanged; + } + + /** + * Only for update method + * + * @return boolean + */ + public function isChanged() + { + return $this->isChanged; + } + + /** + * Check if a field is core field + * + * @param string $name + * @param string $scope + * @return boolean + */ + protected function isCore($name, $scope) + { + $existingField = $this->getFieldDef($name, $scope); + if (isset($existingField) && (!isset($existingField['isCustom']) || !$existingField['isCustom'])) { + return true; + } + + return false; + } + +} \ No newline at end of file diff --git a/application/Espo/Resources/i18n/en_US/Admin.json b/application/Espo/Resources/i18n/en_US/Admin.json index 4df9f83b0a..7c4ce7fdce 100644 --- a/application/Espo/Resources/i18n/en_US/Admin.json +++ b/application/Espo/Resources/i18n/en_US/Admin.json @@ -9,6 +9,8 @@ "Customization": "Customization", "Available Fields": "Available Fields", "Layout": "Layout", + "Entity Manager": "Entity Manager", + "Relationship Manager": "Relationship Manager", "Add Panel": "Add Panel", "Add Field": "Add Field", "Settings": "Settings", @@ -40,7 +42,9 @@ "Install": "Install", "Ready for installation": "Ready for installation", "Uninstalling...": "Uninstalling...", - "Uninstalled": "Uninstalled" + "Uninstalled": "Uninstalled", + "Create Entity": "Create Entity", + "Edit Entity": "Edit Entity" }, "layouts": { "list": "List", @@ -128,6 +132,8 @@ "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.", + "relationshipManager": "Manage relationships between entities.", "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 new file mode 100644 index 0000000000..4da335dffe --- /dev/null +++ b/application/Espo/Resources/i18n/en_US/EntityManager.json @@ -0,0 +1,24 @@ +{ + "labels": { + "Fields": "Fields", + "Relationships": "Relationships" + }, + "fields": { + "systemName": "System Name", + "name": "Name", + "type": "Type", + "labelSingular": "Label Singular", + "labelPlural": "Label Plural", + "stream": "Stream" + }, + "options": { + "type": { + "": "None", + "Base": "Base", + "Person": "Person" + } + }, + "messages": { + "entityCreated": "Entity has been created" + } +} diff --git a/application/Espo/Resources/metadata/app/adminPanel.json b/application/Espo/Resources/metadata/app/adminPanel.json index 3214279871..5204469291 100644 --- a/application/Espo/Resources/metadata/app/adminPanel.json +++ b/application/Espo/Resources/metadata/app/adminPanel.json @@ -107,6 +107,16 @@ "label":"Field Manager", "description":"fieldManager" }, + { + "url":"#Admin/entityManager", + "label":"Entity Manager", + "description":"entityManager" + }, + { + "url":"#Admin/relationshipManager", + "label":"Relationship Manager", + "description":"relationshipManager" + }, { "url":"#Admin/userInterface", "label":"User Interface", diff --git a/frontend/client/res/templates/admin/entity-manager/index.tpl b/frontend/client/res/templates/admin/entity-manager/index.tpl new file mode 100644 index 0000000000..7944c33041 --- /dev/null +++ b/frontend/client/res/templates/admin/entity-manager/index.tpl @@ -0,0 +1,46 @@ + + +
+ +
+ + + + + + + +{{#each scopeDataList}} + + + + + + + + +{{/each}} +
{{translate 'systemName' scope='EntityManager' category='fields'}}{{translate 'name' scope='EntityManager' category='fields'}}{{translate 'type' scope='EntityManager' category='fields'}}
+ + {{name}} + + + {{translate name category='scopeNames'}} + + {{#if type}} + {{translateOption type field='type' scope='EntityManager'}} + {{/if}} + + {{#if customizable}} + {{translate 'Fields' scope='EntityManager'}} + {{/if}} + + {{#if customizable}} + {{translate 'Relationships' scope='EntityManager'}} + {{/if}} + + {{#if isCustom}} + {{translate 'Remove'}} + {{/if}} +
+ diff --git a/frontend/client/res/templates/admin/entity-manager/modals/edit-entity.tpl b/frontend/client/res/templates/admin/entity-manager/modals/edit-entity.tpl new file mode 100644 index 0000000000..bce983b19e --- /dev/null +++ b/frontend/client/res/templates/admin/entity-manager/modals/edit-entity.tpl @@ -0,0 +1,36 @@ +
+ +
+ {{{type}}} +
+
+ +
+ +
+ {{{name}}} +
+
+ +
+ +
+ {{{labelSingular}}} +
+
+ +
+ +
+ {{{labelPlural}}} +
+
+ +{{#if stream}} +
+ +
+ {{{stream}}} +
+
+{{/if}} diff --git a/frontend/client/src/controllers/admin.js b/frontend/client/src/controllers/admin.js index c455ac86a4..cb54462693 100644 --- a/frontend/client/src/controllers/admin.js +++ b/frontend/client/src/controllers/admin.js @@ -47,6 +47,19 @@ Espo.define('Controllers.Admin', 'Controller', function (Dep) { this.main('Admin.FieldManager.Index', {scope: scope, field: field}); }, + entityManager: function (options) { + var scope = options.scope || null; + + this.main('Admin.EntityManager.Index', {scope: scope}); + }, + + relationshipManager: function (options) { + var scope = options.scope || null; + var link = options.link || null; + + this.main('Admin.RelationshipManager.Index', {scope: scope, link: link}); + }, + upgrade: function (options) { this.main('Admin.Upgrade.Index'); }, @@ -71,7 +84,7 @@ Espo.define('Controllers.Admin', 'Controller', function (Dep) { }, }); }, this); - model.fetch(); + model.fetch(); }, outboundEmail: function () { diff --git a/frontend/client/src/views/admin/entity-manager/index.js b/frontend/client/src/views/admin/entity-manager/index.js new file mode 100644 index 0000000000..5103c5df59 --- /dev/null +++ b/frontend/client/src/views/admin/entity-manager/index.js @@ -0,0 +1,139 @@ +/************************************************************************ + * 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.EntityManager.Index', 'View', function (Dep) { + + return Dep.extend({ + + template: 'admin.entity-manager.index', + + scopeDataList: null, + + scope: null, + + type: null, + + data: function () { + return { + scopeDataList: this.scopeDataList, + scope: this.scope, + }; + }, + + events: { + 'click a[data-action="editEntity"]': function (e) { + var scope = $(e.currentTarget).data('scope'); + this.editEntity(scope); + }, + 'click button[data-action="createEntity"]': function (e) { + this.editEntity(); + }, + 'click [data-action="removeEntity"]': function (e) { + var scope = $(e.currentTarget).data('scope'); + if (confirm(this.translate('confirmation', 'messages'))) { + this.removeEntity(scope); + } + } + }, + + setup: function () { + this.scopeDataList = []; + + var scopeList = Object.keys(this.getMetadata().get('scopes')).sort(function (v1, v2) { + return this.translate(v1, 'scopeNames').localeCompare(this.translate(v2, 'scopeNames')); + }.bind(this)); + + scopeList.forEach(function (scope) { + var d = this.getMetadata().get('scopes.' + scope); + if (d.entity) { + this.scopeDataList.push({ + name: scope, + isCustom: d.isCustom, + customizable: d.customizable, + type: d.type + }); + + } + }, this); + + this.scope = this.options.scope || null; + + this.on('after:render', function () { + this.renderHeader(); + if (!this.scope) { + this.renderDefaultPage(); + } else { + if (!this.field) { + this.openScope(this.scope); + } else { + this.openField(this.scope, this.field); + } + } + }); + }, + + createEntity: function () { + this.createView('edit', 'Admin.EntityManager.Modals.EditEntity', {}, function (view) { + view.render(); + }); + }, + + editEntity: function (scope) { + this.createView('edit', 'Admin.EntityManager.Modals.EditEntity', { + scope: scope + }, function (view) { + view.render(); + }); + }, + + removeEntity: function (scope) { + $.ajax({ + url: 'EntityManager/action/removeEntity', + type: 'POST', + data: JSON.stringify({ + scope: scope + }) + }).done(function () { + this.$el.find('table tr[data-scope="'+scope+'"]').remove(); + }.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/entity-manager/modals/edit-entity.js b/frontend/client/src/views/admin/entity-manager/modals/edit-entity.js new file mode 100644 index 0000000000..056c9071ac --- /dev/null +++ b/frontend/client/src/views/admin/entity-manager/modals/edit-entity.js @@ -0,0 +1,195 @@ +/************************************************************************ + * 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.EntityManager.Modals.EditEntity', 'Views.Modal', function (Dep) { + + return Dep.extend({ + + cssName: 'edit-entity', + + template: 'admin.entity-manager.modals.edit-entity', + + 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 + } + } + }); + }, + + 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'; + } + + $.ajax({ + url: url, + type: 'POST', + data: JSON.stringify({ + name: this.model.get('name'), + labelSingular: this.model.get('labelSingular'), + labelPlural: this.model.get('labelPlural'), + type: this.model.get('type') + }), + 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')); + } + this.trigger('saved'); + this.close(); + }.bind(this)); + }, + + }); +}); + diff --git a/frontend/client/src/views/admin/field-manager/index.js b/frontend/client/src/views/admin/field-manager/index.js index 6564356603..252b9c1bfb 100644 --- a/frontend/client/src/views/admin/field-manager/index.js +++ b/frontend/client/src/views/admin/field-manager/index.js @@ -17,7 +17,7 @@ * * 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.FieldManager.Index', 'View', function (Dep) { @@ -43,16 +43,16 @@ Espo.define('Views.Admin.FieldManager.Index', 'View', function (Dep) { var scope = $(e.currentTarget).data('scope'); this.openScope(scope); }, - + 'click #fields-content a.field-link': function (e) { var scope = $(e.currentTarget).data('scope'); var field = $(e.currentTarget).data('field'); this.openField(scope, field); }, - + 'click a[data-action="addField"]': function (e) { var scope = $(e.currentTarget).data('scope'); - var type = $(e.currentTarget).data('type'); + var type = $(e.currentTarget).data('type'); this.createField(scope, type); }, }, @@ -68,15 +68,15 @@ Espo.define('Views.Admin.FieldManager.Index', 'View', function (Dep) { if (this.getMetadata().get('scopes.' + scope + '.entity')) { if (this.getMetadata().get('scopes.' + scope + '.customizable')) { this.scopeList.push(scope); - } + } } }.bind(this)); this.scope = this.options.scope || null; - this.field = this.options.field || null; - - this.on('after:render', function () { - this.renderFieldsHeader(); + this.field = this.options.field || null; + + this.on('after:render', function () { + this.renderFieldsHeader(); if (!this.scope) { this.renderDefaultPage(); } else { @@ -85,35 +85,35 @@ Espo.define('Views.Admin.FieldManager.Index', 'View', function (Dep) { } else { this.openField(this.scope, this.field); } - } - }); + } + }); }, openScope: function (scope) { this.scope = scope; this.field = null; - + this.getRouter().navigate('#Admin/fieldManager/scope=' + scope, {trigger: false}); - - this.notify('Loading...'); + + this.notify('Loading...'); this.createView('content', 'Admin.FieldManager.List', { el: '#fields-content', scope: scope, }, function (view) { this.renderFieldsHeader(); view.render(); - this.notify(false); + this.notify(false); $(window).scrollTop(0); }.bind(this)); }, - + openField: function (scope, field) { this.scope = scope; this.field = field - + this.getRouter().navigate('#Admin/fieldManager/scope=' + scope + '&field=' + field, {trigger: false}); - - this.notify('Loading...'); + + this.notify('Loading...'); this.createView('content', 'Admin.FieldManager.Edit', { el: '#fields-content', scope: scope, @@ -125,14 +125,14 @@ Espo.define('Views.Admin.FieldManager.Index', 'View', function (Dep) { $(window).scrollTop(0); }.bind(this)); }, - + createField: function (scope, type) { this.scope = scope; this.type = type; - + this.getRouter().navigate('#Admin/fieldManager/scope=' + scope + '&type=' + type + '&create=true', {trigger: false}); - - this.notify('Loading...'); + + this.notify('Loading...'); this.createView('content', 'Admin.FieldManager.Edit', { el: '#fields-content', scope: scope, @@ -155,7 +155,7 @@ Espo.define('Views.Admin.FieldManager.Index', 'View', function (Dep) { $('#fields-header').html(''); return; } - + if (this.field) { $('#fields-header').show().html('' + this.getLanguage().translate(this.scope, 'scopeNamesPlural') + ' ยป ' + this.field); } else { @@ -164,7 +164,7 @@ Espo.define('Views.Admin.FieldManager.Index', 'View', function (Dep) { }, updatePageTitle: function () { - this.setPageTitle(this.getLanguage().translate('Field Manager')); + this.setPageTitle(this.getLanguage().translate('Field Manager', 'labels', 'Admin')); }, }); }); diff --git a/frontend/client/src/views/admin/layouts/index.js b/frontend/client/src/views/admin/layouts/index.js index 5a5c3771e8..bc6973092f 100644 --- a/frontend/client/src/views/admin/layouts/index.js +++ b/frontend/client/src/views/admin/layouts/index.js @@ -17,7 +17,7 @@ * * 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.Layouts.Index', 'View', function (Dep) { @@ -28,7 +28,7 @@ Espo.define('Views.Admin.Layouts.Index', 'View', function (Dep) { scopeList: null, typeList: ['list', 'detail', 'listSmall', 'detailSmall', 'filters', 'massUpdate', 'relationships'], - + additionalLayouts: { 'Opportunity': ['detailConvert'], 'Contact': ['detailConvert'], @@ -53,9 +53,9 @@ Espo.define('Views.Admin.Layouts.Index', 'View', function (Dep) { (this.additionalLayouts[scope] || []).forEach(function (item) { d.typeList.push(item); }); - + dataList.push(d); - }, this); + }, this); return dataList; }).call(this) }; @@ -77,7 +77,7 @@ Espo.define('Views.Admin.Layouts.Index', 'View', function (Dep) { }, setup: function () { - this.scopeList = []; + this.scopeList = []; var scopesAll = Object.keys(this.getMetadata().get('scopes')).sort(function (v1, v2) { return this.translate(v1, 'scopeNamesPlural').localeCompare(this.translate(v2, 'scopeNamesPlural')); @@ -88,7 +88,7 @@ Espo.define('Views.Admin.Layouts.Index', 'View', function (Dep) { this.getMetadata().get('scopes.' + scope + '.layouts')) { this.scopeList.push(scope); } - }.bind(this)); + }.bind(this)); this.on('after:render', function () { $("#layouts-menu button[data-scope='" + this.options.scope + "'][data-type='" + this.options.type + "']").addClass('disabled'); @@ -139,7 +139,7 @@ Espo.define('Views.Admin.Layouts.Index', 'View', function (Dep) { }, updatePageTitle: function () { - this.setPageTitle(this.getLanguage().translate('Layout Manager')); + this.setPageTitle(this.getLanguage().translate('Layout Manager', 'labels', 'Admin')); }, }); });