layout sets

This commit is contained in:
Yuri Kuznetsov
2020-02-25 16:41:52 +02:00
parent 506f7cf410
commit f2e5197568
61 changed files with 976 additions and 184 deletions

View File

@@ -30,62 +30,49 @@
namespace Espo\Controllers;
use Espo\Core\Utils as Utils;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Error;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Error;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\BadRequest;
class Layout extends \Espo\Core\Controllers\Base
{
public function actionRead($params, $data)
public function getActionRead($params, $data)
{
return $this->getServiceFactory()->create('Layout')->getForFrontend($params['scope'], $params['name']);
$scope = $params['scope'] ?? null;
$name = $params['name'] ?? null;
return $this->getServiceFactory()->create('Layout')->getForFrontend($scope, $name);
}
public function actionUpdate($params, $data, $request)
public function putActionUpdate($params, $data, $request)
{
if (is_object($data)) {
$data = get_object_vars($data);
}
if (is_object($data)) $data = get_object_vars($data);
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
if (!$this->getUser()->isAdmin()) throw new Forbidden();
if (!$request->isPut() && !$request->isPatch()) {
throw new BadRequest();
}
$scope = $params['scope'] ?? null;
$name = $params['name'] ?? null;
$setId = $params['setId'] ?? null;
$layoutManager = $this->getContainer()->get('layout');
$layoutManager->set($data, $params['scope'], $params['name']);
$result = $layoutManager->save();
if ($result === false) {
throw new Error("Error while saving layout.");
}
$this->getContainer()->get('dataManager')->updateCacheTimestamp();
return $layoutManager->get($params['scope'], $params['name']);
}
public function actionPatch($params, $data, $request)
{
return $this->actionUpdate($params, $data, $request);
return $this->getServiceFactory()->create('Layout')->update($scope, $name, $setId, $data);
}
public function postActionResetToDefault($params, $data, $request)
{
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
if (!$this->getUser()->isAdmin()) throw new Forbidden();
if (empty($data->scope) || empty($data->name)) {
throw new BadRequest();
}
if (empty($data->scope) || empty($data->name)) throw new BadRequest();
$this->getContainer()->get('dataManager')->updateCacheTimestamp();
return $this->getServiceFactory()->create('Layout')->resetToDefault($data->scope, $data->name, $data->setId ?? null);
}
return $this->getContainer()->get('layout')->resetToDefault($data->scope, $data->name);
public function getActionGetOriginal($params, $data, $request)
{
if (!$this->getUser()->isAdmin()) throw new Forbidden();
return $this->getServiceFactory()->create('Layout')->getOriginal(
$request->get('scope'), $request->get('name'), $request->get('setId')
);
}
}

View File

@@ -0,0 +1,42 @@
<?php
/************************************************************************
* 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.
************************************************************************/
namespace Espo\Controllers;
use Espo\Core\Exceptions\Forbidden;
class LayoutSet extends \Espo\Core\Controllers\Record
{
protected function checkControllerAccess()
{
if (!$this->getUser()->isAdmin()) {
throw new Forbidden();
}
}
}

View File

@@ -1863,6 +1863,7 @@ class Base
$method = 'filter' . ucfirst($filter);
if (method_exists($this, $method)) {
$this->$method($result);
return;
} else {
$className = $this->getMetadata()->get(['entityDefs', $this->entityType, 'collection', 'filters', $filter, 'className']);
if ($className) {
@@ -1877,7 +1878,10 @@ class Base
}
$impl->applyFilter($this->entityType, $filter, $result, $this);
}
return;
}
$result['whereClause'][] = ['id' => null];
}
public function applyFilter(string $filter, array &$result)

View File

@@ -99,6 +99,7 @@ class Currency extends Base
'orderBy' => [
'sql' => $converedFieldName . " {direction}",
'leftJoins' => $leftJoins,
'additionalSelect' => ["{$alias}.rate"],
],
'attributeRole' => 'valueConverted',
'fieldType' => 'currency',
@@ -107,6 +108,7 @@ class Currency extends Base
$defs[$entityType]['fields'][$fieldName]['orderBy'] = [
'sql' => $part . " * {$alias}.rate {direction}",
'leftJoins' => $leftJoins,
'additionalSelect' => ["{$alias}.rate"],
];
}

View File

@@ -0,0 +1,34 @@
<?php
/************************************************************************
* 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.
************************************************************************/
namespace Espo\Entities;
class LayoutRecord extends \Espo\Core\ORM\Entity
{
}

View File

@@ -0,0 +1,34 @@
<?php
/************************************************************************
* 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.
************************************************************************/
namespace Espo\Entities;
class LayoutSet extends \Espo\Core\ORM\Entity
{
}

View File

@@ -29,10 +29,10 @@
namespace Espo\EntryPoints;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\BadRequest;
use \Espo\Core\Exceptions\Error;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Error;
class Avatar extends Image
{
@@ -40,7 +40,7 @@ class Avatar extends Image
public static $notStrictAuth = true;
protected $systemColor = [212,114,155];
protected $systemColor = '#a4b5bd';
protected $colorList = [
[111,168,214],
@@ -63,8 +63,10 @@ class Avatar extends Image
}
$x = intval($sum % 128) + 1;
$index = intval($x * count($this->colorList) / 128);
return $this->colorList[$index];
$colorList = $this->getMetadata()->get(['app', 'avatars', 'colorList']) ?? $this->colorList;
$index = intval($x * count($colorList) / 128);
return $colorList[$index];
}
public function run()
@@ -111,7 +113,7 @@ class Avatar extends Image
$hash = $userId;
$color = $this->getColor($userId);
if ($hash === 'system') {
$color = $this->systemColor;
$color = $this->getMetadata()->get(['app', 'avatars', 'systemColor']) ?? $this->systemColor;
}
$imgContent = $identicon->getImageData($hash, $width, $color);
@@ -120,6 +122,4 @@ class Avatar extends Image
}
}
}
}

