link manager dev

This commit is contained in:
yuri
2015-01-28 16:38:27 +02:00
parent fbf665fd76
commit b1e184c6d1
7 changed files with 449 additions and 53 deletions
@@ -131,5 +131,97 @@ class EntityManager extends \Espo\Core\Controllers\Base
return true;
}
public function actionCreateLink($params, $data, $request)
{
if (!$request->isPost()) {
throw new BadRequest();
}
$paramList = [
'entity',
'entityForeign',
'link',
'linkForeign',
'label',
'labelForeign',
'linkType'
];
$d = array();
foreach ($paramList as $item) {
if (empty($data[$item])) {
throw new BadRequest();
}
$d[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
}
$result = $this->getContainer()->get('entityManagerUtil')->createLink($d);
if ($result) {
$this->getContainer()->get('dataManager')->rebuild();
} else {
throw new Error();
}
return true;
}
public function actionUpdateLink($params, $data, $request)
{
if (!$request->isPost()) {
throw new BadRequest();
}
$paramList = [
'entity',
'entityForeign',
'link',
'linkForeign',
'label',
'labelForeign'
];
$d = array();
foreach ($paramList as $item) {
$d[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
}
$result = $this->getContainer()->get('entityManagerUtil')->updateLink($d);
if ($result) {
$this->getContainer()->get('dataManager')->clearCache();
} else {
throw new Error();
}
return true;
}
public function actionRemoveLink($params, $data, $request)
{
if (!$request->isPost()) {
throw new BadRequest();
}
$paramList = [
'entity',
'link',
];
$d = array();
foreach ($paramList as $item) {
$d[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
}
$result = $this->getContainer()->get('entityManagerUtil')->deleteLink($d);
if ($result) {
$this->getContainer()->get('dataManager')->clearCache();
} else {
throw new Error();
}
return true;
}
}
@@ -221,4 +221,191 @@ class EntityManager
{
return $this->getMetadata()->get('scopes.' . $name . '.isCustom');
}
public function createLink(array $params)
{
$linkType = $params['linkType'];
$entity = $params['entity'];
$link = $params['link'];
$entityForeign = $params['entityForeign'];
$linkForeign = $params['linkForeign'];
$label = $params['label'];
$labelForeign = $params['labelForeign'];
if (empty($linkType)) {
throw new Error();
}
if (empty($entity) || empty($entityForeign)) {
throw new Error();
}
if (empty($entityForeign) || empty($linkForeign)) {
throw new Error();
}
if ($this->getMetadata()->get('entityDefs.' . $entity . '.links.' . $link)) {
throw new Conflict('Link ['.$entity.'::'.$link.'] already exists.');
}
if ($this->getMetadata()->get('entityDefs.' . $entityForeign . '.links.' . $linkForeign)) {
throw new Conflict('Link ['.$entityForeign.'::'.$linkForeign.'] already exists.');
}
switch ($linkType) {
case 'oneToMany':
if ($this->getMetadata()->get('entityDefs.' . $entityForeign . '.field.' . $linkForeign)) {
throw new Conflict('Field ['.$entityForeign.'::'.$linkForeign.'] already exists.');
}
$dataLeft = array(
'links' => array(
$link => array(
'type' => 'hasMany',
'foreign' => $linkForeign,
'entity' => $entityForeign,
'isCustom' => true
)
)
);
$dataRight = array(
'fields' => array(
$linkForeign => array(
'type' => 'link'
)
),
'links' => array(
$linkForeign => array(
'type' => 'belongsTo',
'foreign' => $link,
'entity' => $entity,
'isCustom' => true
)
)
);
break;
case 'manyToOne':
if ($this->getMetadata()->get('entityDefs.' . $entity . '.field.' . $link)) {
throw new Conflict('Field ['.$entity.'::'.$link.'] already exists.');
}
$dataLeft = array(
'fields' => array(
$link => array(
'type' => 'link'
)
),
'links' => array(
$link => array(
'type' => 'belongsTo',
'foreign' => $linkForeign,
'entity' => $entityForeign,
'isCustom' => true
)
)
);
$dataRight = array(
'links' => array(
$linkForeign => array(
'type' => 'hasMany',
'foreign' => $link,
'entity' => $entity,
'isCustom' => true
)
)
);
break;
case 'manyToMany':
$dataLeft = array(
'links' => array(
$link => array(
'type' => 'hasMany',
'foreign' => $linkForeign,
'entity' => $entityForeign,
'isCustom' => true
)
)
);
$dataRight = array(
'links' => array(
$linkForeign => array(
'type' => 'hasMany',
'foreign' => $link,
'entity' => $entity,
'isCustom' => true
)
)
);
break;
}
$this->getMetadata()->set('entityDefs', $entity, $dataLeft);
$this->getMetadata()->set('entityDefs', $entityForeign, $dataRight);
$this->getMetadata()->save();
$this->getLanguage()->set($entity, 'fields', $link, $label);
$this->getLanguage()->set($entity, 'links', $link, $label);
$this->getLanguage()->set($entityForeign, 'fields', $linkForeign, $labelForeign);
$this->getLanguage()->set($entityForeign, 'links', $linkForeign, $labelForeign);
$this->getLanguage()->save();
return true;
}
public function updateLink(array $params)
{
$entity = $params['entity'];
$link = $params['link'];
$entityForeign = $params['entityForeign'];
$linkForeign = $params['linkForeign'];
$label = $params['label'];
$labelForeign = $params['labelForeign'];
if (empty($entity) || empty($entityForeign)) {
throw new Error();
}
if (empty($entityForeign) || empty($linkForeign)) {
throw new Error();
}
$this->getLanguage()->set($entity, 'fields', $link, $label);
$this->getLanguage()->set($entity, 'links', $link, $label);
$this->getLanguage()->set($entityForeign, 'fields', $linkForeign, $labelForeign);
$this->getLanguage()->set($entityForeign, 'links', $linkForeign, $labelForeign);
$this->getLanguage()->save();
return true;
}
public function deleteLink(array $params)
{
$entity = $params['entity'];
$link = $params['link'];
if (!$this->getMetadata()->get("entityDefs.{$entity}.links.{$link}.isCustom")) {
throw new Error();
}
$entityForeign = $this->getMetadata()->get("entityDefs.{$entity}.links.{$link}.entity");
$linkForeign = $this->getMetadata()->get("entityDefs.{$entity}.links.{$link}.foreign");
if (empty($entity) || empty($entityForeign)) {
throw new Error();
}
if (empty($entityForeign) || empty($linkForeign)) {
throw new Error();
}
$this->getMetadata()->delete('entityDefs', $entity, array(
'fields.' . $link,
'links.' . $link
));
$this->getMetadata()->delete('entityDefs', $entityForeign, array(
'fields.' . $linkForeign,
'links.' . $linkForeign
));
$this->getMetadata()->save();
return true;
}
}
@@ -25,10 +25,13 @@
"linkType": {
"manyToMany": "Many-to-Many",
"oneToMany": "One-to-Many",
"manyToOne": "Many-to-One"
"manyToOne": "Many-to-One",
"parentToChildren": "Parent-to-Children",
"childrenToParent": "Children-to-Parent"
}
},
"messages": {
"entityCreated": "Entity has been created"
"entityCreated": "Entity has been created",
"linkAlreadyExists": "Conflict: link already exists."
}
}
@@ -16,8 +16,15 @@
<table class="table">
{{#unless linkDataList.length}}
<tr>
<td>
{{translate 'No Data'}}
</td>
</tr>
{{/unless}}
{{#each linkDataList}}
<tr data-scope="{{name}}">
<tr data-link="{{link}}">
<td width="15%" align="left">
{{translate entity category='scopeNames'}}
</td>
@@ -36,15 +43,13 @@
{{translate entityForeign category='scopeNames'}}
</td>
<td align="right" width="10%">
{{#if isCustom}}
<a href="javascript:" data-action="editLink" data-link="{{name}}">
<a href="javascript:" data-action="editLink" data-link="{{link}}">
{{translate 'Edit'}}
</a>
{{/if}}
</td>
<td align="right" width="10%">
{{#if isCustom}}
<a href="javascript:" data-action="removeLink" data-link="{{name}}">
<a href="javascript:" data-action="removeLink" data-link="{{link}}">
{{translate 'Remove'}}
</a>
{{/if}}
+7
View File
@@ -114,6 +114,13 @@
return Espo.Utils.convert(string, 'c-h').split('.').join('-');
},
lowerCaseFirst: function (string) {
if (string == null) {
return string;
}
return string.charAt(0).toLowerCase() + string.slice(1);
},
upperCaseFirst: function (string) {
if (string == null) {
return string;
@@ -52,6 +52,34 @@ Espo.define('Views.Admin.LinkManager.Index', 'View', function (Dep) {
}
},
computeRelationshipType: function (type, foreignType) {
if (type == 'hasMany') {
if (foreignType == 'hasMany') {
return 'manyToMany';
} else if (foreignType == 'belongsTo') {
return 'oneToMany';
} else {
return;
}
} else if (type == 'belongsTo') {
if (foreignType == 'hasMany') {
return 'manyToOne';
} else {
return;
}
} else if (type == 'belongsToParent') {
if (foreignType == 'hasChildren') {
return 'childrenToParent'
}
return;
} else if (type == 'hasChildren') {
if (foreignType == 'belongsToParent') {
return 'parentToChildren'
}
return;
}
},
setupLinkData: function () {
this.linkDataList = [];
@@ -68,25 +96,9 @@ Espo.define('Views.Admin.LinkManager.Index', 'View', function (Dep) {
var foreignType = this.getMetadata().get('entityDefs.' + d.entity + '.links.' + d.foreign + '.type');
var type;
var type = this.computeRelationshipType(d.type, foreignType);
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;
}
if (!type) return;
this.linkDataList.push({
link: link,
@@ -140,12 +152,12 @@ Espo.define('Views.Admin.LinkManager.Index', 'View', function (Dep) {
}.bind(this));
},
removeEntity: function (link) {
removeLink: function (link) {
$.ajax({
url: 'EntityManager/action/removeLink',
type: 'POST',
data: JSON.stringify({
scope: this.scope,
entity: this.scope,
link: link
})
}).done(function () {
@@ -19,7 +19,7 @@
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
Espo.define('Views.Admin.LinkManager.Modals.Edit', 'Views.Modal', function (Dep) {
Espo.define('Views.Admin.LinkManager.Modals.Edit', ['Views.Modal', 'Views.Admin.LinkManager.Index'], function (Dep, Index) {
return Dep.extend({
@@ -50,11 +50,13 @@ Espo.define('Views.Admin.LinkManager.Modals.Edit', 'Views.Modal', function (Dep)
var scope = this.scope = this.options.scope;
var link = this.link = this.options.link || false;
var entity = scope;
var isNew = this.isNew = (false == link);
var header = 'Create Link';
if (link) {
header = 'Edit Entity';
if (!isNew) {
header = 'Edit Link';
}
this.header = this.translate(header, 'labels', 'Admin');
@@ -62,11 +64,28 @@ Espo.define('Views.Admin.LinkManager.Modals.Edit', 'Views.Modal', function (Dep)
var model = this.model = new Espo.Model();
model.name = 'EntityManager';
this.model.set('entity', scope);
if (scope) {
this.model.set('entity', scope);
if (!isNew) {
var entityForeign = this.getMetadata().get('entityDefs.' + scope + '.links.' + link + '.entity');
var linkForeign = this.getMetadata().get('entityDefs.' + scope + '.links.' + link + '.foreign');
var label = this.getLanguage().translate(link, 'links', scope);
var labelForeign = this.getLanguage().translate(linkForeign, 'links', entityForeign);
var type = this.getMetadata().get('entityDefs.' + entity + '.links.' + link + '.type');
var foreignType = this.getMetadata().get('entityDefs.' + entityForeign + '.links.' + linkForeign + '.type');
var linkType = Index.prototype.computeRelationshipType.call(this, type, foreignType);
this.model.set('linkType', linkType);
this.model.set('entityForeign', entityForeign);
this.model.set('link', link);
this.model.set('linkForeign', linkForeign);
this.model.set('label', label);
this.model.set('labelForeign', labelForeign);
}
var scopes = this.getMetadata().get('scopes') || null;
var entityList = (Object.keys(scopes) || []).filter(function (item) {
var d = scopes[item];
@@ -77,6 +96,8 @@ Espo.define('Views.Admin.LinkManager.Modals.Edit', 'Views.Modal', function (Dep)
return t1.localeCompare(t2);
}.bind(this));
entityList.unshift('');
this.createView('entity', 'Fields.Varchar', {
model: model,
@@ -109,7 +130,7 @@ Espo.define('Views.Admin.LinkManager.Modals.Edit', 'Views.Modal', function (Dep)
name: 'linkType',
params: {
required: true,
options: ['', 'oneToMany', 'ManyToOne', 'ManyToMany']
options: ['', 'oneToMany', 'manyToOne', 'manyToMany']
}
},
readOnly: !isNew
@@ -163,22 +184,66 @@ Espo.define('Views.Admin.LinkManager.Modals.Edit', 'Views.Modal', function (Dep)
});
},
toPlural: function (string) {
if (string.slice(-1) == 'y') {
return string.substr(0, string.length - 1) + 'ies';
} else {
return string + 's';
}
},
populateFields: function () {
var entityForeign = this.model.get('entityForeign');
var linkType = this.model.get('linkType');
name = entityForeign.charAt(0).toUpperCase() + name.slice(1);
if (!entityForeign || !linkType) {
this.model.set('link', '');
this.model.set('linkForeign', '');
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) {
this.model.set('label', '');
this.model.set('labelForeign', '');
return;
}
var link;
var linkForeign;
switch (linkType) {
case 'oneToMany':
linkForeign = Espo.Utils.lowerCaseFirst(this.scope);
link = this.toPlural(Espo.Utils.lowerCaseFirst(entityForeign))
break;
case 'manyToOne':
linkForeign = this.toPlural(Espo.Utils.lowerCaseFirst(this.scope));
link = Espo.Utils.lowerCaseFirst(entityForeign);
break;
case 'manyToMany':
linkForeign = this.toPlural(Espo.Utils.lowerCaseFirst(this.scope));
link = this.toPlural(Espo.Utils.lowerCaseFirst(entityForeign));
break;
}
this.model.set('link', link);
this.model.set('linkForeign', linkForeign);
this.model.set('label', Espo.Utils.upperCaseFirst(link));
this.model.set('labelForeign', Espo.Utils.upperCaseFirst(linkForeign));
return;
},
handleLinkChange: function (field) {
var value = this.model.get(field);
if (value) {
value = value.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);
if (value.length) {
value = Espo.Utils.lowerCaseFirst(value);
}
}
this.model.set('name', name);
this.model.set(field, value);
},
afterRender: function () {
@@ -188,6 +253,13 @@ Espo.define('Views.Admin.LinkManager.Modals.Edit', 'Views.Modal', function (Dep)
this.getView('entityForeign').on('change', function (m) {
this.populateFields();
}, this);
this.getView('link').on('change', function (m) {
this.handleLinkChange('link');
}, this);
this.getView('linkForeign').on('change', function (m) {
this.handleLinkChange('linkForeign');
}, this);
},
save: function () {
@@ -221,23 +293,34 @@ Espo.define('Views.Admin.LinkManager.Modals.Edit', 'Views.Modal', function (Dep)
this.$el.find('button[data-name="save"]').addClass('disabled');
var url = 'EntityManager/action/createLink';
if (this.scope) {
if (!this.isNew) {
url = 'EntityManager/action/updateLink';
}
var entity = this.scope;
var entityForeign = this.model.get('entityForeign');
var link = this.model.get('link');
var linkForeign = this.model.get('linkForeign');
var label = this.model.get('label');
var labelForeign = this.model.get('labelForeign');
$.ajax({
url: url,
type: 'POST',
data: JSON.stringify({
scope: this.scope,
entityForeign: this.model.get('entityForeign'),
link: this.model.get('link'),
linkForeign: this.model.get('linkForeign'),
labelSingular: this.model.get('label'),
labelPlural: this.model.get('labelForeign'),
linkType: this.model.get('linkType'),
entity: entity,
entityForeign: entityForeign,
link: link,
linkForeign: linkForeign,
label: label,
labelForeign: labelForeign,
linkType: this.model.get('linkType')
}),
error: function () {
error: function (x) {
if (x.status == 409) {
Espo.Ui.error(this.translate('linkAlreadyExists', 'messages', 'EntityManager'));
x.errorIsHandled = true;
}
this.$el.find('button[data-name="save"]').removeClass('disabled');
}.bind(this)
}).done(function () {
@@ -246,9 +329,16 @@ Espo.define('Views.Admin.LinkManager.Modals.Edit', 'Views.Modal', function (Dep)
} else {
Espo.Ui.success(this.translate('Created'));
}
/*var global = ((this.getLanguage().data || {}) || {}).Global;
(global.scopeNames || {})[name] = this.model.get('labelSingular');
(global.scopeNamesPlural || {})[name] = this.model.get('labelPlural');*/
var data;
data = ((this.getLanguage().data || {}) || {})[entity];
(data.fields || {})[link] = label;
(data.links || {})[link] = label;
data = ((this.getLanguage().data || {}) || {})[entityForeign];
(data.fields || {})[linkForeign] = labelForeign;
(data.links || {})[linkForeign] = labelForeign;
this.getMetadata().load(function () {
this.trigger('after:save');