entity manager page

This commit is contained in:
Yuri Kuznetsov
2020-11-05 14:12:57 +02:00
parent 4e1e6bf776
commit 2d385c163f
12 changed files with 375 additions and 75 deletions

View File

@@ -64,6 +64,7 @@
}
},
"messages": {
"confirmRemove": "Are you sure you want to remove the entity type from the system?",
"entityCreated": "Entity has been created",
"linkAlreadyExists": "Link name conflict.",
"linkConflict": "Name conflict: link or field with the same name already exists."

View File

@@ -3,73 +3,43 @@
{{translate 'Entity Manager' scope='Admin'}}</h3></div>
<div class="button-container">
<button class="btn btn-primary" data-action="createEntity">{{translate 'Create Entity' scope='Admin'}}</button>
<button class="btn btn-default" data-action="createEntity">
<span class="fas fa-plus"></span>
{{translate 'Create Entity' scope='Admin'}}
</button>
</div>
<div class="row">
<div class="col-md-9">
<table class="table table-hover">
<thead>
<tr>
<th>{{translate 'name' scope='EntityManager' category='fields'}}</th>
<th>{{translate 'label' scope='EntityManager' category='fields'}}</th>
<th>{{translate 'type' scope='EntityManager' category='fields'}}</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{{#each scopeDataList}}
<tr data-scope="{{name}}">
<td width="25%">
<td>
{{#if customizable}}
<a href="#Admin/entityManager/scope={{name}}">{{name}}</a>
{{else}}
{{name}}
{{/if}}
</td>
<td width="">
<td width="33%">
{{label}}
</td>
<td width="120">
<td width="30%">
{{#if type}}
{{translateOption type field='type' scope='EntityManager'}}
{{/if}}
</td>
<td width="120">
{{#if customizable}}
<a href="#Admin/fieldManager/scope={{name}}">{{translate 'Fields' scope='EntityManager'}}</a>
{{/if}}
</td>
<td width="120">
{{#if customizable}}
<a href="#Admin/linkManager/scope={{name}}">{{translate 'Relationships' scope='EntityManager'}}</a>
{{/if}}
</td>
<td align="right" width="120">
{{#if customizable}}
<a href="javascript:" data-action="editEntity" data-scope="{{name}}" title="{{translate 'Edit'}}">
{{translate 'Edit'}}
</a>
{{/if}}
</td>
<td class="cell" align="right" width="120" data-name="buttons">
{{#if customizable}}
<div class="list-row-buttons btn-group pull-right">
<button type="button" class="btn btn-link btn-sm dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right">
{{#if layouts}}
<li>
<a href="#Admin/layouts/scope={{name}}&em=true" data-scope="{{name}}">{{translate 'Layouts' scope='EntityManager'}}</a>
</li>
{{/if}}
<li><a href="javascript:" data-action="editFormula" data-scope="{{name}}">{{translate 'Formula' scope='EntityManager'}}</a></li>
{{#if isRemovable}}
<li><a href="javascript:" data-action="removeEntity" data-scope="{{name}}">{{translate 'Remove'}}</a></li>
{{/if}}
</ul>
</div>
{{/if}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,113 @@
<div class="page-header">
<h3><a href="#Admin">{{translate 'Administration'}}</a>
<span class="breadcrumb-separator"><span class="chevron-right"></span></span>
<a href="#Admin/entityManager">{{translate 'Entity Manager' scope='Admin'}}</a>
<span class="breadcrumb-separator"><span class="chevron-right"></span></span>
{{scope}}
</h3>
</div>
<div class="button-container">
<div class="btn-group actions-btn-group" role="group">
<button class="btn btn-default action" data-action="editEntity">
<span class="icon fas fa-cog"></span>
{{translate 'Edit'}}
</button>
{{#if isRemovable}}
<button class="btn btn-default dropdown-toggle item-dropdown-button" data-toggle="dropdown">
<span class="fas fa-ellipsis-h"></span>
</button>
<ul class="dropdown-menu pull-left">
<li><a href="javascript:" data-action="removeEntity">{{translate 'Remove'}}</a></li>
</ul>
{{/if}}
</div>
</div>
<div class="record">
<div class="record-grid">
<div class="left">
<div class="panel panel-default">
<div class="panel-body panel-body-form">
<div class="row">
<div class="cell col-sm-6 form-group">
<label class="control-label">{{translate 'name' scope='EntityManager' category='fields'}}</label>
<div class="field">
{{scope}}
</div>
</div>
{{#if type}}
<div class="cell col-sm-6 form-group">
<label class="control-label">{{translate 'type' scope='EntityManager' category='fields'}}</label>
<div class="field">
{{type}}
</div>
</div>
{{/if}}
</div>
<div class="row">
<div class="cell col-sm-6 form-group">
<label class="control-label">{{translate 'label' scope='EntityManager' category='fields'}}</label>
<div class="field">
{{label}}
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-body panel-body-form">
<div class="row">
<div class="cell col-sm-4 form-group">
{{#if isCustomizable}}
<div class="">
<a class="btn btn-default btn-lg action btn-full-wide"
href="#Admin/fieldManager/scope={{scope}}">
<span class="fas fa-asterisk"></span>
{{translate 'Fields' scope='EntityManager'}}
</a>
</div>
{{/if}}
</div>
<div class="cell col-sm-4 form-group">
{{#if isCustomizable}}
<div class="">
<a class="btn btn-default btn-lg action btn-full-wide"
href="#Admin/linkManager/scope={{scope}}">
<span class="fas fa-link"></span>
{{translate 'Relationships' scope='EntityManager'}}
</a>
</div>
{{/if}}
</div>
<div class="cell col-sm-4 form-group">
{{#if hasLayouts}}
<div class="">
<a class="btn btn-default btn-lg action btn-full-wide"
href="#Admin/layouts/scope={{scope}}&em=true">
<span class="fas fa-table"></span>
{{translate 'Layouts' scope='EntityManager'}}
</a>
</div>
{{/if}}
</div>
</div>
<div class="row">
<div class="cell col-sm-4 form-group">
{{#if isCustomizable}}
<div class="">
<a class="btn btn-default btn-lg action btn-full-wide"
href="javascript:" data-action="editFormula">
<span class="fas fa-code"></span>
{{translate 'Formula' scope='EntityManager'}}
</a>
</div>
{{/if}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -4,7 +4,7 @@
<span class="breadcrumb-separator"><span class="chevron-right"></span></span>
<a href="#Admin/entityManager">{{translate 'Entity Manager' scope='Admin'}}</a>
<span class="breadcrumb-separator"><span class="chevron-right"></span></span>
{{translate scope category='scopeNames'}}
<a href="#Admin/entityManager/scope={{scope}}">{{translate scope category='scopeNames'}}</a>
<span class="breadcrumb-separator"><span class="chevron-right"></span></span>
{{translate 'Fields' scope='EntityManager'}}
</h3>

View File

@@ -4,14 +4,17 @@
<span class="breadcrumb-separator"><span class="chevron-right"></span></span>
<a href="#Admin/entityManager">{{translate 'Entity Manager' scope='Admin'}}</a>
<span class="breadcrumb-separator"><span class="chevron-right"></span></span>
{{translate scope category='scopeNames'}}
<a href="#Admin/entityManager/scope={{scope}}">{{translate scope category='scopeNames'}}</a>
<span class="breadcrumb-separator"><span class="chevron-right"></span></span>
{{translate 'Relationships' scope='EntityManager'}}
</h3>
</div>
<div class="button-container">
<button class="btn btn-primary" data-action="createLink">{{translate 'Create Link' scope='Admin'}}</button>
<button class="btn btn-default" data-action="createLink">
<span class="fas fa-plus"></span>
{{translate 'Create Link' scope='Admin'}}
</button>
</div>

View File

@@ -87,7 +87,13 @@ define('controllers/admin', ['controller', 'search-manager'], function (Dep, Sea
actionEntityManager: function (options) {
var scope = options.scope || null;
this.main('views/admin/entity-manager/index', {scope: scope});
if (scope) {
this.main('views/admin/entity-manager/scope', {scope: scope});
return;
}
this.main('views/admin/entity-manager/index');
},
actionLinkManager: function (options) {

View File

@@ -39,28 +39,27 @@ define('views/admin/entity-manager/index', 'view', function (Dep) {
data: function () {
return {
scopeDataList: this.scopeDataList,
scope: this.scope,
};
},
events: {
'click a[data-action="editEntity"]': function (e) {
/*'click a[data-action="editEntity"]': function (e) {
var scope = $(e.currentTarget).data('scope');
this.editEntity(scope);
},
'click [data-action="editFormula"]': function (e) {
var scope = $(e.currentTarget).data('scope');
this.editFormula(scope);
},
},*/
'click button[data-action="createEntity"]': function (e) {
this.createEntity();
},
'click [data-action="removeEntity"]': function (e) {
/*'click [data-action="removeEntity"]': function (e) {
var scope = $(e.currentTarget).data('scope');
this.confirm(this.translate('confirmation', 'messages'), function () {
this.removeEntity(scope);
}, this);
}
}*/
},
setupScopeData: function () {
@@ -78,6 +77,7 @@ define('views/admin/entity-manager/index', 'view', function (Dep) {
scopeListSorted.push(scope);
}
}, this);
scopeList.forEach(function (scope) {
var d = this.getMetadata().get('scopes.' + scope);
if (d.entity && !d.customizable) {
@@ -109,8 +109,6 @@ define('views/admin/entity-manager/index', 'view', function (Dep) {
},
setup: function () {
this.scope = this.options.scope || null;
this.setupScopeData();
},
@@ -118,10 +116,9 @@ define('views/admin/entity-manager/index', 'view', function (Dep) {
this.createView('edit', 'views/admin/entity-manager/modals/edit-entity', {}, function (view) {
view.render();
this.listenTo(view, 'after:save', function () {
this.listenTo(view, 'after:save', function (o) {
this.clearView('edit');
this.setupScopeData();
this.render();
this.getRouter().navigate('#Admin/entityManager/scope=' + o.scope, {trigger: true});
}, this);
this.listenTo(view, 'close', function () {
@@ -130,7 +127,7 @@ define('views/admin/entity-manager/index', 'view', function (Dep) {
}, this);
},
editEntity: function (scope) {
/*editEntity: function (scope) {
this.createView('edit', 'views/admin/entity-manager/modals/edit-entity', {
scope: scope,
}, function (view) {
@@ -154,7 +151,7 @@ define('views/admin/entity-manager/index', 'view', function (Dep) {
{
name: 'close',
label: this.translate('Close'),
}
},
],
}, function (view) {
view.render();
@@ -166,14 +163,14 @@ define('views/admin/entity-manager/index', 'view', function (Dep) {
this.clearView('edit');
}, this);
}, this);
},
},*/
removeEntity: function (scope) {
/*removeEntity: function (scope) {
$.ajax({
url: 'EntityManager/action/removeEntity',
type: 'POST',
data: JSON.stringify({
name: scope
name: scope,
})
}).done(function () {
this.$el.find('table tr[data-scope="'+scope+'"]').remove();
@@ -184,11 +181,11 @@ define('views/admin/entity-manager/index', 'view', function (Dep) {
}.bind(this), true);
}.bind(this), true);
}.bind(this));
},
},*/
editFormula: function (scope) {
/*editFormula: function (scope) {
this.createView('edit', 'views/admin/entity-manager/modals/edit-formula', {
scope: scope
scope: scope,
}, function (view) {
view.render();
@@ -200,12 +197,10 @@ define('views/admin/entity-manager/index', 'view', function (Dep) {
this.clearView('edit');
}, this);
}, this);
},
},*/
updatePageTitle: function () {
this.setPageTitle(this.getLanguage().translate('Entity Manager', 'labels', 'Admin'));
},
});
});

View File

@@ -485,7 +485,7 @@ define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'model']
'stream',
'disabled',
'statusField',
'iconClass'
'iconClass',
];
if (this.scope) {
@@ -608,7 +608,8 @@ define('views/admin/entity-manager/modals/edit-entity', ['views/modal', 'model']
function () {
var rebuildRequired = data.fullTextSearch && !fetchedAttributes.fullTextSearch;
var o = {
rebuildRequired: rebuildRequired
rebuildRequired: rebuildRequired,
scope: name,
};
this.trigger('after:save', o);
}.bind(this)

View File

@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/entity-manager/modals/edit-formula', ['views/modal', 'model'], function (Dep, Model) {
define('views/admin/entity-manager/modals/edit-formula', ['views/modal', 'model'], function (Dep, Model) {
return Dep.extend({

View File

@@ -0,0 +1,203 @@
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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/admin/entity-manager/scope', 'view', function (Dep) {
return Dep.extend({
template: 'admin/entity-manager/scope',
scope: null,
data: function () {
return {
scope: this.scope,
isRemovable: this.isRemovable,
isCustomizable: this.isCustomizable,
type: this.type,
hasLayouts: this.hasLayouts,
label: this.label,
};
},
events: {
'click [data-action="editEntity"]': function (e) {
this.editEntity();
},
'click [data-action="editFormula"]': function (e) {
this.editFormula();
},
'click [data-action="removeEntity"]': function (e) {
this.removeEntity();
},
},
setup: function () {
this.scope = this.options.scope;
this.setupScopeData();
},
setupScopeData: function () {
var scopeData = this.getMetadata().get(['scopes', this.scope]);
if (!scopeData) {
throw new Espo.Exceptions.NotFound();
}
this.isRemovable = !!scopeData.isCustom;
if (scopeData.isNotRemovable) {
this.isRemovable = false;
}
this.isCustomizable = !!scopeData.customizable;
this.type = scopeData.type;
this.hasLayouts = scopeData.layouts;
this.label = this.getLanguage().translate(this.scope, 'scopeNames');
},
editEntity: function () {
this.createView('edit', 'views/admin/entity-manager/modals/edit-entity', {
scope: this.scope,
}, function (view) {
view.render();
this.listenTo(view, 'after:save', function (o) {
this.clearView('edit');
this.setupScopeData();
this.reRender();
if (o.rebuildRequired) {
this.createView('dialog', 'views/modal', {
templateContent:
"{{complexText viewObject.options.msg}}" +
"{{complexText viewObject.options.msgRebuild}}",
headerText: this.translate('rebuildRequired', 'strings', 'Admin'),
backdrop: 'static',
msg: this.translate('rebuildRequired', 'messages', 'Admin'),
msgRebuild: '```php rebuild.php```',
buttonList: [
{
name: 'close',
label: this.translate('Close'),
},
],
}, function (view) {
view.render();
});
}
}, this);
this.listenTo(view, 'close', function () {
this.clearView('edit');
}, this);
}, this);
},
removeEntity: function () {
var scope = this.scope;
this.confirm(this.translate('confirmRemove', 'messages', 'EntityManager'), function () {
Espo.Ui.notify(
this.translate('pleaseWait', 'messages')
);
this.disableButtons();
Espo.Ajax.postRequest('EntityManager/action/removeEntity', {
name: scope,
})
.then(
function () {
this.getMetadata().load(function () {
this.getConfig().load(function () {
Espo.Ui.notify(false);
this.getRouter().navigate('#Admin/entityManager', {trigger: true});
}.bind(this), true);
}.bind(this), true);
}.bind(this)
)
.fail(
function () {
this.enableButtons();
}.bind(this)
);
}.bind(this));
},
afterRender: function () {
},
editFormula: function (scope) {
var scope = this.scope;
this.createView('edit', 'views/admin/entity-manager/modals/edit-formula', {
scope: scope,
}, function (view) {
view.render();
this.listenTo(view, 'after:save', function () {
this.clearView('edit');
}, this);
this.listenTo(view, 'close', function () {
this.clearView('edit');
}, this);
}, this);
},
updatePageTitle: function () {
this.setPageTitle(
this.getLanguage().translate('Entity Manager', 'labels', 'Admin')
);
},
disableButtons: function () {
this.$el.find('.btn.action').addClass('disabled').attr('disabled', 'disabled');
this.$el.find('.item-dropdown-button').addClass('disabled').attr('disabled', 'disabled');
},
enableButtons: function () {
this.$el.find('.btn.action').removeClass('disabled').removeAttr('disabled');
this.$el.find('.item-dropdown-button"]').removeClass('disabled').removeAttr('disabled');
},
});
});

View File

@@ -258,8 +258,10 @@ define('views/admin/layouts/index', 'view', function (Dep) {
html += "<a href=\"#Admin/entityManager\">" + this.translate('Entity Manager', 'labels', 'Admin') + "</a>";
if (this.scope) {
html += ' ' + separatorHtml + ' ' + this.translate(this.scope, 'scopeNames') +
' ' + separatorHtml + ' ' + this.translate('Layouts', 'labels', 'EntityManager');
html += ' ' + separatorHtml + ' ' +
"<a href=\"#Admin/entityManager/scope=" + this.scope + "\">" +
this.translate(this.scope, 'scopeNames') + '</a>' +
' ' + separatorHtml + ' ' + this.translate('Layouts', 'labels', 'EntityManager');
}
} else {
html += this.translate('Layout Manager', 'labels', 'Admin');

View File

@@ -97,6 +97,12 @@
width: 100px;
}
.btn.btn-full-wide {
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
}
.btn-default > .fa,
.btn-default > .fas {
color: @gray-soft;