View File

@@ -308,6 +308,7 @@ abstract class Base
if (empty($params['customJoin'])) {
$params['customJoin'] = '';
}
$params['additionalSelect'] = $params['additionalSelect'] ?? [];
$wherePart = $this->getWhere($entity, $whereClause, 'AND', $params);
@@ -318,9 +319,29 @@ abstract class Base
}
if (empty($params['aggregation'])) {
$selectPart = $this->getSelect($entity, $params['select'], $params['distinct'], $params['skipTextColumns'], $params['maxTextColumnsLength'], $params);
$selectPart = $this->getSelect(
$entity, $params['select'], $params['distinct'], $params['skipTextColumns'], $params['maxTextColumnsLength'], $params
);
$orderPart = $this->getOrder($entity, $params['orderBy'], $params['order'], $params);
if (!empty($params['extraAdditionalSelect'])) {
$extraSelect = [];
foreach ($params['extraAdditionalSelect'] as $item) {
if (!in_array($item, $params['select']) && !in_array($item, $params['additionalSelect'])) {
$extraSelect[] = $item;
}
}
if (count($extraSelect)) {
$extraSelectPart = $this->getSelect(
$entity, $extraSelect, false
);
if ($extraSelectPart) {
$selectPart .= ', ' . $extraSelectPart;
}
}
}
if (!empty($params['additionalColumns']) && is_array($params['additionalColumns']) && !empty($params['relationName'])) {
foreach ($params['additionalColumns'] as $column => $field) {
$itemAlias = $this->sanitizeSelectAlias($field);
@@ -955,6 +976,16 @@ abstract class Base
$params['joins'][] = $j;
}
}
if (!empty($fieldDefs[$type]['additionalSelect'])) {
$params['extraAdditionalSelect'] = $params['extraAdditionalSelect'] ?? [];
foreach ($fieldDefs[$type]['additionalSelect'] as $value) {
$value = str_replace('{alias}', $alias, $value);
if (!in_array($value, $params['extraAdditionalSelect'])) {
$params['extraAdditionalSelect'][] = $value;
}
}
}
}
return $part;

View File

@@ -0,0 +1,68 @@
<?php
/************************************************************************
* 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.
************************************************************************/
namespace Espo\Repositories;
use Espo\ORM\Entity;
class LayoutSet extends \Espo\Core\ORM\Repositories\RDB
{
protected function afterSave(Entity $entity, array $options = [])
{
parent::afterSave($entity);
if (!$entity->isNew() && $entity->has('layoutList')) {
$listBefore = $entity->getFetched('layoutList') ?? [];
$listNow = $entity->get('layoutList') ?? [];
foreach ($listBefore as $name) {
if (!in_array($name, $listNow)) {
$layout = $this->getEntityManager()->getRepository('LayoutRecord')->where([
'layoutSetId' => $entity->id,
'name' => $name,
])->findOne();
if ($layout) {
$this->getEntityManager()->removeEntity($layout);
}
}
}
}
}
protected function afterRemove(Entity $entity, array $options = [])
{
$layoutList = $this->getEntityManager()->getRepository('LayoutRecord')->where([
'layoutSetId' => $entity->id,
])->find();
foreach ($layoutList as $layout) {
$this->getEntityManager()->removeEntity($layout);
}
}
}

View File

