entity manager 1

This commit is contained in:
yuri
2015-01-20 16:40:04 +02:00
parent 46913c3574
commit 3030643ce4
23 changed files with 1302 additions and 34 deletions
@@ -0,0 +1,65 @@
<?php
/************************************************************************
* 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/.
************************************************************************/
namespace Espo\Controllers;
use \Espo\Core\Exceptions\BadRequest;
use \Espo\Core\Exceptions\Forbidden;
class EntityManager extends \Espo\Core\Controllers\Base
{
protected function checkControllerAccess()
{
if (!$this->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);
}
}
@@ -0,0 +1,38 @@
<?php
/************************************************************************
* 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/.
************************************************************************/
namespace Espo\Core\Loaders;
class EntityManagerUtil extends Base
{
public function load()
{
$entityManager = new \Espo\Core\Utils\EntityManager(
$this->getContainer()->get('metadata'),
$this->getContainer()->get('language'),
$this->getContainer()->get('fileManager')
);
return $entityManager;
}
}
@@ -0,0 +1,30 @@
<?php
/************************************************************************
* 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/.
************************************************************************/
namespace Espo\Core\Templates\Controllers;
class Base extends \Espo\Core\Controllers\Record
{
}
@@ -0,0 +1,30 @@
<?php
/************************************************************************
* 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/.
************************************************************************/
namespace Espo\Core\Templates\Controllers;
class Person extends \Espo\Core\Controllers\Record
{
}
@@ -0,0 +1,29 @@
<?php
/************************************************************************
* 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/.
************************************************************************/
namespace Espo\Core\Templates\Entities;
class Base extends \Espo\Core\ORM\Entity
{
}
@@ -0,0 +1,29 @@
<?php
/************************************************************************
* 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/.
************************************************************************/
namespace Espo\Core\Templates\Entities;
class Person extends \Espo\Core\Entities\Person
{
}
@@ -0,0 +1,4 @@
{
"controller": "Controllers.Record",
"boolFilters": ["onlyMy"]
}
@@ -0,0 +1,65 @@
{
"fields": {
"name": {
"type": "varchar",
"required": true
},
"description": {
"type": "text"
},
"createdAt": {
"type": "datetime",
"readOnly": true
},
"modifiedAt": {
"type": "datetime",
"readOnly": true
},
"createdBy": {
"type": "link",
"readOnly": true
},
"modifiedBy": {
"type": "link",
"readOnly": true
},
"assignedUser": {
"type": "link",
"required": true
},
"teams": {
"type": "linkMultiple"
}
},
"links": {
"createdBy": {
"type": "belongsTo",
"entity": "User"
},
"modifiedBy": {
"type": "belongsTo",
"entity": "User"
},
"assignedUser": {
"type": "belongsTo",
"entity": "User"
},
"teams": {
"type": "hasMany",
"entity": "Team",
"relationName": "EntityTeam"
}
},
"collection": {
"sortBy": "createdAt",
"asc": false
},
"indexes": {
"name": {
"columns": ["name", "deleted"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
}
}
}
@@ -0,0 +1,4 @@
{
"controller": "Controllers.Record",
"boolFilters": ["onlyMy"]
}
@@ -0,0 +1,109 @@
{
"name": {
"type": "personName"
},
"salutationName": {
"type": "enum",
"options": ["", "Mr.", "Mrs.", "Dr."]
},
"firstName": {
"type": "varchar",
"maxLength": 100,
"default": ""
},
"lastName": {
"type": "varchar",
"maxLength": 100,
"required": true,
"default": ""
},
"description": {
"type": "text"
},
"createdAt": {
"type": "datetime",
"readOnly": true
},
"emailAddress": {
"type": "email"
},
"phoneNumber": {
"type": "phone",
"typeList": ["Mobile", "Office", "Home", "Fax", "Other"],
"defaultType": "Mobile"
},
"address": {
"type": "address"
},
"addressStreet": {
"type": "text",
"maxLength": 255,
"dbType": "varchar"
},
"addressCity": {
"type": "varchar"
},
"addressState": {
"type": "varchar"
},
"addressCountry": {
"type": "varchar"
},
"addressPostalCode": {
"type": "varchar"
},
"modifiedAt": {
"type": "datetime",
"readOnly": true
},
"createdBy": {
"type": "link",
"readOnly": true
},
"modifiedBy": {
"type": "link",
"readOnly": true
},
"assignedUser": {
"type": "link",
"required": true
},
"teams": {
"type": "linkMultiple"
}
},
"links": {
"createdBy": {
"type": "belongsTo",
"entity": "User"
},
"modifiedBy": {
"type": "belongsTo",
"entity": "User"
},
"assignedUser": {
"type": "belongsTo",
"entity": "User"
},
"teams": {
"type": "hasMany",
"entity": "Team",
"relationName": "EntityTeam"
}
},
"collection": {
"sortBy": "createdAt",
"asc": false
},
"indexes": {
"firstName": {
"columns": ["firstName", "deleted"]
},
"name": {
"columns": ["firstName", "lastName"]
},
"assignedUser": {
"columns": ["assignedUserId", "deleted"]
}
}
}
@@ -0,0 +1,30 @@
<?php
/************************************************************************
* 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/.
************************************************************************/
namespace Espo\Core\Templates\Repositories;
class Base extends \Espo\Core\ORM\Repositories\RDB
{
}
@@ -0,0 +1,30 @@
<?php
/************************************************************************
* 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/.
************************************************************************/
namespace Espo\Core\Templates\Repositories;
class Base extends \Espo\Core\ORM\Repositories\RDB
{
}
@@ -0,0 +1,336 @@
<?php
/************************************************************************
* 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/.
************************************************************************/
namespace Espo\Core\Utils;
use \Espo\Core\Exceptions\Error;
use \Espo\Core\Exceptions\Conflict;
use \Espo\Core\Utils\Json;
class EntityManager
{
private $metadata;
private $language;
private $fileManager;
private $metadataUtils;
protected $isChanged = null;
public function __construct(Metadata $metadata, Language $language, File\Manager $fileManager)
{
$this->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;
}
}
@@ -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.",
@@ -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"
}
}
@@ -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",
@@ -0,0 +1,46 @@
<div class="page-header"><h3><a href="#Admin">{{translate 'Administration'}}</a> &raquo {{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>
</div>
<table class="table table-bordered table-hover">
<tr>
<th>{{translate 'systemName' scope='EntityManager' category='fields'}}</th>
<th>{{translate 'name' scope='EntityManager' category='fields'}}</th>
<th>{{translate 'type' scope='EntityManager' category='fields'}}</th>
</tr>
{{#each scopeDataList}}
<tr data-scope="{{name}}">
<td width="20%">
<a href="javascript:" data-action="editEntity" data-scope="{{name}}">
{{name}}
</a>
</td>
<td width="20%">
{{translate name category='scopeNames'}}
</td>
<td width="15%">
{{#if type}}
{{translateOption type field='type' scope='EntityManager'}}
{{/if}}
</td>
<td width="10%">
{{#if customizable}}
<a href="#Admin/fieldManager/scope={{name}}">{{translate 'Fields' scope='EntityManager'}}</a>
{{/if}}
</td>
<td width="10%">
{{#if customizable}}
<a href="#Admin/relationshipManager/scope={{name}}">{{translate 'Relationships' scope='EntityManager'}}</a>
{{/if}}
</td>
<td align="right" width="10%">
{{#if isCustom}}
<a href="javascript:" class="btn btn-danger btn-sm" data-action="removeEntity" data-scope="{{name}}">{{translate 'Remove'}}</a>
{{/if}}
</td>
</tr>
{{/each}}
</table>
@@ -0,0 +1,36 @@
<div class="cell cell-type form-group">
<label class="field-label-type control-label">{{translate 'type' category='fields' scope='EntityManager'}}</label>
<div class="field field-type">
{{{type}}}
</div>
</div>
<div class="cell cell-name form-group">
<label class="field-label-name control-label">{{translate 'name' category='fields' scope='EntityManager'}}</label>
<div class="field field-name">
{{{name}}}
</div>
</div>
<div class="cell cell-labelSingular form-group">
<label class="field-label-labelSingular control-label">{{translate 'labelSingular' category='fields' scope='EntityManager'}}</label>
<div class="field field-labelSingular">
{{{labelSingular}}}
</div>
</div>
<div class="cell cell-labelPlural form-group">
<label class="field-label-labelPlural control-label">{{translate 'labelPlural' category='fields' scope='EntityManager'}}</label>
<div class="field field-labelPlural">
{{{labelPlural}}}
</div>
</div>
{{#if stream}}
<div class="cell cell-stream form-group">
<label class="field-label-stream control-label">{{translate 'stream' category='fields' scope='EntityManager'}}</label>
<div class="field field-stream">
{{{stream}}}
</div>
</div>
{{/if}}
+14 -1
View File
@@ -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 () {
@@ -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'));
},
});
});
@@ -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));
},
});
});
@@ -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('<a href="#Admin/fieldManager/scope='+this.scope+'">' + this.getLanguage().translate(this.scope, 'scopeNamesPlural') + '</a> » ' + 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'));
},
});
});
@@ -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'));
},
});
});