Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend
This commit is contained in:
@@ -33,9 +33,13 @@ class I18n extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
public function actionRead($params, $data, $request)
|
||||
{
|
||||
if ($request->get('default')) {
|
||||
$default = $request->get('default') === 'true';
|
||||
|
||||
return $this->getServiceFactory()->create('Language')->getDataForFrontend($default);
|
||||
|
||||
/*if ($request->get('default')) {
|
||||
return $this->getContainer()->get('defaultLanguage')->getAll();
|
||||
}
|
||||
return $this->getContainer()->get('language')->getAll();
|
||||
return $this->getContainer()->get('language')->getAll();*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,11 +39,7 @@ class Layout extends \Espo\Core\Controllers\Base
|
||||
{
|
||||
public function actionRead($params, $data)
|
||||
{
|
||||
$data = $this->getContainer()->get('layout')->get($params['scope'], $params['name']);
|
||||
if (empty($data)) {
|
||||
throw new NotFound("Layout " . $params['scope'] . ":" . $params['name'] . ' is not found.');
|
||||
}
|
||||
return $data;
|
||||
return $this->getServiceFactory()->create('Layout')->getForFrontend($params['scope'], $params['name']);
|
||||
}
|
||||
|
||||
public function actionUpdate($params, $data, $request)
|
||||
|
||||
@@ -36,7 +36,7 @@ class Metadata extends \Espo\Core\Controllers\Base
|
||||
|
||||
public function actionRead($params, $data)
|
||||
{
|
||||
return $this->getMetadata()->getAllForFrontend();
|
||||
return $this->getServiceFactory()->create('Metadata')->getDataForFrontend();
|
||||
}
|
||||
|
||||
public function getActionGet($params, $data, $request)
|
||||
|
||||
@@ -97,7 +97,21 @@ class SumRelatedType extends \Espo\Core\Formula\Functions\Base
|
||||
|
||||
$selectParams['select'] = [[$foreignLink . '.id', 'foreignId'], 'SUM:' . $field];
|
||||
|
||||
$foreignSelectManager->addJoin($foreignLink, $selectParams);
|
||||
if ($entity->getRelationType($link) === 'hasChildren') {
|
||||
$foreignSelectManager->addJoin([
|
||||
$entity->getEntityType(),
|
||||
$foreignLink,
|
||||
[
|
||||
$foreignLink . '.id:' => $foreignLink . 'Id',
|
||||
'deleted' => false,
|
||||
$foreignLink . '.id!=' => null,
|
||||
]
|
||||
], $selectParams);
|
||||
$selectParams['whereClause'][] = [$foreignLink . 'Type' => $entity->getEntityType()];
|
||||
|
||||
} else {
|
||||
$foreignSelectManager->addJoin($foreignLink, $selectParams);
|
||||
}
|
||||
|
||||
$selectParams['groupBy'] = [$foreignLink . '.id'];
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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\Core\Formula\Functions\PasswordGroup;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
|
||||
class GenerateType extends \Espo\Core\Formula\Functions\Base
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
$this->addDependency('config');
|
||||
}
|
||||
|
||||
public function process(\StdClass $item)
|
||||
{
|
||||
$config = $this->getInjection('config');
|
||||
|
||||
$length = $config->get('passwordGenerateLength', 10);
|
||||
$letterCount = $config->get('passwordGenerateLetterCount', 4);
|
||||
$numberCount = $config->get('passwordGenerateNumberCount', 2);
|
||||
|
||||
$password = \Espo\Core\Utils\Util::generatePassword($length, $letterCount, $numberCount, true);
|
||||
|
||||
return $password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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\Core\Formula\Functions\PasswordGroup;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
|
||||
class HashType extends \Espo\Core\Formula\Functions\Base
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
$this->addDependency('config');
|
||||
}
|
||||
|
||||
public function process(\StdClass $item)
|
||||
{
|
||||
$args = $item->value ?? [];
|
||||
|
||||
if (!is_array($args)) throw new Error();
|
||||
if (count($args) < 1)
|
||||
throw new Error("Formula: password\\hash: no argument.");
|
||||
|
||||
$password = $this->evaluate($args[0]);
|
||||
|
||||
if (!is_string($password))
|
||||
throw new Error("Formula: password\\hash: bad argument.");
|
||||
|
||||
$passwordHash = new \Espo\Core\Utils\PasswordHash($this->getInjection('config'));
|
||||
$hash = $passwordHash->hash($password);
|
||||
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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\Core\Loaders;
|
||||
|
||||
class Hasher extends Base
|
||||
{
|
||||
public function load()
|
||||
{
|
||||
return new \Espo\Core\Utils\Hasher(
|
||||
$this->getContainer()->get('config')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,13 @@
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Copyright (C] 2014-2019 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.
|
||||
* (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
|
||||
@@ -23,37 +23,39 @@
|
||||
* 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,
|
||||
* 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.
|
||||
************************************************************************/
|
||||
|
||||
return array(
|
||||
|
||||
'Subscription' => array(
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
return [
|
||||
'Subscription' => [
|
||||
'fields' => [
|
||||
'id' => [
|
||||
'type' => 'id',
|
||||
'dbType' => 'int',
|
||||
'len' => '11',
|
||||
'autoincrement' => true,
|
||||
),
|
||||
'entityId' => array(
|
||||
],
|
||||
'entityId' => [
|
||||
'type' => 'varchar',
|
||||
'len' => '24',
|
||||
'index' => 'entity',
|
||||
),
|
||||
'entityType' => array(
|
||||
],
|
||||
'entityType' => [
|
||||
'type' => 'varchar',
|
||||
'len' => '100',
|
||||
'index' => 'entity',
|
||||
),
|
||||
'userId' => array(
|
||||
],
|
||||
'userId' => [
|
||||
'type' => 'varchar',
|
||||
'len' => '24',
|
||||
'index' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
'userEntity' => [
|
||||
'columns' => ['userId', 'entityId', 'entityType'],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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\Core\Utils;
|
||||
|
||||
class Hasher
|
||||
{
|
||||
protected $config;
|
||||
|
||||
protected $secretKeyParam = 'hashSecretKey';
|
||||
|
||||
public function __construct(\Espo\Core\Utils\Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function hash(string $string) : string
|
||||
{
|
||||
$secretKey = $this->config->get($this->secretKeyParam) ?? '';
|
||||
|
||||
return md5(hash_hmac('sha256', $string, $secretKey, true));
|
||||
}
|
||||
}
|
||||
@@ -568,6 +568,14 @@ class Util
|
||||
return bin2hex(random_bytes(16));
|
||||
}
|
||||
|
||||
public static function generateSecretKey()
|
||||
{
|
||||
if (!function_exists('random_bytes')) {
|
||||
return self::generateId();
|
||||
}
|
||||
return bin2hex(random_bytes(16));
|
||||
}
|
||||
|
||||
public static function generateKey()
|
||||
{
|
||||
return md5(uniqid(rand(), true));
|
||||
|
||||
@@ -96,6 +96,7 @@ return [
|
||||
'passwordSalt',
|
||||
'cryptKey',
|
||||
'apiSecretKeys',
|
||||
'hashSecretKey',
|
||||
'restrictedMode',
|
||||
'userLimit',
|
||||
'portalUserLimit',
|
||||
|
||||
@@ -35,11 +35,11 @@ class Stream extends \Espo\Core\Hooks\Base
|
||||
{
|
||||
protected $streamService = null;
|
||||
|
||||
protected $auditedFieldsCache = array();
|
||||
protected $auditedFieldsCache = [];
|
||||
|
||||
protected $hasStreamCache = array();
|
||||
protected $hasStreamCache = [];
|
||||
|
||||
protected $isLinkObservableInStreamCache = array();
|
||||
protected $isLinkObservableInStreamCache = [];
|
||||
|
||||
protected $statusFields = null;
|
||||
|
||||
|
||||
@@ -47,10 +47,19 @@ class SubscribeAgain extends \Espo\Core\EntryPoints\Base
|
||||
|
||||
public function run()
|
||||
{
|
||||
if (empty($_GET['id'])) {
|
||||
$id = $_GET['id'] ?? null;
|
||||
$emailAddress = $_GET['emailAddress'] ?? null;
|
||||
$hash = $_GET['hash'] ?? null;
|
||||
|
||||
if ($emailAddress && $hash) {
|
||||
$this->processWithHash($emailAddress, $hash);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$id) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$queueItemId = $_GET['id'];
|
||||
$queueItemId = $id;
|
||||
|
||||
$queueItem = $this->getEntityManager()->getEntity('EmailQueueItem', $queueItemId);
|
||||
|
||||
@@ -76,6 +85,10 @@ class SubscribeAgain extends \Espo\Core\EntryPoints\Base
|
||||
if ($targetType && $targetId) {
|
||||
$target = $this->getEntityManager()->getEntity($targetType, $targetId);
|
||||
|
||||
if (!$target) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
if ($massEmail->get('optOutEntirely')) {
|
||||
$emailAddress = $target->get('emailAddress');
|
||||
if ($emailAddress) {
|
||||
@@ -92,7 +105,7 @@ class SubscribeAgain extends \Espo\Core\EntryPoints\Base
|
||||
'Account' => 'accounts',
|
||||
'Contact' => 'contacts',
|
||||
'Lead' => 'leads',
|
||||
'User' => 'users'
|
||||
'User' => 'users',
|
||||
];
|
||||
if (!empty($m[$target->getEntityType()])) {
|
||||
$link = $m[$target->getEntityType()];
|
||||
@@ -114,22 +127,9 @@ class SubscribeAgain extends \Espo\Core\EntryPoints\Base
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'actionData' => [
|
||||
'queueItemId' => $queueItemId,
|
||||
],
|
||||
'view' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'subscribeView']),
|
||||
'template' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'subscribeTemplate']),
|
||||
];
|
||||
$this->getHookManager()->process($target->getEntityType(), 'afterCancelOptOut', $target, [], []);
|
||||
|
||||
$runScript = "
|
||||
Espo.require('crm:controllers/unsubscribe', function (Controller) {
|
||||
var controller = new Controller(app.baseController.params, app.getControllerInjection());
|
||||
controller.masterView = app.masterView;
|
||||
controller.doAction('subscribeAgain', ".json_encode($data).");
|
||||
});
|
||||
";
|
||||
$this->getClientManager()->display($runScript);
|
||||
$this->display(['queueItemId' => $queueItemId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,5 +147,56 @@ class SubscribeAgain extends \Espo\Core\EntryPoints\Base
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected function display(array $actionData)
|
||||
{
|
||||
$data = [
|
||||
'actionData' => $actionData,
|
||||
'view' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'subscribeView']),
|
||||
'template' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'subscribeTemplate']),
|
||||
];
|
||||
|
||||
$runScript = "
|
||||
Espo.require('crm:controllers/unsubscribe', function (Controller) {
|
||||
var controller = new Controller(app.baseController.params, app.getControllerInjection());
|
||||
controller.masterView = app.masterView;
|
||||
controller.doAction('subscribeAgain', ".json_encode($data).");
|
||||
});
|
||||
";
|
||||
$this->getClientManager()->display($runScript);
|
||||
}
|
||||
|
||||
protected function processWithHash(string $emailAddress, string $hash)
|
||||
{
|
||||
$secretKey = $this->getConfig()->get('hashSecretKey');
|
||||
|
||||
$hash2 = $this->getContainer()->get('hasher')->hash($emailAddress);
|
||||
|
||||
if ($hash2 !== $hash) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$repository = $this->getEntityManager()->getRepository('EmailAddress');
|
||||
|
||||
$ea = $repository->getByAddress($emailAddress);
|
||||
if ($ea) {
|
||||
$entityList = $repository->getEntityListByAddressId($ea->id);
|
||||
|
||||
if ($ea->get('optOut')) {
|
||||
$ea->set('optOut', false);
|
||||
$this->getEntityManager()->saveEntity($ea);
|
||||
|
||||
foreach ($entityList as $entity) {
|
||||
$this->getHookManager()->process($entity->getEntityType(), 'afterCancelOptOut', $entity, [], []);
|
||||
}
|
||||
}
|
||||
|
||||
$this->display([
|
||||
'emailAddress' => $emailAddress,
|
||||
'hash' => $hash,
|
||||
]);
|
||||
} else {
|
||||
throw new NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,19 @@ class Unsubscribe extends \Espo\Core\EntryPoints\Base
|
||||
|
||||
public function run()
|
||||
{
|
||||
if (empty($_GET['id'])) {
|
||||
$id = $_GET['id'] ?? null;
|
||||
$emailAddress = $_GET['emailAddress'] ?? null;
|
||||
$hash = $_GET['hash'] ?? null;
|
||||
|
||||
if ($emailAddress && $hash) {
|
||||
$this->processWithHash($emailAddress, $hash);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$id) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$queueItemId = $_GET['id'];
|
||||
$queueItemId = $id;
|
||||
|
||||
$queueItem = $this->getEntityManager()->getEntity('EmailQueueItem', $queueItemId);
|
||||
|
||||
@@ -76,6 +85,10 @@ class Unsubscribe extends \Espo\Core\EntryPoints\Base
|
||||
if ($targetType && $targetId) {
|
||||
$target = $this->getEntityManager()->getEntity($targetType, $targetId);
|
||||
|
||||
if (!$target) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
if ($massEmail->get('optOutEntirely')) {
|
||||
$emailAddress = $target->get('emailAddress');
|
||||
if ($emailAddress) {
|
||||
@@ -92,7 +105,7 @@ class Unsubscribe extends \Espo\Core\EntryPoints\Base
|
||||
'Account' => 'accounts',
|
||||
'Contact' => 'contacts',
|
||||
'Lead' => 'leads',
|
||||
'User' => 'users'
|
||||
'User' => 'users',
|
||||
];
|
||||
if (!empty($m[$target->getEntityType()])) {
|
||||
$link = $m[$target->getEntityType()];
|
||||
@@ -114,22 +127,9 @@ class Unsubscribe extends \Espo\Core\EntryPoints\Base
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'actionData' => [
|
||||
'queueItemId' => $queueItemId,
|
||||
],
|
||||
'view' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'unsubscribeView']),
|
||||
'template' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'unsubscribeTemplate']),
|
||||
];
|
||||
$this->getHookManager()->process($target->getEntityType(), 'afterOptOut', $target, [], []);
|
||||
|
||||
$runScript = "
|
||||
Espo.require('crm:controllers/unsubscribe', function (Controller) {
|
||||
var controller = new Controller(app.baseController.params, app.getControllerInjection());
|
||||
controller.masterView = app.masterView;
|
||||
controller.doAction('unsubscribe', ".json_encode($data).");
|
||||
});
|
||||
";
|
||||
$this->getClientManager()->display($runScript);
|
||||
$this->display(['queueItemId' => $queueItemId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,4 +140,56 @@ class Unsubscribe extends \Espo\Core\EntryPoints\Base
|
||||
$campaignService->logOptedOut($campaignId, $queueItemId, $target, $queueItem->get('emailAddress'), null, $queueItem->get('isTest'));
|
||||
}
|
||||
}
|
||||
|
||||
protected function display(array $actionData)
|
||||
{
|
||||
$data = [
|
||||
'actionData' => $actionData,
|
||||
'view' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'unsubscribeView']),
|
||||
'template' => $this->getMetadata()->get(['clientDefs', 'Campaign', 'unsubscribeTemplate']),
|
||||
];
|
||||
|
||||
$runScript = "
|
||||
Espo.require('crm:controllers/unsubscribe', function (Controller) {
|
||||
var controller = new Controller(app.baseController.params, app.getControllerInjection());
|
||||
controller.masterView = app.masterView;
|
||||
controller.doAction('unsubscribe', ".json_encode($data).");
|
||||
});
|
||||
";
|
||||
$this->getClientManager()->display($runScript);
|
||||
}
|
||||
|
||||
protected function processWithHash(string $emailAddress, string $hash)
|
||||
{
|
||||
$secretKey = $this->getConfig()->get('hashSecretKey');
|
||||
|
||||
$hash2 = $this->getContainer()->get('hasher')->hash($emailAddress);
|
||||
|
||||
if ($hash2 !== $hash) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$repository = $this->getEntityManager()->getRepository('EmailAddress');
|
||||
|
||||
$ea = $repository->getByAddress($emailAddress);
|
||||
if ($ea) {
|
||||
$entityList = $repository->getEntityListByAddressId($ea->id);
|
||||
|
||||
if (!$ea->get('optOut')) {
|
||||
$ea->set('optOut', true);
|
||||
$this->getEntityManager()->saveEntity($ea);
|
||||
|
||||
foreach ($entityList as $entity) {
|
||||
$this->getHookManager()->process($entity->getEntityType(), 'afterOptOut', $entity, [], []);
|
||||
}
|
||||
}
|
||||
|
||||
$this->display([
|
||||
'emailAddress' => $emailAddress,
|
||||
'hash' => $hash,
|
||||
]);
|
||||
} else {
|
||||
throw new NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,34 +7,6 @@
|
||||
"EmailQueueItem": false
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"scopeFieldLevel": {
|
||||
"KnowledgeBaseArticle": {
|
||||
"portals": false,
|
||||
"order": false,
|
||||
"status": false,
|
||||
"assignedUser": false
|
||||
},
|
||||
"Case": {
|
||||
"status": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
},
|
||||
"account": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
},
|
||||
"contacts": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
},
|
||||
"contact": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"strictDefault": {
|
||||
"scopeFieldLevel": {
|
||||
"KnowledgeBaseArticle": {
|
||||
@@ -43,22 +15,27 @@
|
||||
"status": false,
|
||||
"assignedUser": false
|
||||
},
|
||||
"Call": {
|
||||
"users": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
},
|
||||
"leads": false
|
||||
},
|
||||
"Meeting": {
|
||||
"users": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
},
|
||||
"leads": false
|
||||
},
|
||||
"KnowledgeBaseArticle": {
|
||||
"assignedUser": false
|
||||
},
|
||||
"Case": {
|
||||
"status": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
},
|
||||
"account": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
},
|
||||
"contacts": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
},
|
||||
"contact": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,18 +74,23 @@ class Portal extends \Espo\Core\ORM\Repositories\RDB
|
||||
}
|
||||
}
|
||||
|
||||
protected function afterSave(Entity $entity, array $options = array())
|
||||
protected function afterSave(Entity $entity, array $options = [])
|
||||
{
|
||||
parent::afterSave($entity, $options);
|
||||
|
||||
if ($entity->has('isDefault')) {
|
||||
if ($entity->get('isDefault')) {
|
||||
$this->getConfig()->set('defaultPortalId', $entity->id);
|
||||
$this->getConfig()->save();
|
||||
$defaultPortalId = $this->getConfig()->get('defaultPortalId');
|
||||
if ($defaultPortalId !== $entity->id) {
|
||||
$this->getConfig()->set('defaultPortalId', $entity->id);
|
||||
$this->getConfig()->save();
|
||||
}
|
||||
} else {
|
||||
if ($entity->isAttributeChanged('isDefault')) {
|
||||
$this->getConfig()->set('defaultPortalId', null);
|
||||
$this->getConfig()->save();
|
||||
if ($entity->getFetched('isDefault')) {
|
||||
$this->getConfig()->set('defaultPortalId', null);
|
||||
$this->getConfig()->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"label":"",
|
||||
"rows":[
|
||||
[{"name":"name"}, {"name": "status"}],
|
||||
[{"name":"queue"}, {"name":"number"}]
|
||||
]
|
||||
},
|
||||
{
|
||||
"label":"",
|
||||
"rows":[
|
||||
[{"name":"executeTime"}, {"name": "createdAt"}],
|
||||
[{"name":"startedAt"}, {"name": "modifiedAt"}],
|
||||
[{"name":"executedAt"}, false],
|
||||
[{"name":"attempts"}, false],
|
||||
[{"name":"failedAttempts"}, false]
|
||||
]
|
||||
},
|
||||
{
|
||||
"label":"",
|
||||
"rows":[
|
||||
[{"name":"scheduledJob"}, {"name":"targetType"}],
|
||||
[{"name":"serviceName"}, {"name":"targetId"}],
|
||||
[{"name":"methodName"}, {"name":"job"}],
|
||||
[{"name": "data", "fullWidth": true}]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -2,8 +2,7 @@
|
||||
{
|
||||
"label": "In-app Notifications",
|
||||
"rows": [
|
||||
[{"name": "assignmentNotificationsEntityList"}, false],
|
||||
[{"name": "notificationSoundsDisabled"}, false]
|
||||
[{"name": "assignmentNotificationsEntityList"}, false]
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -105,6 +105,9 @@
|
||||
"read": "own",
|
||||
"edit": "no"
|
||||
},
|
||||
"Team": {
|
||||
"read": "team"
|
||||
},
|
||||
"Import": false,
|
||||
"Webhook": false
|
||||
},
|
||||
|
||||
@@ -100,8 +100,11 @@
|
||||
"type": false,
|
||||
"contact": false,
|
||||
"accounts": false,
|
||||
"account": false,
|
||||
"portalRoles": false,
|
||||
"portals": false,
|
||||
"roles": false,
|
||||
"defaultTeam": false,
|
||||
"isActive": false
|
||||
}
|
||||
}
|
||||
@@ -121,23 +124,6 @@
|
||||
"teams": false
|
||||
},
|
||||
"scopeFieldLevel": {
|
||||
"Call": {
|
||||
"users": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
},
|
||||
"leads": false
|
||||
},
|
||||
"Meeting": {
|
||||
"users": {
|
||||
"read": "yes",
|
||||
"edit": "no"
|
||||
},
|
||||
"leads": false
|
||||
},
|
||||
"KnowledgeBaseArticle": {
|
||||
"assignedUser": false
|
||||
},
|
||||
"User": {
|
||||
"gender": false
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
"internal": true,
|
||||
"nonAdminReadOnly": true
|
||||
},
|
||||
"authLogRecordId": {
|
||||
"internal": true
|
||||
},
|
||||
"authMethod": {
|
||||
"onlyAdmin": true
|
||||
},
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
"collection": {
|
||||
"orderBy": "number",
|
||||
"order": "desc",
|
||||
"textFilterFields": ["name", "methodName", "serviceName", "scheduledJob"]
|
||||
"textFilterFields": ["id", "name", "methodName", "serviceName", "scheduledJob"]
|
||||
},
|
||||
"indexes": {
|
||||
"executeTime": {
|
||||
|
||||
@@ -155,6 +155,9 @@
|
||||
"parentAndSuperParent": {
|
||||
"type": "index",
|
||||
"columns": ["parentId", "parentType", "superParentId", "superParentType"]
|
||||
},
|
||||
"createdByNumber": {
|
||||
"columns": ["createdById", "number"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +85,7 @@
|
||||
},
|
||||
"dashboardLayout": {
|
||||
"type": "jsonArray",
|
||||
"view": "views/settings/fields/dashboard-layout",
|
||||
"required": true
|
||||
"view": "views/settings/fields/dashboard-layout"
|
||||
},
|
||||
"dashletsOptions": {
|
||||
"type": "jsonObject",
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"entity": true,
|
||||
"layouts": false,
|
||||
"tab": false,
|
||||
"acl": false,
|
||||
"customizable": false
|
||||
}
|
||||
@@ -1 +1,7 @@
|
||||
{"entity":true,"layouts":false,"tab":false,"acl":false,"customizable":false}
|
||||
{
|
||||
"entity": true,
|
||||
"layouts": false,
|
||||
"tab": false,
|
||||
"acl": false,
|
||||
"customizable": false
|
||||
}
|
||||
|
||||
@@ -52,8 +52,10 @@ class Email extends \Espo\Core\SelectManagers\Base
|
||||
break;
|
||||
} else {
|
||||
if (isset($item['attribute'])) {
|
||||
$skipIndex = true;
|
||||
break;
|
||||
if (!in_array($item['attribute'], ['teams', 'users', 'status'])) {
|
||||
$skipIndex = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,9 +63,6 @@ class Email extends \Espo\Core\SelectManagers\Base
|
||||
if ($folderId === 'important' || $folderId === 'drafts') {
|
||||
$skipIndex = true;
|
||||
}
|
||||
if (!$skipIndex && $this->hasLinkJoined('teams', $result)) {
|
||||
$skipIndex = true;
|
||||
}
|
||||
if (!$skipIndex) {
|
||||
$result['useIndexList'] = ['dateSent'];
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@ class Team extends \Espo\Core\SelectManagers\Base
|
||||
|
||||
protected function accessOnlyTeam(&$result)
|
||||
{
|
||||
$this->boolFilterOnlyMy($result);
|
||||
$this->setDistinct(true, $result);
|
||||
$this->addLeftJoin(['users', 'usersOnlyMyAccess'], $result);
|
||||
$result['whereClause'][] = [
|
||||
'usersOnlyMyAccessMiddle.userId' => $this->getUser()->id
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ class App extends \Espo\Core\Services\Base
|
||||
$settingsService = $this->getServiceFactory()->create('Settings');
|
||||
|
||||
$user = $this->getUser();
|
||||
|
||||
if (!$user->has('teamsIds')) {
|
||||
$user->loadLinkMultipleField('teams');
|
||||
}
|
||||
@@ -83,13 +84,6 @@ class App extends \Espo\Core\Services\Base
|
||||
$user->loadLinkMultipleField('accounts');
|
||||
}
|
||||
|
||||
$userData = $user->getValueMap();
|
||||
|
||||
$emailAddressData = $this->getEmailAddressData();
|
||||
|
||||
$userData->emailAddressList = $emailAddressData->emailAddressList;
|
||||
$userData->userEmailAddressList = $emailAddressData->userEmailAddressList;
|
||||
|
||||
$settings = $this->getServiceFactory()->create('Settings')->getConfigData();
|
||||
|
||||
if ($user->get('dashboardTemplateId')) {
|
||||
@@ -100,14 +94,11 @@ class App extends \Espo\Core\Services\Base
|
||||
}
|
||||
}
|
||||
|
||||
unset($userData->authTokenId);
|
||||
unset($userData->password);
|
||||
|
||||
$language = \Espo\Core\Utils\Language::detectLanguage($this->getConfig(), $this->getPreferences());
|
||||
|
||||
return [
|
||||
'user' => $userData,
|
||||
'acl' => $this->getAcl()->getMap(),
|
||||
'user' => $this->getUserDataForFrontend(),
|
||||
'acl' => $this->getAclDataForFrontend(),
|
||||
'preferences' => $preferencesData,
|
||||
'token' => $this->getUser()->get('token'),
|
||||
'settings' => $settings,
|
||||
@@ -116,11 +107,63 @@ class App extends \Espo\Core\Services\Base
|
||||
'maxUploadSize' => $this->getMaxUploadSize() / 1024.0 / 1024.0,
|
||||
'templateEntityTypeList' => $this->getTemplateEntityTypeList(),
|
||||
'isRestrictedMode' => $this->getConfig()->get('restrictedMode'),
|
||||
'passwordChangeForNonAdminDisabled' => $this->getConfig()->get('authenticationMethod', 'Espo') !== 'Espo'
|
||||
'passwordChangeForNonAdminDisabled' => $this->getConfig()->get('authenticationMethod', 'Espo') !== 'Espo',
|
||||
'timeZoneList' => $this->getMetadata()->get(['entityDefs', 'Settings', 'fields', 'timeZone', 'options'], []),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
protected function getUserDataForFrontend()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$emailAddressData = $this->getEmailAddressData();
|
||||
|
||||
$data = $user->getValueMap();
|
||||
|
||||
$data->emailAddressList = $emailAddressData->emailAddressList;
|
||||
$data->userEmailAddressList = $emailAddressData->userEmailAddressList;
|
||||
|
||||
unset($data->authTokenId);
|
||||
unset($data->password);
|
||||
|
||||
$forbiddenAttributeList = $this->getAcl()->getScopeForbiddenAttributeList('User');
|
||||
|
||||
$isPortal = $user->isPortal();
|
||||
|
||||
foreach ($forbiddenAttributeList as $attribute) {
|
||||
if ($attribute === 'type') continue;
|
||||
if ($isPortal) {
|
||||
if (in_array($attribute, ['contactId', 'contactName', 'accountId', 'accountsIds'])) continue;
|
||||
} else {
|
||||
if (in_array($attribute, ['teamsIds', 'defaultTeamId', 'defaultTeamName'])) continue;
|
||||
}
|
||||
unset($data->$attribute);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function getAclDataForFrontend()
|
||||
{
|
||||
$data = $this->getAcl()->getMap();
|
||||
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
$data = unserialize(serialize($data));
|
||||
|
||||
$scopeList = array_keys($this->getMetadata()->get(['scopes'], []));
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!$this->getAcl()->check($scope)) {
|
||||
unset($data->table->$scope);
|
||||
unset($data->fieldTable->$scope);
|
||||
unset($data->fieldTableQuickAccess->$scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function getEmailAddressData()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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\Services;
|
||||
|
||||
class Language extends \Espo\Core\Services\Base
|
||||
{
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$this->addDependency('container');
|
||||
$this->addDependency('metadata');
|
||||
$this->addDependency('acl');
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->getInjection('metadata');
|
||||
}
|
||||
|
||||
protected function getAcl()
|
||||
{
|
||||
return $this->getInjection('acl');
|
||||
}
|
||||
|
||||
protected function getDefaultLanguage()
|
||||
{
|
||||
return $this->getInjection('container')->get('defaultLanguage');
|
||||
}
|
||||
|
||||
protected function getLanguage()
|
||||
{
|
||||
return $this->getInjection('container')->get('language');
|
||||
}
|
||||
|
||||
public function getDataForFrontend(bool $default = false)
|
||||
{
|
||||
if ($default) {
|
||||
$languageObj = $this->getDefaultLanguage();
|
||||
} else {
|
||||
$languageObj = $this->getLanguage();
|
||||
}
|
||||
$data = $languageObj->getAll();
|
||||
|
||||
if ($this->getUser()->isSystem()) {
|
||||
unset($data['Global']['scopeNames']);
|
||||
unset($data['Global']['scopeNamesPlural']);
|
||||
unset($data['Global']['dashlets']);
|
||||
unset($data['Global']['links']);
|
||||
unset($data['Global']['fields']);
|
||||
unset($data['Global']['options']);
|
||||
|
||||
foreach ($data as $k => $item) {
|
||||
if (in_array($k, ['Global', 'User', 'Campaign'])) continue;
|
||||
unset($data[$k]);
|
||||
}
|
||||
unset($data['User']['fields']);
|
||||
unset($data['User']['links']);
|
||||
unset($data['User']['options']);
|
||||
unset($data['User']['filters']);
|
||||
unset($data['User']['presetFilters']);
|
||||
unset($data['User']['boolFilters']);
|
||||
unset($data['User']['tooltips']);
|
||||
|
||||
unset($data['Campaign']['fields']);
|
||||
unset($data['Campaign']['links']);
|
||||
unset($data['Campaign']['options']);
|
||||
unset($data['Campaign']['tooltips']);
|
||||
unset($data['Campaign']['presetFilters']);
|
||||
} else {
|
||||
$scopeList = array_keys($this->getMetadata()->get(['scopes'], []));
|
||||
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!$this->getAcl()->check($scope)) {
|
||||
unset($data[$scope]);
|
||||
unset($data['Global']['scopeNames'][$scope]);
|
||||
unset($data['Global']['scopeNamesPlural'][$scope]);
|
||||
} else {
|
||||
foreach ($this->getAcl()->getScopeForbiddenFieldList($scope) as $field) {
|
||||
if (isset($data[$scope]['fields'])) unset($data[$scope]['fields'][$field]);
|
||||
if (isset($data[$scope]['options'])) unset($data[$scope]['options'][$field]);
|
||||
if (isset($data[$scope]['links'])) unset($data[$scope]['links'][$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
unset($data['Admin']);
|
||||
unset($data['LayoutManager']);
|
||||
unset($data['EntityManager']);
|
||||
unset($data['FieldManager']);
|
||||
unset($data['Settings']);
|
||||
unset($data['ApiUser']);
|
||||
unset($data['DynamicLogic']);
|
||||
|
||||
$data['Settings'] = [
|
||||
'options' => [
|
||||
'weekStart' => $languageObj->get(['Settings', 'options', 'weekStart']),
|
||||
],
|
||||
];
|
||||
$data['Admin'] = [
|
||||
'messages' => [
|
||||
'userHasNoEmailAddress' => $languageObj->translate('userHasNoEmailAddress', 'messages', 'Admin'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$data['User']['fields']['password'] = $languageObj->translate('password', 'fields', 'User');
|
||||
$data['User']['fields']['passwordConfirm'] = $languageObj->translate('passwordConfirm', 'fields', 'User');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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\Services;
|
||||
|
||||
class Layout extends \Espo\Core\Services\Base
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
$this->addDependency('acl');
|
||||
$this->addDependency('layout');
|
||||
$this->addDependency('metadata');
|
||||
}
|
||||
|
||||
protected function getAcl()
|
||||
{
|
||||
return $this->getInjection('acl');
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->getInjection('metadata');
|
||||
}
|
||||
|
||||
public function getForFrontend(string $scope, string $name)
|
||||
{
|
||||
$dataString = $this->getInjection('layout')->get($scope, $name);
|
||||
|
||||
if (!$dataString) {
|
||||
throw new NotFound("Layout {$scope}:{$scope} is not found.");
|
||||
}
|
||||
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
if ($name === 'relationships') {
|
||||
$data = json_decode($dataString);
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $i => $link) {
|
||||
$foreignEntityType = $this->getMetadata()->get(['entityDefs', $scope, 'links', $link, 'entity']);
|
||||
if ($foreignEntityType) {
|
||||
if (!$this->getAcl()->check($foreignEntityType)) {
|
||||
unset($data[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$data = array_values($data);
|
||||
$dataString = json_encode($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $dataString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2019 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\Services;
|
||||
|
||||
class Metadata extends \Espo\Core\Services\Base
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
$this->addDependency('metadata');
|
||||
$this->addDependency('acl');
|
||||
}
|
||||
|
||||
protected function getMetadata()
|
||||
{
|
||||
return $this->getInjection('metadata');
|
||||
}
|
||||
|
||||
protected function getAcl()
|
||||
{
|
||||
return $this->getInjection('acl');
|
||||
}
|
||||
|
||||
protected function getDefaultLanguage()
|
||||
{
|
||||
return $this->getInjection('container')->get('defaultLanguage');
|
||||
}
|
||||
|
||||
protected function getLanguage()
|
||||
{
|
||||
return $this->getInjection('container')->get('language');
|
||||
}
|
||||
|
||||
public function getDataForFrontend()
|
||||
{
|
||||
$data = $this->getMetadata()->getAllForFrontend();
|
||||
|
||||
if (!$this->getUser()->isAdmin()) {
|
||||
$scopeList = array_keys($this->getMetadata()->get(['scopes'], []));
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!$this->getAcl()->check($scope)) {
|
||||
unset($data->entityDefs->$scope);
|
||||
unset($data->clientDefs->$scope);
|
||||
unset($data->entityAcl->$scope);
|
||||
unset($data->scopes->$scope);
|
||||
}
|
||||
}
|
||||
|
||||
$entityTypeList = array_keys(get_object_vars($data->entityDefs));
|
||||
foreach ($entityTypeList as $entityType) {
|
||||
$linksDefs = $this->getMetadata()->get(['entityDefs', $entityType, 'links'], []);
|
||||
|
||||
$fobiddenFieldList = $this->getAcl()->getScopeForbiddenFieldList($entityType);
|
||||
|
||||
foreach ($linksDefs as $link => $defs) {
|
||||
$type = $defs['type'] ?? null;
|
||||
|
||||
$hasField = !!$this->getMetadata()->get(['entityDefs', $entityType, 'fields', $link]);
|
||||
|
||||
if ($type === 'belongsToParent') {
|
||||
if ($hasField) {
|
||||
$parentEntityList = $this->getMetadata()->get(['entityDefs', $entityType, 'fields', $link, 'entityList']);
|
||||
if (is_array($parentEntityList)) {
|
||||
foreach ($parentEntityList as $i => $e) {
|
||||
if (!$this->getAcl()->check($e)) {
|
||||
unset($parentEntityList[$i]);
|
||||
}
|
||||
}
|
||||
$parentEntityList = array_values($parentEntityList);
|
||||
$data->entityDefs->$entityType->fields->$link->entityList = $parentEntityList;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$foreignEntityType = $defs['entity'] ?? null;
|
||||
if ($this->getAcl()->check($foreignEntityType)) continue;
|
||||
|
||||
if ($this->getUser()->isPortal()) {
|
||||
if ($foreignEntityType === 'Account' || $foreignEntityType === 'Contact') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasField) {
|
||||
if (!in_array($link, $fobiddenFieldList)) {
|
||||
continue;
|
||||
}
|
||||
unset($data->entityDefs->$entityType->fields->$link);
|
||||
}
|
||||
|
||||
unset($data->entityDefs->$entityType->links->$link);
|
||||
|
||||
if (
|
||||
isset($data->clientDefs)
|
||||
&&
|
||||
isset($data->clientDefs->$entityType)
|
||||
&&
|
||||
isset($data->clientDefs->$entityType->relationshipPanels)
|
||||
) {
|
||||
unset($data->clientDefs->$entityType->relationshipPanels->$link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unset($data->entityDefs->Settings);
|
||||
|
||||
$dashletList = array_keys($this->getMetadata()->get(['dashlets'], []));
|
||||
|
||||
foreach ($dashletList as $item) {
|
||||
$aclScope = $this->getMetadata()->get(['dashlets', $item, 'aclScope']);
|
||||
if ($aclScope && !$this->getAcl()->check($aclScope)) {
|
||||
unset($data->dashlets->$item);
|
||||
}
|
||||
}
|
||||
|
||||
unset($data->authenticationMethods);
|
||||
unset($data->formula);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,13 @@ class Portal extends Record
|
||||
{
|
||||
protected $getEntityBeforeUpdate = true;
|
||||
|
||||
protected function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->addDependency('fileManager');
|
||||
$this->addDependency('dataManager');
|
||||
}
|
||||
|
||||
public function loadAdditionalFields(Entity $entity)
|
||||
{
|
||||
parent::loadAdditionalFields($entity);
|
||||
@@ -50,11 +57,20 @@ class Portal extends Record
|
||||
protected function afterUpdateEntity(Entity $entity, $data)
|
||||
{
|
||||
$this->loadUrlField($entity);
|
||||
|
||||
if (property_exists($data, 'portalRolesIds')) {
|
||||
$this->clearRolesCache();
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadUrlField(Entity $entity)
|
||||
{
|
||||
$this->getRepository()->loadUrlField($entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected function clearRolesCache()
|
||||
{
|
||||
$this->getInjection('fileManager')->removeInDir('data/cache/application/acl-portal');
|
||||
$this->getInjection('dataManager')->updateCacheTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ class PortalRole extends Record
|
||||
{
|
||||
parent::init();
|
||||
$this->addDependency('fileManager');
|
||||
$this->addDependency('dataManager');
|
||||
}
|
||||
|
||||
protected $forceSelectAllAttributes = true;
|
||||
@@ -56,6 +57,6 @@ class PortalRole extends Record
|
||||
protected function clearRolesCache()
|
||||
{
|
||||
$this->getInjection('fileManager')->removeInDir('data/cache/application/acl-portal');
|
||||
$this->getInjection('dataManager')->updateCacheTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -558,7 +558,6 @@ class Record extends \Espo\Core\Services\Base
|
||||
$mandatoryValidationList = $this->getMetadata()->get(['fields', $fieldType, 'mandatoryValidationList'], []);
|
||||
$fieldValidatorManager = $this->getInjection('container')->get('fieldValidatorManager');
|
||||
|
||||
|
||||
foreach ($validationList as $type) {
|
||||
$value = $this->getFieldManagerUtil()->getEntityTypeFieldParam($this->entityType, $field, $type);
|
||||
if (is_null($value)) {
|
||||
@@ -570,7 +569,7 @@ class Record extends \Espo\Core\Services\Base
|
||||
$skipPropertyName = 'validate' . ucfirst($type) . 'SkipFieldList';
|
||||
if (property_exists($this, $skipPropertyName)) {
|
||||
$skipList = $this->$skipPropertyName;
|
||||
if (!in_array($type, $skipList)) {
|
||||
if (in_array($type, $skipList)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1857,6 +1856,7 @@ class Record extends \Espo\Core\Services\Base
|
||||
public function follow($id, $userId = null)
|
||||
{
|
||||
$entity = $this->getRepository()->get($id);
|
||||
if (!$entity) throw new NotFoundSilent();
|
||||
|
||||
if (!$this->getAcl()->check($entity, 'stream')) {
|
||||
throw new Forbidden();
|
||||
@@ -1872,6 +1872,7 @@ class Record extends \Espo\Core\Services\Base
|
||||
public function unfollow($id, $userId = null)
|
||||
{
|
||||
$entity = $this->getRepository()->get($id);
|
||||
if (!$entity) throw new NotFoundSilent();
|
||||
|
||||
if (empty($userId)) {
|
||||
$userId = $this->getUser()->id;
|
||||
|
||||
@@ -37,6 +37,7 @@ class Role extends Record
|
||||
{
|
||||
parent::init();
|
||||
$this->addDependency('fileManager');
|
||||
$this->addDependency('dataManager');
|
||||
}
|
||||
|
||||
protected $forceSelectAllAttributes = true;
|
||||
@@ -56,5 +57,6 @@ class Role extends Record
|
||||
protected function clearRolesCache()
|
||||
{
|
||||
$this->getInjection('fileManager')->removeInDir('data/cache/application/acl');
|
||||
$this->getInjection('dataManager')->updateCacheTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,10 +111,44 @@ class Settings extends \Espo\Core\Services\Base
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->getUser()->isAdmin() && !$this->getUser()->isSystem()) {
|
||||
$entityTypeListParamList = [
|
||||
'tabList',
|
||||
'quickCreateList',
|
||||
'globalSearchEntityList',
|
||||
'assignmentEmailNotificationsEntityList',
|
||||
'assignmentNotificationsEntityList',
|
||||
'calendarEntityList',
|
||||
'disabledCountQueryEntityList',
|
||||
'streamEmailNotificationsEntityList',
|
||||
'activitiesEntityList',
|
||||
'historyEntityList',
|
||||
'streamEmailNotificationsTypeList',
|
||||
'emailKeepParentTeamsEntityList',
|
||||
];
|
||||
$scopeList = array_keys($this->getMetadata()->get(['entityDefs'], []));
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!$this->getMetadata()->get(['scopes', $scope, 'acl'])) continue;
|
||||
if (!$this->getAcl()->check($scope)) {
|
||||
foreach ($entityTypeListParamList as $param) {
|
||||
$list = $data->$param ?? [];
|
||||
foreach ($list as $i => $item) {
|
||||
if ($item === $scope) {
|
||||
unset($list[$i]);
|
||||
}
|
||||
}
|
||||
$list = array_values($list);
|
||||
|
||||
$data->$param = $list;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
($this->getConfig()->get('smtpServer') || $this->getConfig()->get('internalSmtpServer'))
|
||||
&&
|
||||
!$this->getConfig()->geT('passwordRecoveryDisabled')
|
||||
!$this->getConfig()->get('passwordRecoveryDisabled')
|
||||
) {
|
||||
$data->passwordRecoveryEnabled = true;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ class Stream extends \Espo\Core\Services\Base
|
||||
|
||||
protected $emailsWithContentEntityList = ['Case'];
|
||||
|
||||
protected $auditedFieldsCache = array();
|
||||
protected $auditedFieldsCache = [];
|
||||
|
||||
private $notificationService = null;
|
||||
|
||||
@@ -106,7 +106,7 @@ class Stream extends \Espo\Core\Services\Base
|
||||
protected function getStatusStyles()
|
||||
{
|
||||
if (empty($this->statusStyles)) {
|
||||
$this->statusStyles = $this->getMetadata()->get('entityDefs.Note.statusStyles', array());
|
||||
$this->statusStyles = $this->getMetadata()->get('entityDefs.Note.statusStyles', []);
|
||||
}
|
||||
return $this->statusStyles;
|
||||
}
|
||||
@@ -115,7 +115,7 @@ class Stream extends \Espo\Core\Services\Base
|
||||
{
|
||||
if (is_null($this->statusFields)) {
|
||||
$this->statusFields = array();
|
||||
$scopes = $this->getMetadata()->get('scopes', array());
|
||||
$scopes = $this->getMetadata()->get('scopes', []);
|
||||
foreach ($scopes as $scope => $data) {
|
||||
if (empty($data['statusField'])) continue;
|
||||
$this->statusFields[$scope] = $data['statusField'];
|
||||
@@ -143,7 +143,8 @@ class Stream extends \Espo\Core\Services\Base
|
||||
|
||||
foreach ($userIdList as $i => $userId) {
|
||||
$user = $this->getEntityManager()->getEntity('User', $userId);
|
||||
if (!$user){
|
||||
if (!$user) {
|
||||
unset($userIdList[$i]);
|
||||
continue;
|
||||
}
|
||||
if (!$this->getAclManager()->check($user, $entity, 'stream')) {
|
||||
@@ -165,10 +166,10 @@ class Stream extends \Espo\Core\Services\Base
|
||||
|
||||
$this->followEntityMass($entity, $userIdList);
|
||||
|
||||
$noteList = $this->getEntityManager()->getRepository('Note')->where(array(
|
||||
$noteList = $this->getEntityManager()->getRepository('Note')->where([
|
||||
'parentType' => $entityType,
|
||||
'parentId' => $entityId
|
||||
))->order('number', 'ASC')->find();
|
||||
])->order('number', 'ASC')->find();
|
||||
|
||||
foreach ($noteList as $note) {
|
||||
$this->getNotificationService()->notifyAboutNote($userIdList, $note);
|
||||
@@ -197,9 +198,9 @@ class Stream extends \Espo\Core\Services\Base
|
||||
return false;
|
||||
}
|
||||
|
||||
public function followEntityMass(Entity $entity, array $sourceUserIdList)
|
||||
public function followEntityMass(Entity $entity, array $sourceUserIdList, bool $skipAclCheck = false)
|
||||
{
|
||||
if (!$this->getMetadata()->get('scopes.' . $entity->getEntityName() . '.stream')) {
|
||||
if (!$this->getMetadata()->get(['scopes', $entity->getEntityType(), 'stream'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -213,6 +214,27 @@ class Stream extends \Espo\Core\Services\Base
|
||||
|
||||
$userIdList = array_unique($userIdList);
|
||||
|
||||
if (!$skipAclCheck) {
|
||||
foreach ($userIdList as $i => $userId) {
|
||||
$user = $this->getEntityManager()->getRepository('User')
|
||||
->select(['id', 'type', 'isActive'])
|
||||
->where([
|
||||
'id' => $userId,
|
||||
'isActive' => true,
|
||||
])->findOne();
|
||||
|
||||
if (!$user) {
|
||||
unset($userIdList[$i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->getAclManager()->check($user, $entity, 'stream')) {
|
||||
unset($userIdList[$i]);
|
||||
}
|
||||
}
|
||||
$userIdList = array_values($userIdList);
|
||||
}
|
||||
|
||||
if (empty($userIdList)) {
|
||||
return;
|
||||
}
|
||||
@@ -364,6 +386,7 @@ class Stream extends \Espo\Core\Services\Base
|
||||
'orderBy' => 'number',
|
||||
'order' => 'DESC',
|
||||
'limit' => $sqLimit,
|
||||
'useIndexList' => ['createdByNumber'],
|
||||
];
|
||||
|
||||
if ($user->isPortal()) {
|
||||
@@ -461,6 +484,7 @@ class Stream extends \Espo\Core\Services\Base
|
||||
'orderBy' => 'number',
|
||||
'order' => 'DESC',
|
||||
'limit' => $sqLimit,
|
||||
'useIndexList' => ['createdByNumber'],
|
||||
];
|
||||
|
||||
if ($user->isPortal()) {
|
||||
@@ -1638,17 +1662,30 @@ class Stream extends \Espo\Core\Services\Base
|
||||
{
|
||||
$ignoreScopeList = [];
|
||||
$scopes = $this->getMetadata()->get('scopes', []);
|
||||
|
||||
$aclManager = $this->getAclManager();
|
||||
|
||||
if ($user->isPortal() && !$this->getUser()->isPortal()) {
|
||||
$aclManager = new \Espo\Core\Portal\AclManager($this->getInjection('container'));
|
||||
|
||||
$portals = $user->get('portals');
|
||||
if (count($portals)) {
|
||||
$aclManager->setPortal($portals[0]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($scopes as $scope => $item) {
|
||||
if (empty($item['entity'])) continue;
|
||||
if (empty($item['object'])) continue;
|
||||
if (
|
||||
!$this->getAclManager()->checkScope($user, $scope, 'read')
|
||||
!$aclManager->checkScope($user, $scope, 'read')
|
||||
||
|
||||
!$this->getAclManager()->checkScope($user, $scope, 'stream')
|
||||
!$aclManager->checkScope($user, $scope, 'stream')
|
||||
) {
|
||||
$ignoreScopeList[] = $scope;
|
||||
}
|
||||
}
|
||||
|
||||
return $ignoreScopeList;
|
||||
}
|
||||
|
||||
|
||||
@@ -751,10 +751,28 @@ class User extends Record
|
||||
{
|
||||
parent::afterUpdateEntity($entity, $data);
|
||||
|
||||
if (property_exists($data, 'rolesIds') || property_exists($data, 'teamsIds') || property_exists($data, 'type')) {
|
||||
if (
|
||||
property_exists($data, 'rolesIds') ||
|
||||
property_exists($data, 'teamsIds') ||
|
||||
property_exists($data, 'type') ||
|
||||
property_exists($data, 'portalRolesIds') ||
|
||||
property_exists($data, 'portalsIds')
|
||||
) {
|
||||
$this->clearRoleCache($entity->id);
|
||||
}
|
||||
|
||||
if (
|
||||
property_exists($data, 'portalRolesIds')
|
||||
||
|
||||
property_exists($data, 'portalsIds')
|
||||
||
|
||||
property_exists($data, 'contactId')
|
||||
||
|
||||
property_exists($data, 'accountsIds')
|
||||
) {
|
||||
$this->clearPortalRolesCache();
|
||||
}
|
||||
|
||||
if ($entity->isPortal() && $entity->get('contactId')) {
|
||||
if (property_exists($data, 'firstName') || property_exists($data, 'lastName') || property_exists($data, 'salutationName')) {
|
||||
$contact = $this->getEntityManager()->getEntity('Contact', $entity->get('contactId'));
|
||||
@@ -775,6 +793,12 @@ class User extends Record
|
||||
protected function clearRoleCache($id)
|
||||
{
|
||||
$this->getFileManager()->removeFile('data/cache/application/acl/' . $id . '.php');
|
||||
$this->getContainer()->get('dataManager')->updateCacheTimestamp();
|
||||
}
|
||||
|
||||
protected function clearPortalRolesCache()
|
||||
{
|
||||
$this->getInjection('fileManager')->removeInDir('data/cache/application/acl-portal');
|
||||
}
|
||||
|
||||
public function massUpdate(array $params, $data)
|
||||
@@ -792,11 +816,29 @@ class User extends Record
|
||||
{
|
||||
parent::afterMassUpdate($idList, $data);
|
||||
|
||||
if (array_key_exists('rolesIds', $data) || array_key_exists('teamsIds', $data) || array_key_exists('type', $data)) {
|
||||
if (
|
||||
property_exists($data, 'rolesIds') ||
|
||||
property_exists($data, 'teamsIds') ||
|
||||
property_exists($data, 'type') ||
|
||||
property_exists($data, 'portalRolesIds') ||
|
||||
property_exists($data, 'portalsIds')
|
||||
) {
|
||||
foreach ($idList as $id) {
|
||||
$this->clearRoleCache($id);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
property_exists($data, 'portalRolesIds')
|
||||
||
|
||||
property_exists($data, 'portalsIds')
|
||||
||
|
||||
property_exists($data, 'contactId')
|
||||
||
|
||||
property_exists($data, 'accountsIds')
|
||||
) {
|
||||
$this->clearPortalRolesCache();
|
||||
}
|
||||
}
|
||||
|
||||
public function loadAdditionalFields(Entity $entity)
|
||||
|
||||
Vendored
+44
-42
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Bootstrap Colorpicker v2.5.1
|
||||
* Bootstrap Colorpicker v2.5.2
|
||||
* https://itsjavi.com/bootstrap-colorpicker/
|
||||
*
|
||||
* Originally written by (c) 2012 Stefan Petre
|
||||
@@ -38,14 +38,9 @@
|
||||
val, predefinedColors, fallbackColor, fallbackFormat, hexNumberSignPrefix) {
|
||||
this.fallbackValue = fallbackColor ?
|
||||
(
|
||||
fallbackColor && (typeof fallbackColor.h !== 'undefined') ?
|
||||
fallbackColor :
|
||||
this.value = {
|
||||
h: 0,
|
||||
s: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
}
|
||||
(typeof fallbackColor === 'string') ?
|
||||
this.parse(fallbackColor) :
|
||||
fallbackColor
|
||||
) :
|
||||
null;
|
||||
|
||||
@@ -480,6 +475,9 @@
|
||||
* @returns {Object} Object containing h,s,b,a,format properties or FALSE if failed to parse
|
||||
*/
|
||||
parse: function(strVal) {
|
||||
if (typeof strVal !== 'string') {
|
||||
return this.fallbackValue;
|
||||
}
|
||||
if (arguments.length === 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -793,6 +791,8 @@
|
||||
this.updateData(this.color);
|
||||
}
|
||||
|
||||
this.disabled = false;
|
||||
|
||||
// Setup picker
|
||||
var $picker = this.picker = $(this.options.template);
|
||||
if (this.options.customClass) {
|
||||
@@ -861,7 +861,7 @@
|
||||
'keyup.colorpicker': $.proxy(this.keyup, this)
|
||||
});
|
||||
this.input.on({
|
||||
'change.colorpicker': $.proxy(this.change, this)
|
||||
'input.colorpicker': $.proxy(this.change, this)
|
||||
});
|
||||
if (this.component === false) {
|
||||
this.element.on({
|
||||
@@ -1105,34 +1105,31 @@
|
||||
return (this.input !== false);
|
||||
},
|
||||
isDisabled: function() {
|
||||
if (this.hasInput()) {
|
||||
return (this.input.prop('disabled') === true);
|
||||
}
|
||||
return false;
|
||||
return this.disabled;
|
||||
},
|
||||
disable: function() {
|
||||
if (this.hasInput()) {
|
||||
this.input.prop('disabled', true);
|
||||
this.element.trigger({
|
||||
type: 'disable',
|
||||
color: this.color,
|
||||
value: this.getValue()
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
this.disabled = true;
|
||||
this.element.trigger({
|
||||
type: 'disable',
|
||||
color: this.color,
|
||||
value: this.getValue()
|
||||
});
|
||||
return true;
|
||||
},
|
||||
enable: function() {
|
||||
if (this.hasInput()) {
|
||||
this.input.prop('disabled', false);
|
||||
this.element.trigger({
|
||||
type: 'enable',
|
||||
color: this.color,
|
||||
value: this.getValue()
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
this.disabled = false;
|
||||
this.element.trigger({
|
||||
type: 'enable',
|
||||
color: this.color,
|
||||
value: this.getValue()
|
||||
});
|
||||
return true;
|
||||
},
|
||||
currentSlider: null,
|
||||
mousePointer: {
|
||||
@@ -1251,7 +1248,24 @@
|
||||
return false;
|
||||
},
|
||||
change: function(e) {
|
||||
this.keyup(e);
|
||||
this.color = this.createColor(this.input.val());
|
||||
// Change format dynamically
|
||||
// Only occurs if user choose the dynamic format by
|
||||
// setting option format to false
|
||||
if (this.color.origFormat && this.options.format === false) {
|
||||
this.format = this.color.origFormat;
|
||||
}
|
||||
if (this.getValue(false) !== false) {
|
||||
this.updateData();
|
||||
this.updateComponent();
|
||||
this.updatePicker();
|
||||
}
|
||||
|
||||
this.element.trigger({
|
||||
type: 'changeColor',
|
||||
color: this.color,
|
||||
value: this.input.val()
|
||||
});
|
||||
},
|
||||
keyup: function(e) {
|
||||
if ((e.keyCode === 38)) {
|
||||
@@ -1264,20 +1278,8 @@
|
||||
this.color.value.a = Math.round((this.color.value.a - 0.01) * 100) / 100;
|
||||
}
|
||||
this.update(true);
|
||||
} else {
|
||||
this.color = this.createColor(this.input.val());
|
||||
// Change format dynamically
|
||||
// Only occurs if user choose the dynamic format by
|
||||
// setting option format to false
|
||||
if (this.color.origFormat && this.options.format === false) {
|
||||
this.format = this.color.origFormat;
|
||||
}
|
||||
if (this.getValue(false) !== false) {
|
||||
this.updateData();
|
||||
this.updateComponent();
|
||||
this.updatePicker();
|
||||
}
|
||||
}
|
||||
|
||||
this.element.trigger({
|
||||
type: 'changeColor',
|
||||
color: this.color,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
{{translate 'subscribedAgain' category='messages' scope='Campaign'}}
|
||||
</p>
|
||||
<p>
|
||||
<a class="btn btn-default btn-sm" href="?entryPoint=unsubscribe&id={{actionData.queueItemId}}">{{translate 'Unsubscribe again' scope='Campaign'}}</a>
|
||||
<a class="btn btn-default btn-sm" href="{{revertUrl}}">{{translate 'Unsubscribe again' scope='Campaign'}}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
{{translate 'unsubscribed' category='messages' scope='Campaign'}}
|
||||
</p>
|
||||
<p>
|
||||
<a class="btn btn-default btn-sm" href="?entryPoint=subscribeAgain&id={{actionData.queueItemId}}">{{translate 'Subscribe again' scope='Campaign'}}</a>
|
||||
<a class="btn btn-default btn-sm" href="{{revertUrl}}">{{translate 'Subscribe again' scope='Campaign'}}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -149,6 +149,13 @@ Espo.define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], functi
|
||||
this.enabledScopeList = [];
|
||||
}
|
||||
|
||||
this.enabledScopeList.forEach(function (item) {
|
||||
var color = this.getMetadata().get(['clientDefs', item, 'color']);
|
||||
if (color) {
|
||||
this.colors[item] = color;
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (this.header) {
|
||||
this.createView('modeButtons', 'crm:views/calendar/mode-buttons', {
|
||||
el: this.getSelector() + ' .mode-buttons',
|
||||
|
||||
@@ -170,6 +170,13 @@ Espo.define('crm:views/calendar/timeline', ['view', 'lib!vis'], function (Dep, V
|
||||
this.enabledScopeList = [];
|
||||
}
|
||||
|
||||
this.enabledScopeList.forEach(function (item) {
|
||||
var color = this.getMetadata().get(['clientDefs', item, 'color']);
|
||||
if (color) {
|
||||
this.colors[item] = color;
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (this.options.calendarType) {
|
||||
this.calendarType = this.options.calendarType;
|
||||
} else {
|
||||
|
||||
@@ -26,15 +26,23 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('crm:views/campaign/subscribe-again', 'view', function (Dep) {
|
||||
define('crm:views/campaign/subscribe-again', 'view', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
template: 'crm:campaign/subscribe-again',
|
||||
|
||||
data: function () {
|
||||
var revertUrl;
|
||||
|
||||
var actionData = this.options.actionData;
|
||||
if (actionData.hash && actionData.emailAddress)
|
||||
revertUrl = '?entryPoint=unsubscribe&emailAddress=' + actionData.emailAddress + '&hash=' + actionData.hash;
|
||||
else
|
||||
revertUrl = '?entryPoint=unsubscribe&id=' + actionData.queueItemId;
|
||||
|
||||
var data = {
|
||||
actionData: this.options.actionData
|
||||
revertUrl: revertUrl,
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -26,15 +26,23 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('crm:views/campaign/unsubscribe', 'view', function (Dep) {
|
||||
define('crm:views/campaign/unsubscribe', 'view', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
template: 'crm:campaign/unsubscribe',
|
||||
|
||||
data: function () {
|
||||
var revertUrl;
|
||||
|
||||
var actionData = this.options.actionData;
|
||||
if (actionData.hash && actionData.emailAddress)
|
||||
revertUrl = '?entryPoint=subscribeAgain&emailAddress=' + actionData.emailAddress + '&hash=' + actionData.hash;
|
||||
else
|
||||
revertUrl = '?entryPoint=subscribeAgain&id=' + actionData.queueItemId;
|
||||
|
||||
var data = {
|
||||
actionData: this.options.actionData
|
||||
revertUrl: revertUrl,
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('crm:views/mass-email/fields/smtp-account', 'views/fields/enum', function (Dep) {
|
||||
define('crm:views/mass-email/fields/smtp-account', 'views/fields/enum', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -36,6 +36,15 @@ Espo.define('crm:views/mass-email/fields/smtp-account', 'views/fields/enum', fun
|
||||
return [this.name, 'inboundEmailId'];
|
||||
},
|
||||
|
||||
data: function () {
|
||||
var data = Dep.prototype.data.call(this);
|
||||
|
||||
data.valueIsSet = true;
|
||||
data.isNotEmpty = true;
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
setupOptions: function () {
|
||||
Dep.prototype.setupOptions.call(this);
|
||||
|
||||
@@ -118,7 +127,7 @@ Espo.define('crm:views/mass-email/fields/smtp-account', 'views/fields/enum', fun
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
{{#if isEmpty}}
|
||||
{{translate 'None'}}
|
||||
{{/if}}
|
||||
|
||||
<div class="button-container clearfix">
|
||||
{{#ifNotEqual dashboardLayout.length 1}}
|
||||
<div class="btn-group pull-right dashboard-tabs">
|
||||
|
||||
@@ -57,7 +57,7 @@ define('acl-portal', ['acl'], function (Dep) {
|
||||
return true;
|
||||
}
|
||||
if (data === null) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
action = action || null;
|
||||
|
||||
+2
-2
@@ -62,13 +62,13 @@ define('acl', [], function () {
|
||||
return true;
|
||||
}
|
||||
if (data === null) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
action = action || null;
|
||||
|
||||
if (action === null) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
if (!(action in data)) {
|
||||
return false;
|
||||
|
||||
+1
-1
@@ -225,7 +225,7 @@ define(
|
||||
this.fieldManager.defs = this.metadata.get('fields');
|
||||
this.fieldManager.metadata = this.metadata;
|
||||
|
||||
this.settings.defs = this.metadata.get('entityDefs.Settings');
|
||||
this.settings.defs = this.metadata.get('entityDefs.Settings') || {};
|
||||
this.user.defs = this.metadata.get('entityDefs.User');
|
||||
this.preferences.defs = this.metadata.get('entityDefs.Preferences');
|
||||
this.viewHelper.layoutManager.userId = this.user.id;
|
||||
|
||||
+4
-4
@@ -178,14 +178,14 @@ define('model', [], function () {
|
||||
},
|
||||
|
||||
getFieldType: function (field) {
|
||||
if (('defs' in this) && ('fields' in this.defs) && (field in this.defs.fields)) {
|
||||
if (this.defs && this.defs.fields && (field in this.defs.fields)) {
|
||||
return this.defs.fields[field].type || null;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getFieldParam: function (field, param) {
|
||||
if (('defs' in this) && ('fields' in this.defs) && (field in this.defs.fields)) {
|
||||
if (this.defs && this.defs.fields && (field in this.defs.fields)) {
|
||||
if (param in this.defs.fields[field]) {
|
||||
return this.defs.fields[field][param];
|
||||
}
|
||||
@@ -194,14 +194,14 @@ define('model', [], function () {
|
||||
},
|
||||
|
||||
getLinkType: function (link) {
|
||||
if (('defs' in this) && ('links' in this.defs) && (link in this.defs.links)) {
|
||||
if (this.defs && this.defs.links && (link in this.defs.links)) {
|
||||
return this.defs.links[link].type || null;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getLinkParam: function (link, param) {
|
||||
if (('defs' in this) && ('links' in this.defs) && (link in this.defs.links)) {
|
||||
if (this.defs && this.defs.links && (link in this.defs.links)) {
|
||||
if (param in this.defs.links[link]) {
|
||||
return this.defs.links[link][param];
|
||||
}
|
||||
|
||||
+11
-1
@@ -77,6 +77,10 @@ define('ui', [], function () {
|
||||
|
||||
this.id = 'dialog-' + Math.floor((Math.random() * 100000));
|
||||
|
||||
if (typeof this.backdrop === 'undefined') {
|
||||
this.backdrop = 'static';
|
||||
}
|
||||
|
||||
this.contents = '';
|
||||
if (this.header) {
|
||||
var headerClassName = '';
|
||||
@@ -340,13 +344,19 @@ define('ui', [], function () {
|
||||
Dialog: Dialog,
|
||||
|
||||
confirm: function (message, o, callback, context) {
|
||||
o = o || {};
|
||||
var confirmText = o.confirmText;
|
||||
var cancelText = o.cancelText;
|
||||
var confirmStyle = o.confirmStyle || 'danger';
|
||||
|
||||
var backdrop = o.backdrop;
|
||||
if (typeof backdrop === 'undefined') {
|
||||
backdrop = false;
|
||||
}
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
var dialog = new Dialog({
|
||||
backdrop: ('backdrop' in o) ? o.backdrop : false,
|
||||
backdrop: backdrop,
|
||||
header: false,
|
||||
className: 'dialog-confirm',
|
||||
body: '<span class="confirm-message">' + message + '</a>',
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/admin/job/fields/name', 'views/fields/varchar', function (Dep) {
|
||||
define('views/admin/job/fields/name', 'views/fields/varchar', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
getValueForDisplay: function () {
|
||||
if (this.mode == 'list' || this.mode == 'detail') {
|
||||
if (this.mode == 'list' || this.mode == 'detail' || this.mode === 'listLink') {
|
||||
if (!this.model.get('name')) {
|
||||
return this.model.get('serviceName') + ': ' + this.model.get('methodName');
|
||||
} else {
|
||||
@@ -42,4 +42,3 @@ Espo.define('views/admin/job/fields/name', 'views/fields/varchar', function (Dep
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -246,6 +246,11 @@ define('views/dashboard', ['view', 'lib!gridstack'], function (Dep, Gridstack) {
|
||||
|
||||
this.currentTabLayout.forEach(function (o) {
|
||||
if (!o.id || !o.name) return;
|
||||
|
||||
if (!this.getMetadata().get(['dashlets', o.name])) {
|
||||
console.error("Dashlet " + o.name + " doesn't exist or not available.");
|
||||
return;
|
||||
}
|
||||
this.createDashletView(o.id, o.name);
|
||||
}, this);
|
||||
|
||||
@@ -323,6 +328,9 @@ define('views/dashboard', ['view', 'lib!gridstack'], function (Dep, Gridstack) {
|
||||
|
||||
this.currentTabLayout.forEach(function (o) {
|
||||
var $item = this.prepareGridstackItem(o.id, o.name);
|
||||
if (!this.getMetadata().get(['dashlets', o.name])) {
|
||||
return;
|
||||
}
|
||||
grid.addWidget($item, o.x, o.y, o.width, o.height);
|
||||
}, this);
|
||||
|
||||
@@ -330,6 +338,10 @@ define('views/dashboard', ['view', 'lib!gridstack'], function (Dep, Gridstack) {
|
||||
|
||||
this.currentTabLayout.forEach(function (o) {
|
||||
if (!o.id || !o.name) return;
|
||||
if (!this.getMetadata().get(['dashlets', o.name])) {
|
||||
console.error("Dashlet " + o.name + " doesn't exist or not available.");
|
||||
return;
|
||||
}
|
||||
this.createDashletView(o.id, o.name);
|
||||
}, this);
|
||||
|
||||
|
||||
@@ -81,7 +81,8 @@ define('views/dashlet', 'view', function (Dep) {
|
||||
bodyView.trigger('resize');
|
||||
}, this);
|
||||
|
||||
var viewName = this.getMetadata().get('dashlets.' + this.name + '.view') || 'views/dashlets/' + Espo.Utils.camelCaseToHyphen(this.name);
|
||||
var viewName = this.getMetadata().get(['dashlets', this.name, 'view']) ||
|
||||
'views/dashlets/' + Espo.Utils.camelCaseToHyphen(this.name);
|
||||
|
||||
this.createView('body', viewName, {
|
||||
el: this.options.el + ' .dashlet-body',
|
||||
|
||||
@@ -119,6 +119,9 @@ define('views/fields/array', ['views/fields/base', 'lib!Selectize'], function (D
|
||||
this.setupTranslation();
|
||||
}
|
||||
|
||||
this.displayAsLabel = this.params.displayAsLabel || this.displayAsLabel;
|
||||
this.displayAsList = this.params.displayAsList || this.displayAsList;
|
||||
|
||||
if (this.params.isSorted && this.translatedOptions) {
|
||||
this.params.options = Espo.Utils.clone(this.params.options);
|
||||
this.params.options = this.params.options.sort(function (v1, v2) {
|
||||
@@ -390,7 +393,10 @@ define('views/fields/array', ['views/fields/base', 'lib!Selectize'], function (D
|
||||
}, this)
|
||||
|
||||
|
||||
if (this.params.displayAsLabel) {
|
||||
if (this.displayAsList) {
|
||||
if (!list.length) return '';
|
||||
return '<div>' + list.join('</div><div>') + '</div>';
|
||||
} else if (this.displayAsLabel) {
|
||||
return list.join(' ');
|
||||
} else {
|
||||
return list.join(', ')
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/fields/colorpicker', ['views/fields/varchar', 'lib!Colorpicker'], function (Dep, Colorpicker) {
|
||||
define('views/fields/colorpicker', ['views/fields/varchar', 'lib!Colorpicker'], function (Dep, Colorpicker) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -46,10 +46,18 @@ Espo.define('views/fields/colorpicker', ['views/fields/varchar', 'lib!Colorpicke
|
||||
|
||||
afterRender: function () {
|
||||
Dep.prototype.afterRender.call(this);
|
||||
|
||||
if (this.mode == 'edit') {
|
||||
var isModal = !!this.$el.closest('.modal').length;
|
||||
|
||||
this.$element.parent().colorpicker({
|
||||
format: 'hex'
|
||||
format: 'hex',
|
||||
container: isModal ? this.$el : false,
|
||||
});
|
||||
|
||||
if (isModal) {
|
||||
this.$el.find('.colorpicker').css('position', 'relative').addClass('pull-right');
|
||||
}
|
||||
}
|
||||
if (this.mode === 'edit') {
|
||||
this.$element.on('change', function () {
|
||||
|
||||
@@ -80,7 +80,7 @@ define('views/modal', 'view', function (Dep) {
|
||||
|
||||
this.options = this.options || {};
|
||||
|
||||
this.backdrop = this.options.backdrop;
|
||||
this.backdrop = this.options.backdrop || this.backdrop;
|
||||
|
||||
this.setSelector(this.containerSelector);
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ define('views/notification/badge', 'view', function (Dep) {
|
||||
setup: function () {
|
||||
this.soundPath = this.getBasePath() + (this.getConfig().get('notificationSound') || this.soundPath);
|
||||
|
||||
this.notificationSoundsDisabled = this.getConfig().get('notificationSoundsDisabled');
|
||||
this.notificationSoundsDisabled = true;
|
||||
|
||||
this.useWebSocket = this.getConfig().get('useWebSocket');
|
||||
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/preferences/fields/time-zone', 'views/fields/enum', function (Dep) {
|
||||
define('views/preferences/fields/time-zone', 'views/fields/enum', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
setupOptions: function () {
|
||||
this.params.options = Espo.Utils.clone(this.getConfig().getFieldParam('timeZone', 'options') || []);
|
||||
this.params.options = Espo.Utils.clone(this.getHelper().getAppParam('timeZoneList')) || [];
|
||||
this.params.options.unshift('');
|
||||
|
||||
this.translatedOptions = this.translatedOptions || {};
|
||||
@@ -39,5 +39,4 @@ Espo.define('views/preferences/fields/time-zone', 'views/fields/enum', function
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -134,6 +134,11 @@ define('views/preferences/record/edit', 'views/record/edit', function (Dep) {
|
||||
this.hideField('assignmentEmailNotificationsIgnoreEntityTypeList');
|
||||
} else {
|
||||
hideNotificationPanel = false;
|
||||
|
||||
this.controlAssignmentEmailNotificationsVisibility();
|
||||
this.listenTo(this.model, 'change:receiveAssignmentEmailNotifications', function () {
|
||||
this.controlAssignmentEmailNotificationsVisibility();
|
||||
}, this);
|
||||
}
|
||||
|
||||
if ((this.getConfig().get('assignmentEmailNotificationsEntityList') || []).length === 0) {
|
||||
@@ -233,6 +238,14 @@ define('views/preferences/record/edit', 'views/record/edit', function (Dep) {
|
||||
}
|
||||
},
|
||||
|
||||
controlAssignmentEmailNotificationsVisibility: function () {
|
||||
if (this.model.get('receiveAssignmentEmailNotifications')) {
|
||||
this.showField('assignmentEmailNotificationsIgnoreEntityTypeList');
|
||||
} else {
|
||||
this.hideField('assignmentEmailNotificationsIgnoreEntityTypeList');
|
||||
}
|
||||
},
|
||||
|
||||
actionReset: function () {
|
||||
this.confirm(this.translate('resetPreferencesConfirmation', 'messages'), function () {
|
||||
$.ajax({
|
||||
|
||||
@@ -66,7 +66,8 @@ Espo.define('views/settings/fields/dashboard-layout', ['views/fields/base', 'lib
|
||||
data: function () {
|
||||
return {
|
||||
dashboardLayout: this.dashboardLayout,
|
||||
currentTab: this.currentTab
|
||||
currentTab: this.currentTab,
|
||||
isEmpty: this.isEmpty(),
|
||||
};
|
||||
},
|
||||
|
||||
@@ -364,10 +365,37 @@ Espo.define('views/settings/fields/dashboard-layout', ['views/fields/base', 'lib
|
||||
return options[optionName];
|
||||
},
|
||||
|
||||
isEmpty: function () {
|
||||
var isEmpty = true
|
||||
|
||||
if (this.dashboardLayout && this.dashboardLayout.length) {
|
||||
this.dashboardLayout.forEach(function (item) {
|
||||
if (item.layout && item.layout.length) {
|
||||
isEmpty = false;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
return isEmpty;
|
||||
},
|
||||
|
||||
validateRequired: function () {
|
||||
if (!this.isRequired()) return;
|
||||
|
||||
if (this.isEmpty()) {
|
||||
var msg = this.translate('fieldIsRequired', 'messages').replace('{field}', this.getLabelText());
|
||||
this.showValidationMessage(msg);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
fetch: function () {
|
||||
var data = {};
|
||||
|
||||
if (!this.dashboardLayout || !this.dashboardLayout.length) {
|
||||
data[this.name] = null;
|
||||
data['dashletsOptions'] = {};
|
||||
return data;
|
||||
} else {
|
||||
data[this.name] = Espo.Utils.cloneDeep(this.dashboardLayout);
|
||||
}
|
||||
|
||||
@@ -206,6 +206,8 @@ define('views/site/navbar', 'view', function (Dep) {
|
||||
var scopes = this.getMetadata().get('scopes') || {};
|
||||
|
||||
this.tabList = tabList.filter(function (scope) {
|
||||
if (scope === '_delimiter_' || scope === 'Home') return true;
|
||||
if (!scopes[scope]) return false;
|
||||
if ((scopes[scope] || {}).disabled) return;
|
||||
if ((scopes[scope] || {}).acl) {
|
||||
return this.getAcl().check(scope);
|
||||
@@ -214,6 +216,7 @@ define('views/site/navbar', 'view', function (Dep) {
|
||||
}, this);
|
||||
|
||||
this.quickCreateList = this.getQuickCreateList().filter(function (scope) {
|
||||
if (!scopes[scope]) return false;
|
||||
if ((scopes[scope] || {}).disabled) return;
|
||||
if ((scopes[scope] || {}).acl) {
|
||||
return this.getAcl().check(scope, 'create');
|
||||
|
||||
@@ -2213,6 +2213,10 @@ h4.panel-title span.fas.color-icon {
|
||||
}
|
||||
|
||||
.record .middle {
|
||||
> .panel.hidden + .panel {
|
||||
border-top-width: @panel-border-width;
|
||||
}
|
||||
|
||||
margin-bottom: @line-height-computed;
|
||||
}
|
||||
|
||||
@@ -2256,7 +2260,6 @@ h4.panel-title span.fas.color-icon {
|
||||
|
||||
.bottom {
|
||||
.panel.hidden + .panel.sticked {
|
||||
margin-top: 0;
|
||||
border-top-width: @panel-border-width;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,13 +254,14 @@ class Installer
|
||||
$siteUrl = $this->getSystemHelper()->getBaseUrl();
|
||||
$databaseDefaults = $this->app->getContainer()->get('config')->get('database');
|
||||
|
||||
$data = array(
|
||||
$data = [
|
||||
'database' => array_merge($databaseDefaults, $database),
|
||||
'language' => $language,
|
||||
'siteUrl' => $siteUrl,
|
||||
'passwordSalt' => $this->getPasswordHash()->generateSalt(),
|
||||
'cryptKey' => $this->getContainer()->get('crypt')->generateKey()
|
||||
);
|
||||
'cryptKey' => $this->getContainer()->get('crypt')->generateKey(),
|
||||
'hashSecretKey' => \Espo\Core\Utils\Util::generateSecretKey(),
|
||||
];
|
||||
|
||||
$owner = $this->getFileManager()->getPermissionUtils()->getDefaultOwner(true);
|
||||
$group = $this->getFileManager()->getPermissionUtils()->getDefaultGroup(true);
|
||||
|
||||
@@ -296,4 +296,28 @@ class FormulaTest extends \tests\integration\Core\BaseTestCase
|
||||
$result = $fm->run($script, $contact);
|
||||
$this->assertEquals('1', $result);
|
||||
}
|
||||
|
||||
public function testPasswordGenerate()
|
||||
{
|
||||
$fm = $this->getContainer()->get('formulaManager');
|
||||
|
||||
$script = "password\\generate()";
|
||||
$result = $fm->run($script);
|
||||
$this->assertTrue(is_string($result));
|
||||
}
|
||||
|
||||
public function testPasswordHash()
|
||||
{
|
||||
$fm = $this->getContainer()->get('formulaManager');
|
||||
|
||||
$script1 = "password\\hash('1')";
|
||||
$result1 = $fm->run($script1);
|
||||
|
||||
$script2 = "password\\hash('2')";
|
||||
$result2 = $fm->run($script2);
|
||||
|
||||
$this->assertTrue(is_string($result1));
|
||||
|
||||
$this->assertTrue($result1 !== $result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user