@@ -71,6 +71,7 @@
"Permissions": "Permissions",
"Email Addresses": "Email Addresses",
"Phone Numbers": "Phone Numbers",
"Layout Sets": "Layout Sets",
"Success": "Success",
"Fail": "Fail",
"is recommended": "is recommended",
@@ -262,6 +263,7 @@
"emailAddresses": "All emailes addresses stored in the system.",
"phoneNumbers": "All phone numbers stored in the system.",
"dashboardTemplates": "Deploy dashboards to users.",
"layoutSets": "Collections of layouts that can be assigned to teams & portals.",
"pdfTemplates": "Templates for printing to PDF."
},
"options": {

View File

@@ -51,6 +51,7 @@
"ArrayValue": "Array Value",
"DashboardTemplate": "Dashboard Template",
"Currency": "Currency",
"LayoutSet": "Layout Set",
"Webhook": "Webhook"
},
"scopeNamesPlural": {
@@ -94,6 +95,7 @@
"ArrayValue": "Array Values",
"DashboardTemplate": "Dashboard Templates",
"Currency": "Currency",
"LayoutSet": "Layout Sets",
"Webhook": "Webhooks"
},
"labels": {

View File

@@ -0,0 +1,11 @@
{
"fields": {
"layoutList": "Layouts"
},
"labels": {
"Create LayoutSet": "Create Layout Set",
"Edit Layouts": "Edit Layouts"
},
"tooltips": {
}
}

View File

@@ -17,12 +17,14 @@
"timeZone": "Time Zone",
"weekStart": "First Day of Week",
"defaultCurrency": "Default Currency",
"layoutSet": "Layout Set",
"customUrl": "Custom URL",
"customId": "Custom ID"
},
"links": {
"users": "Users",
"portalRoles": "Roles",
"layoutSet": "Layout Set",
"notes": "Notes"
},
"tooltips": {

View File

@@ -2,12 +2,14 @@
"fields": {
"name": "Name",
"roles": "Roles",
"layoutSet": "Layout Set",
"positionList": "Position List"
},
"links": {
"users": "Users",
"notes": "Notes",
"roles": "Roles",
"layoutSet": "Layout Set",
"inboundEmails": "Group Email Accounts"
},
"tooltips": {

View File

@@ -0,0 +1,17 @@
[
{
"label": "",
"rows": [
[{"name": "name"}, false],
[
{"name": "layoutList"},
{
"name": "edit",
"customLabel": "",
"view": "views/layout-set/fields/edit",
"inlineEditDisabled": true
}
]
]
}
]

View File

@@ -0,0 +1,8 @@
[
{
"label": "",
"rows": [
[{"name": "name"}]
]
}
]

View File

@@ -0,0 +1,3 @@
[
{"name":"name", "link": true}
]

View File

@@ -0,0 +1,3 @@
[
"teams"
]

View File

@@ -20,6 +20,7 @@
"label": "User Interface",
"rows": [
[{"name": "companyLogo"}, {"name": "theme"}],
[{"name": "layoutSet"}, false],
[{"name": "tabList"}, {"name": "quickCreateList"}],
[{"name": "dashboardLayout", "fullWidth": true}]
]

View File

@@ -2,11 +2,16 @@
{
"rows": [
[
{"name": "name"}
{"name": "name"},
false
],
[
{"name": "roles"},
{"name": "positionList"}
],
[
{"name": "layoutSet"},
false
]
]
}

View File

@@ -257,6 +257,13 @@
"iconClass": "fas fa-th-large",
"description": "dashboardTemplates"
},
{
"url": "#LayoutSet",
"label": "Layout Sets",
"iconClass": "fas fa-table",
"description": "layoutSets"
},
{
"url": "#Attachment",
"label": "Attachments",

View File

@@ -0,0 +1,12 @@
{
"controller": "controllers/layout-set",
"searchPanelDisabled": true,
"duplicateDisabled": true,
"relationshipPanels": {
"teams": {
"createDisabled": true,
"viewDisabled": true,
"rowActionsView": "views/record/row-actions/relationship-unlink-only"
}
}
}

View File

@@ -0,0 +1,25 @@
{
"fields": {
"name": {
"type": "varchar"
},
"layoutSet": {
"type": "link"
},
"data": {
"type": "jsonObject"
}
},
"links": {
"layoutSet": {
"type": "belengsTo",
"entity": "LayoutSet",
"foreign": "layoutRecords"
}
},
"indexes": {
"nameLayoutSetId": {
"columns": ["name", "layoutSetId"]
}
}
}

View File

@@ -0,0 +1,44 @@
{
"fields": {
"name": {
"type": "varchar",
"required": true,
"maxLength": 100,
"trim": true
},
"layoutList": {
"type": "multiEnum",
"displayAsList": true,
"view": "views/layout-set/fields/layout-list"
},
"createdAt": {
"type": "datetime",
"readOnly": true
},
"modifiedAt": {
"type": "datetime",
"readOnly": true
}
},
"links": {
"layoutRecords": {
"type": "hasMany",
"entity": "LayoutRecord",
"foreign": "layoutSet"
},
"teams": {
"type": "hasMany",
"entity": "Team",
"foreign": "layoutSet"
},
"portals": {
"type": "hasMany",
"entity": "Portal",
"foreign": "layoutSet"
}
},
"collection": {
"orderBy": "name",
"order": "asc"
}
}

View File

@@ -94,6 +94,9 @@
"customUrl": {
"type": "url"
},
"layoutSet": {
"type": "link"
},
"modifiedAt": {
"type": "datetime",
"readOnly": true
@@ -136,6 +139,11 @@
"type": "hasMany",
"entity": "Note",
"foreign": "portals"
},
"layoutSet": {
"type": "belongsTo",
"entity": "LayoutSet",
"foreign": "portals"
}
},
"collection": {

View File

@@ -18,6 +18,9 @@
"notStorable": true,
"disabled": true
},
"layoutSet": {
"type": "link"
},
"createdAt": {
"type": "datetime",
"readOnly": true
@@ -47,6 +50,11 @@
"type": "hasMany",
"entity": "InboundEmail",
"foreign": "teams"
},
"layoutSet": {
"type": "belongsTo",
"entity": "LayoutSet",
"foreign": "teams"
}
},
"collection": {

View File

@@ -0,0 +1,3 @@
{
"entity": true
}

View File

@@ -0,0 +1,3 @@
{
"entity": true
}

View File

@@ -157,8 +157,8 @@
}
},
{
"route": "/:controller/layout/:name",
"method": "patch",
"route": "/:controller/layout/:name/:setId",
"method": "put",
"params": {
"controller": "Layout",
"scope": ":controller"

View File

@@ -30,6 +30,7 @@
namespace Espo\Services;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Error;
class Layout extends \Espo\Core\Services\Base
{
@@ -37,7 +38,10 @@ class Layout extends \Espo\Core\Services\Base
{
$this->addDependency('acl');
$this->addDependency('layout');
$this->addDependency('entityManager');
$this->addDependency('metadata');
$this->addDependency('dataManager');
$this->addDependency('user');
}
protected function getAcl()
@@ -52,10 +56,53 @@ class Layout extends \Espo\Core\Services\Base
public function getForFrontend(string $scope, string $name)
{
$dataString = $this->getInjection('layout')->get($scope, $name);
$layoutSetId = null;
$data = null;
$em = $this->getInjection('entityManager');
$user = $this->getInjection('user');
if ($user->isPortal()) {
$portalId = $user->get('portalId');
if ($portalId) {
$portal = $em->getRepository('Portal')->select(['layoutSetId'])->where(['id' => $portalId])->findOne();
if ($portal) {
$layoutSetId = $portal->get('layoutSetId');
}
}
} else {
$teamId = $user->get('defaultTeamId');
if ($teamId) {
$team = $em->getRepository('Team')->select(['layoutSetId'])->where(['id' => $teamId])->findOne();
if ($team) {
$layoutSetId = $team->get('layoutSetId');
}
}
}
if ($layoutSetId) {
$nameReal = $name;
if ($user->isPortal()) {
if (substr($name, -6) === 'Portal') {
$nameReal = substr($name, 0, -6);
}
}
$layout = $this->getRecordFromSet($scope, $nameReal, $layoutSetId, true);
if ($layout) {
$data = $layout->get('data');
}
}
if (!$data) {
$dataString = $this->getInjection('layout')->get($scope, $name);
} else {
$dataString = json_encode($data);
}
if (!$dataString) {
throw new NotFound("Layout {$scope}:{$scope} is not found.");
throw new NotFound("Layout {$scope}:{$name} is not found.");
}
if (!$this->getUser()->isAdmin()) {
@@ -80,4 +127,90 @@ class Layout extends \Espo\Core\Services\Base
return $dataString;
}
protected function getRecordFromSet(string $scope, string $name, string $setId, bool $skipCheck = false)
{
$em = $this->getInjection('entityManager');
$layoutSet = $em->getEntity('LayoutSet', $setId);
if (!$layoutSet) throw new NotFound();
$layoutList = $layoutSet->get('layoutList') ?? [];
$fullName = $scope . '.' . $name;
if (!in_array($fullName, $layoutList)) {
if ($skipCheck) return null;
throw new NotFound("Layout {$fullName} is no allowed in set.");
}
$layout = $em->getRepository('LayoutRecord')->where([
'layoutSetId' => $setId,
'name' => $fullName,
])->findOne();
return $layout;
}
public function update(string $scope, string $name, ?string $setId, $data)
{
if ($setId) {
$layout = $this->getRecordFromSet($scope, $name, $setId);
$em = $this->getInjection('entityManager');
if (!$layout) {
$layout = $em->getEntity('LayoutRecord');
$layout->set([
'layoutSetId' => $setId,
'name' => $scope . '.' . $name,
]);
}
$layout->set('data', $data);
$em->saveEntity($layout);
return $layout->get('data');
}
$layoutManager = $this->getInjection('layout');
$layoutManager->set($data, $scope, $name);
$result = $layoutManager->save();
if ($result === false) throw new Error("Error while saving layout.");
$this->getInjection('dataManager')->updateCacheTimestamp();
return $layoutManager->get($scope, $name);
}
public function resetToDefault(string $scope, string $name, ?string $setId = null)
{
$this->getInjection('dataManager')->updateCacheTimestamp();
if ($setId) {
$layout = $this->getRecordFromSet($scope, $name, $setId);
if ($layout) {
$em = $this->getInjection('entityManager');
$em->removeEntity($layout);
}
return $this->getInjection('layout')->get($scope, $name);
}
return $this->getInjection('layout')->resetToDefault($scope, $name);
}
public function getOriginal(string $scope, string $name, ?string $setId = null)
{
$this->getInjection('dataManager')->updateCacheTimestamp();
if ($setId) {
$layout = $this->getRecordFromSet($scope, $name, $setId, true);
if ($layout) {
return $layout->get('data');
}
}
return $this->getInjection('layout')->get($scope, $name);
}
}

View File

@@ -1,6 +1,4 @@
<div class="page-header"><h3><a href="#Admin">{{translate 'Administration'}}</a>
<span class="breadcrumb-separator"><span class="chevron-right"></span></span>
{{translate 'Layout Manager' scope='Admin'}}</h3></div>
<div class="page-header"><h3>{{{headerHtml}}}</h3></div>
<div class="row">
<div id="layouts-menu" class="col-sm-3">

View File

@@ -1,2 +1,2 @@
<input data-name="{{name}}" type="text">
<input data-name="{{name}}" type="text" class="{{#if viewObject.params.displayAsList}}as-list{{/if}}">

View File

@@ -1,5 +1,5 @@
<div class="container content">
<div class="col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2">
<div class="col-md-6 col-md-offset-2 col-sm-8 col-sm-offset-1">
<div class="panel panel-success">
<div class="panel-body">
<p>

View File

@@ -1,5 +1,5 @@
<div class="container content">
<div class="col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2">
<div class="col-md-6 col-md-offset-2 col-sm-8 col-sm-offset-1">
<div class="panel panel-success">
<div class="panel-body">
{{#if messageField}}

View File

@@ -0,0 +1,45 @@
/************************************************************************
* 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('controllers/layout-set', 'controllers/record', function (Dep) {
return Dep.extend({
actionEditLayouts: function (options) {
var id = options.id;
if (!id) throw new Error("ID not passed.");
this.main('views/layout-set/layouts', {
layoutSetId: id,
scope: options.scope,
type: options.type,
});
},
});
});

View File

@@ -50,8 +50,12 @@ define('layout-manager', [], function () {
return this.applicationId + '-' + scope + '-' + type;
},
getUrl: function (scope, type) {
return scope + '/layout/' + type;
getUrl: function (scope, type, setId) {
var url = scope + '/layout/' + type;
if (setId) {
url += '/' + setId;
}
return url;
},
get: function (scope, type, callback, cache) {
@@ -81,11 +85,8 @@ define('layout-manager', [], function () {
}
}
this.ajax({
url: this.getUrl(scope, type),
type: 'GET',
dataType: 'json',
success: function (layout) {
Espo.Ajax.getRequest(this.getUrl(scope, type)).then(
function (layout) {
if (typeof callback === 'function') {
callback(layout);
}
@@ -94,54 +95,61 @@ define('layout-manager', [], function () {
this.cache.set('app-layout', key, layout);
}
}.bind(this)
});
);
},
set: function (scope, type, layout, callback) {
var key = this.getKey(scope, type);
getOriginal: function (scope, type, setId, callback) {
var url = 'Layout/action/getOriginal?scope='+scope+'&name='+type;
if (setId) url += '&setId='+setId;
this.ajax({
url: this.getUrl(scope, type),
type: 'PUT',
data: JSON.stringify(layout),
success: function () {
if (this.cache && key) {
this.cache.set('app-layout', key, layout);
Espo.Ajax.getRequest(url).then(
function (layout) {
if (typeof callback === 'function') {
callback(layout);
}
this.data[key] = layout;
}.bind(this)
);
},
set: function (scope, type, layout, callback, setId) {
Espo.Ajax.putRequest(this.getUrl(scope, type, setId), layout).then(
function () {
var key = this.getKey(scope, type);
if (this.cache && key) {
this.cache.clear('app-layout', key);
}
delete this.data[key];
this.trigger('sync');
if (typeof callback === 'function') {
callback();
}
}.bind(this)
});
);
},
resetToDefault: function (scope, type, callback) {
var key = this.getKey(scope, type);
this.ajax({
url: 'Layout/action/resetToDefault',
type: 'POST',
data: JSON.stringify({
scope: scope,
name: type
}),
success: function (layout) {
resetToDefault: function (scope, type, callback, setId) {
Espo.Ajax.postRequest('Layout/action/resetToDefault', {
scope: scope,
name: type,
setId: setId,
}).then(
function (layout) {
var key = this.getKey(scope, type);
if (this.cache) {
this.cache.clear('app-layout', key);
}
this.data[key] = layout;
delete this.data[key];
this.trigger('sync');
if (typeof callback === 'function') {
callback();
}
}.bind(this)
});
}
);
},
}, Backbone.Events);
return LayoutManager;
});

View File

@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/base', 'view', function (Dep) {
define('views/admin/layouts/base', 'view', function (Dep) {
return Dep.extend({
@@ -92,13 +92,13 @@ Espo.define('views/admin/layouts/base', 'view', function (Dep) {
if (typeof callback == 'function') {
callback();
}
}.bind(this));
}.bind(this), this.setId);
},
resetToDefault: function () {
this.getHelper().layoutManager.resetToDefault(this.scope, this.type, function () {
this.cancel();
}.bind(this));
}.bind(this), this.options.setId);
},
reset: function () {
@@ -112,6 +112,7 @@ Espo.define('views/admin/layouts/base', 'view', function (Dep) {
this.events = _.clone(this.events);
this.scope = this.options.scope;
this.type = this.options.type;
this.setId = this.options.setId;
this.dataAttributeList =
this.getMetadata().get(['clientDefs', this.scope, 'additionalLayouts', this.type, 'dataAttributeList'])

View File

@@ -66,12 +66,12 @@ define('views/admin/layouts/default-side-panel', 'views/admin/layouts/rows', fun
loadLayout: function (callback) {
this.getModelFactory().create(Espo.Utils.hyphenToUpperCamelCase(this.scope), function (model) {
this.getHelper().layoutManager.get(this.scope, this.type, function (layout) {
this.getHelper().layoutManager.getOriginal(this.scope, this.type, this.setId, function (layout) {
this.readDataFromLayout(model, layout);
if (callback) {
callback();
}
}.bind(this), false);
}.bind(this));
}.bind(this));
},

View File

@@ -26,10 +26,9 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/detail-convert', 'views/admin/layouts/detail', function (Dep) {
define('views/admin/layouts/detail-convert', 'views/admin/layouts/detail', function (Dep) {
return Dep.extend({
});
});

View File

@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/detail-small', 'views/admin/layouts/detail', function (Dep) {
define('views/admin/layouts/detail-small', 'views/admin/layouts/detail', function (Dep) {
return Dep.extend({
@@ -34,4 +34,3 @@ Espo.define('views/admin/layouts/detail-small', 'views/admin/layouts/detail', fu
});
});

View File

@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/detail', 'views/admin/layouts/grid', function (Dep) {
define('views/admin/layouts/detail', 'views/admin/layouts/grid', function (Dep) {
return Dep.extend({
@@ -91,7 +91,7 @@ Espo.define('views/admin/layouts/detail', 'views/admin/layouts/grid', function (
promiseList.push(
new Promise(function (resolve) {
this.getModelFactory().create(this.scope, function (m) {
this.getHelper().layoutManager.get(this.scope, this.type, function (layoutLoaded) {
this.getHelper().layoutManager.getOriginal(this.scope, this.type, this.setId, function (layoutLoaded) {
layout = layoutLoaded;
model = m;
resolve();
@@ -103,10 +103,14 @@ Espo.define('views/admin/layouts/detail', 'views/admin/layouts/grid', function (
if (~['detail', 'detailSmall'].indexOf(this.type)) {
promiseList.push(
new Promise(function (resolve) {
this.getHelper().layoutManager.get(this.scope, 'sidePanels' + Espo.Utils.upperCaseFirst(this.type), function (layoutLoaded) {
this.sidePanelsLayout = layoutLoaded;
resolve();
}.bind(this));
this.getHelper().layoutManager.getOriginal(
this.scope, 'sidePanels' + Espo.Utils.upperCaseFirst(this.type),
this.setId,
function (layoutLoaded) {
this.sidePanelsLayout = layoutLoaded;
resolve();
}.bind(this)
);
}.bind(this))
);
}
@@ -116,24 +120,26 @@ Espo.define('views/admin/layouts/detail', 'views/admin/layouts/grid', function (
function (resolve) {
if (this.getMetadata().get(['clientDefs', scope, 'layoutDefaultSidePanelDisabled'])) resolve();
this.getHelper().layoutManager.get(this.scope, 'defaultSidePanel', function (layoutLoaded) {
this.defaultSidePanelLayout = layoutLoaded;
this.getHelper().layoutManager.getOriginal(this.scope, 'defaultSidePanel', this.setId,
function (layoutLoaded) {
this.defaultSidePanelLayout = layoutLoaded;
this.defaultPanelFieldList = Espo.Utils.clone(this.defaultPanelFieldList);
this.defaultPanelFieldList = Espo.Utils.clone(this.defaultPanelFieldList);
layoutLoaded.forEach(function (item) {
var field = item.name;
if (!field) return;
if (field === ':assignedUser') {
field = 'assignedUser';
}
if (!~this.defaultPanelFieldList.indexOf(field)) {
this.defaultPanelFieldList.push(field);
}
}, this);
layoutLoaded.forEach(function (item) {
var field = item.name;
if (!field) return;
if (field === ':assignedUser') {
field = 'assignedUser';
}
if (!~this.defaultPanelFieldList.indexOf(field)) {
this.defaultPanelFieldList.push(field);
}
}, this);
resolve();
}.bind(this));
resolve();
}.bind(this)
);
}.bind(this)
)
);

View File

@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/filters', 'views/admin/layouts/rows', function (Dep) {
define('views/admin/layouts/filters', 'views/admin/layouts/rows', function (Dep) {
return Dep.extend({
@@ -47,7 +47,7 @@ Espo.define('views/admin/layouts/filters', 'views/admin/layouts/rows', function
loadLayout: function (callback) {
this.getModelFactory().create(this.scope, function (model) {
this.getHelper().layoutManager.get(this.scope, this.type, function (layout) {
this.getHelper().layoutManager.getOriginal(this.scope, this.type, this.setId, function (layout) {
var allFields = [];
for (var field in model.defs.fields) {
@@ -86,7 +86,7 @@ Espo.define('views/admin/layouts/filters', 'views/admin/layouts/rows', function
}
callback();
}.bind(this), false);
}.bind(this));
}.bind(this));
},
@@ -111,8 +111,7 @@ Espo.define('views/admin/layouts/filters', 'views/admin/layouts/rows', function
return false;
}
return !model.getFieldParam(name, 'disabled') && !model.getFieldParam(name, 'layoutFiltersDisabled');
}
},
});
});

View File

@@ -58,6 +58,7 @@ define('views/admin/layouts/index', 'view', function (Dep) {
typeList: this.typeList,
scope: this.scope,
layoutScopeDataList: this.getLayoutScopeDataList(),
headerHtml: this.getHeaderHtml(),
};
},
@@ -125,7 +126,8 @@ define('views/admin/layouts/index', 'view', function (Dep) {
}, this);
this.on('after:render', function () {
$("#layouts-menu button[data-scope='" + this.options.scope + "'][data-type='" + this.options.type + "']").addClass('disabled');
$("#layouts-menu button[data-scope='" + this.options.scope + "'][data-type='" + this.options.type + "']")
.addClass('disabled');
this.renderLayoutHeader();
if (!this.options.scope) {
this.renderDefaultPage();
@@ -143,16 +145,17 @@ define('views/admin/layouts/index', 'view', function (Dep) {
this.scope = scope;
this.type = type;
this.getRouter().navigate('#Admin/layouts/scope=' + scope + '&type=' + type, {trigger: false});
this.navigate(scope, type);
this.notify('Loading...');
var typeReal = this.getMetadata().get('clientDefs.' + scope + '.additionalLayouts.' + type + '.type') || type;
this.createView('content', 'Admin.Layouts.' + Espo.Utils.upperCaseFirst(typeReal), {
this.createView('content', 'views/admin/layouts/' + Espo.Utils.camelCaseToHyphen(typeReal), {
el: '#layout-content',
scope: scope,
type: type,
setId: this.setId,
}, function (view) {
this.renderLayoutHeader();
view.render();
@@ -161,6 +164,10 @@ define('views/admin/layouts/index', 'view', function (Dep) {
}.bind(this));
},
navigate: function (scope, type) {
this.getRouter().navigate('#Admin/layouts/scope=' + scope + '&type=' + type, {trigger: false});
},
renderDefaultPage: function () {
$("#layout-header").html('').hide();
$("#layout-content").html(this.translate('selectLayout', 'messages', 'Admin'));
@@ -179,7 +186,14 @@ define('views/admin/layouts/index', 'view', function (Dep) {
updatePageTitle: function () {
this.setPageTitle(this.getLanguage().translate('Layout Manager', 'labels', 'Admin'));
},
getHeaderHtml: function () {
var separatorHtml = '<span class="breadcrumb-separator"><span class="chevron-right"></span></span>';
var html = "<a href=\"#Admin\">"+this.translate('Administration')+"</a> " + separatorHtml + ' ' +
this.translate('Layout Manager', 'labels', 'Admin');
return html;
},
});
});

View File

@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/kanban', 'views/admin/layouts/list', function (Dep) {
define('views/admin/layouts/kanban', 'views/admin/layouts/list', function (Dep) {
return Dep.extend({
@@ -54,7 +54,7 @@ Espo.define('views/admin/layouts/kanban', 'views/admin/layouts/list', function (
ignoreList: [],
ignoreTypeList: []
ignoreTypeList: [],
});
});

View File

@@ -26,10 +26,9 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/list-small', 'views/admin/layouts/list', function (Dep) {
define('views/admin/layouts/list-small', 'views/admin/layouts/list', function (Dep) {
return Dep.extend({
});
});

View File

@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/list', 'views/admin/layouts/rows', function (Dep) {
define('views/admin/layouts/list', 'views/admin/layouts/rows', function (Dep) {
return Dep.extend({
@@ -84,12 +84,12 @@ Espo.define('views/admin/layouts/list', 'views/admin/layouts/rows', function (De
loadLayout: function (callback) {
this.getModelFactory().create(Espo.Utils.hyphenToUpperCamelCase(this.scope), function (model) {
this.getHelper().layoutManager.get(this.scope, this.type, function (layout) {
this.getHelper().layoutManager.getOriginal(this.scope, this.type, this.setId, function (layout) {
this.readDataFromLayout(model, layout);
if (callback) {
callback();
}
}.bind(this), false);
}.bind(this));
}.bind(this));
},

View File

@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/mass-update', 'views/admin/layouts/rows', function (Dep) {
define('views/admin/layouts/mass-update', 'views/admin/layouts/rows', function (Dep) {
return Dep.extend({
@@ -55,7 +55,7 @@ Espo.define('views/admin/layouts/mass-update', 'views/admin/layouts/rows', funct
loadLayout: function (callback) {
this.getModelFactory().create(this.scope, function (model) {
this.getHelper().layoutManager.get(this.scope, this.type, function (layout) {
this.getHelper().layoutManager.getOriginal(this.scope, this.type, this.setId, function (layout) {
var allFields = [];
for (var field in model.defs.fields) {
@@ -97,7 +97,7 @@ Espo.define('views/admin/layouts/mass-update', 'views/admin/layouts/rows', funct
}
callback();
}.bind(this), false);
}.bind(this));
}.bind(this));
},
@@ -125,7 +125,7 @@ Espo.define('views/admin/layouts/mass-update', 'views/admin/layouts/rows', funct
if (layoutList && !~layoutList.indexOf(this.type)) return;
return !model.getFieldParam(name, 'disabled') && !model.getFieldParam(name, 'layoutMassUpdateDisabled') && !model.getFieldParam(name, 'readOnly');
}
},
});
});

View File

@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/relationships', 'views/admin/layouts/rows', function (Dep) {
define('views/admin/layouts/relationships', 'views/admin/layouts/rows', function (Dep) {
return Dep.extend({
@@ -65,7 +65,7 @@ Espo.define('views/admin/layouts/relationships', 'views/admin/layouts/rows', fun
loadLayout: function (callback) {
this.getModelFactory().create(this.scope, function (model) {
this.getHelper().layoutManager.get(this.scope, this.type, function (layout) {
this.getHelper().layoutManager.getOriginal(this.scope, this.type, this.setId, function (layout) {
var allFields = [];
for (var field in model.defs.links) {
@@ -125,7 +125,7 @@ Espo.define('views/admin/layouts/relationships', 'views/admin/layouts/rows', fun
}
callback();
}.bind(this), false);
}.bind(this));
}.bind(this));
},
@@ -138,4 +138,3 @@ Espo.define('views/admin/layouts/relationships', 'views/admin/layouts/rows', fun
}
});
});

View File

@@ -26,13 +26,11 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/side-panels-detail-small', 'views/admin/layouts/side-panels-detail', function (Dep) {
define('views/admin/layouts/side-panels-detail-small', 'views/admin/layouts/side-panels-detail', function (Dep) {
return Dep.extend({
viewType: 'detailSmall'
viewType: 'detailSmall',
});
});

View File

@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/side-panels-detail', 'views/admin/layouts/rows', function (Dep) {
define('views/admin/layouts/side-panels-detail', 'views/admin/layouts/rows', function (Dep) {
return Dep.extend({
@@ -71,12 +71,12 @@ Espo.define('views/admin/layouts/side-panels-detail', 'views/admin/layouts/rows'
},
loadLayout: function (callback) {
this.getHelper().layoutManager.get(this.scope, this.type, function (layout) {
this.getHelper().layoutManager.getOriginal(this.scope, this.type, this.setId, function (layout) {
this.readDataFromLayout(layout);
if (callback) {
callback();
}
}.bind(this), false);
}.bind(this));
},
readDataFromLayout: function (layout) {
@@ -188,5 +188,3 @@ Espo.define('views/admin/layouts/side-panels-detail', 'views/admin/layouts/rows'
});
});

View File

@@ -26,13 +26,11 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/side-panels-edit-small', 'views/admin/layouts/side-panels-detail', function (Dep) {
define('views/admin/layouts/side-panels-edit-small', 'views/admin/layouts/side-panels-detail', function (Dep) {
return Dep.extend({
viewType: 'editSmall'
viewType: 'editSmall',
});
});

View File

@@ -26,13 +26,11 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/admin/layouts/side-panels-edit', 'views/admin/layouts/side-panels-detail', function (Dep) {
define('views/admin/layouts/side-panels-edit', 'views/admin/layouts/side-panels-detail', function (Dep) {
return Dep.extend({
viewType: 'edit'
viewType: 'edit',
});
});

View File

@@ -318,7 +318,7 @@ define(
var entityType = this.typeHash[address] || null;
var id = this.idHash[address] || null;
var addressHtml = '<span>' + address + '</span>';
var addressHtml = this.getHelper().escapeString(address);
if (name) {
name = this.getHelper().escapeString(name);
@@ -329,14 +329,15 @@ define(
lineHtml = '<div>' + '<a href="#' + entityType + '/view/' + id + '">' + name + '</a> <span class="text-muted">&#187;</span> ' + addressHtml + '</div>';
} else {
if (name) {
lineHtml = '<span>' + name + ' <span class="text-muted">&#187;</span> ' + addressHtml + '</span>';
lineHtml = '<span class="email-address-line">' + name + ' <span class="text-muted">&#187;</span> <span>' +
addressHtml + '</span></span>';
} else {
lineHtml = addressHtml;
lineHtml = '<span class="email-address-line">' + addressHtml + '</span>';
}
}
if (!id) {
if (this.getAcl().check('Contact', 'edit')) {
lineHtml += From.prototype.getCreateHtml.call(this, address);
lineHtml = From.prototype.getCreateHtml.call(this, address) + lineHtml;
}
}
lineHtml = '<div>' + lineHtml + '</div>';
@@ -344,5 +345,4 @@ define(
},
});
});

View File

@@ -131,19 +131,21 @@ define(
var entityType = this.typeHash[address] || null;
var id = this.idHash[address] || null;
var addressHtml = '<span>' + address + '</span>';
var addressHtml = this.getHelper().escapeString(address);
var lineHtml = '';
if (id) {
lineHtml = '<div>' + '<a href="#' + entityType + '/view/' + id + '">' + name + '</a> <span class="text-muted">&#187;</span> ' + addressHtml + '</div>';
lineHtml = '<div>' + '<a href="#' + entityType + '/view/' + id + '">' + name +
'</a> <span class="text-muted">&#187;</span> ' + addressHtml + '</div>';
} else {
if (this.getAcl().check('Contact', 'create') || this.getAcl().check('Lead', 'create')) {
lineHtml += this.getCreateHtml(address);
}
if (name) {
lineHtml += '<span>' + name + ' <span class="text-muted">&#187;</span> ' + addressHtml + '</span>';
lineHtml += '<span class="email-address-line">' + name +
' <span class="text-muted">&#187;</span> <span>' + addressHtml + '</span></span>';
} else {
lineHtml += addressHtml;
lineHtml += '<span class="email-address-line">' + addressHtml + '</span>';
}
}
lineHtml = '<div>' + lineHtml + '</div>';

View File

@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
Espo.define('views/fields/array-int', 'views/fields/array', function (Dep) {
define('views/fields/array-int', 'views/fields/array', function (Dep) {
return Dep.extend({
@@ -57,9 +57,15 @@ Espo.define('views/fields/array-int', 'views/fields/array', function (Dep) {
if (isNaN(value)) {
return;
}
Dep.prototype.removeValue.call(this, value);
}
var valueInternal = value.toString().replace(/"/g, '\\"');
this.$list.children('[data-value="' + valueInternal + '"]').remove();
var index = this.selected.indexOf(value);
this.selected.splice(index, 1);
this.trigger('change');
},
});
});

View File

@@ -102,8 +102,11 @@ define('views/fields/formula', 'views/fields/text', function (Dep) {
if (this.$editor.length && (this.mode === 'edit' || this.mode == 'detail' || this.mode == 'list')) {
this.$editor
.css('minHeight', this.height + 'px')
.css('fontSize', '14px');
if (this.mode === 'edit') {
this.$editor.css('minHeight', this.height + 'px');
}
var editor = this.editor = ace.edit(this.containerId);
editor.setOptions({

View File

@@ -159,6 +159,7 @@ define('views/fields/multi-enum', ['views/fields/array', 'lib!Selectize'], funct
highlight: false,
searchField: ['label'],
plugins: pluginList,
copyClassesToDropdown: true,
score: function (search) {
var score = this.getScoreFunction(search);
search = search.toLowerCase();

View File

@@ -0,0 +1,40 @@
/************************************************************************
* 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/layout-set/fields/edit', ['views/fields/base'], function (Dep) {
return Dep.extend({
detailTemplateContent:
"<a class=\"btn btn-default\" href=\"#LayoutSet/editLayouts/id={{model.id}}\">" +
"{{translate 'Edit Layouts' scope='LayoutSet'}}</a>",
editTemplateContent: '',
});
});

View File

@@ -0,0 +1,73 @@
/************************************************************************
* 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/layout-set/fields/layout-list', [
'views/fields/multi-enum', 'views/admin/layouts/index'], function (Dep, LaouytsIndex) {
return Dep.extend({
typeList: [
'list',
'detail',
'listSmall',
'detailSmall',
'filters',
'massUpdate',
'relationships',
'sidePanelsDetail',
'sidePanelsEdit',
'sidePanelsDetailSmall',
'sidePanelsEditSmall',
],
setupOptions: function () {
this.params.options = [];
this.translatedOptions = {};
this.scopeList = Object.keys(this.getMetadata().get('scopes')).filter(function (item) {
return this.getMetadata().get(['scopes', item, 'layouts']);
}, this).sort(function (v1, v2) {
return this.translate(v1, 'scopeNames').localeCompare(this.translate(v2, 'scopeNames'));
}.bind(this));
var dataList = LaouytsIndex.prototype.getLayoutScopeDataList.call(this);
dataList.forEach(function (item1) {
item1.typeList.forEach(function (type) {
var item = item1.scope + '.' + type;
if (type.substr(-6) === 'Portal') return;
this.params.options.push(item);
this.translatedOptions[item] = this.translate(item1.scope, 'scopeNames') + '.' +
this.translate(type, 'layouts', 'Admin');
}, this);
}, this);
},
});
});

View File

@@ -0,0 +1,97 @@
/************************************************************************
* 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/layout-set/layouts', 'views/admin/layouts/index', function (Dep) {
return Dep.extend({
setup: function () {
Dep.prototype.setup.call(this);
var setId = this.setId = this.options.layoutSetId;
this.wait(
this.getModelFactory().create('LayoutSet')
.then(
function (m) {
this.sModel = m;
m.id = setId;
return m.fetch();
}.bind(this)
)
);
},
getLayoutScopeDataList: function () {
var dataList = [];
var list = this.sModel.get('layoutList') || [];
var scopeList = [];
list.forEach(function (item) {
var arr = item.split('.');
var scope = arr[0];
if (~scopeList.indexOf(scope)) return;
scopeList.push(scope);
});
scopeList.forEach(function (scope) {
var o = {};
o.scope = scope;
o.typeList = [];
list.forEach(function (item) {
var arr = item.split('.');
var scope = arr[0];
var type = arr[1];
if (scope !== o.scope) return;
o.typeList.push(type);
});
dataList.push(o);
});
return dataList;
},
getHeaderHtml: function () {
var m = this.sModel;
var separatorHtml = '<span class="breadcrumb-separator"><span class="chevron-right"></span></span>';
var html = "<a href=\"#LayoutSet\">"+this.translate('LayoutSet', 'scopeNamesPlural')+"</a> " + separatorHtml + ' ' +
"<a href=\"#LayoutSet/view/"+m.id+"\">"+Handlebars.Utils.escapeExpression(m.get('name'))+"</a> " +
separatorHtml + ' ' + this.translate('Edit Layouts', 'labels', 'LayoutSet');
return html;
},
navigate: function (scope, type) {
this.getRouter().navigate('#LayoutSet/editLayouts/id='+this.setId+'&scope='+scope + '&type='+type, {trigger: false});
},
});
});

View File

@@ -2790,7 +2790,7 @@ table.table td.cell .complex-text {
.email-address-create-dropdown .btn {
position: relative;
top: -1px;
top: -2px;
padding: 0 4px;
margin: 0;
margin-bottom: 0;
@@ -2798,6 +2798,11 @@ table.table td.cell .complex-text {
line-height: 1.3;
}
.email-address-create-dropdown + .email-address-line {
display: block;
width: ~"calc(100% - 17px)";
}
#main > .calendar-container {
margin-top: 10px;
@@ -3624,3 +3629,8 @@ a.link-gray {
}
}
}
.selectize-control.as-list .item {
display: block;
}