Initial commit
This commit is contained in:
58
application/Espo/Tools/App/Api/GetAbout.php
Normal file
58
application/Espo/Tools/App/Api/GetAbout.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App\Api;
|
||||
|
||||
use Espo\Core\Api\Action;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
use Espo\Core\Api\ResponseComposer;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Resource\FileReader;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class GetAbout implements Action
|
||||
{
|
||||
public function __construct(
|
||||
private FileReader $fileReader,
|
||||
private Config\SystemConfig $systemConfig,
|
||||
) {}
|
||||
|
||||
public function process(Request $request): Response
|
||||
{
|
||||
$text = $this->fileReader->read('texts/about.md', FileReader\Params::create());
|
||||
|
||||
return ResponseComposer::json([
|
||||
'text' => $text,
|
||||
'version' => $this->systemConfig->getVersion(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
54
application/Espo/Tools/App/Api/GetUser.php
Normal file
54
application/Espo/Tools/App/Api/GetUser.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App\Api;
|
||||
|
||||
use Espo\Core\Api\Action;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
use Espo\Core\Api\ResponseComposer;
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Tools\App\AppService as Service;
|
||||
|
||||
/**
|
||||
* Gets user data.
|
||||
*/
|
||||
class GetUser implements Action
|
||||
{
|
||||
public function __construct(private InjectableFactory $injectableFactory) {}
|
||||
|
||||
public function process(Request $request): Response
|
||||
{
|
||||
$data = $this->injectableFactory
|
||||
->create(Service::class)
|
||||
->getUserData();
|
||||
|
||||
return ResponseComposer::json($data);
|
||||
}
|
||||
}
|
||||
67
application/Espo/Tools/App/Api/PostDestroyAuthToken.php
Normal file
67
application/Espo/Tools/App/Api/PostDestroyAuthToken.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App\Api;
|
||||
|
||||
use Espo\Core\Api\Action;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
use Espo\Core\Api\ResponseComposer;
|
||||
use Espo\Core\Authentication\AuthenticationFactory;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Utils\Json;
|
||||
|
||||
class PostDestroyAuthToken implements Action
|
||||
{
|
||||
public function __construct(private AuthenticationFactory $authenticationFactory) {}
|
||||
|
||||
public function process(Request $request): Response
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
$token = $data->token ?? null;
|
||||
|
||||
if (!$token || !is_string($token)) {
|
||||
throw new BadRequest("No `token`.");
|
||||
}
|
||||
|
||||
$authentication = $this->authenticationFactory->create();
|
||||
|
||||
$response = ResponseComposer::empty();
|
||||
|
||||
try {
|
||||
$authentication->destroyAuthToken($token, $request, $response);
|
||||
} catch (NotFound) {
|
||||
return $response->writeBody(Json::encode(false));
|
||||
}
|
||||
|
||||
return $response->writeBody(Json::encode(true));
|
||||
}
|
||||
}
|
||||
40
application/Espo/Tools/App/AppParam.php
Normal file
40
application/Espo/Tools/App/AppParam.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App;
|
||||
|
||||
/**
|
||||
* App parameter to be passed to the frontend.
|
||||
*
|
||||
* @see https://docs.espocrm.com/development/app-params/
|
||||
*/
|
||||
interface AppParam
|
||||
{
|
||||
public function get(): mixed;
|
||||
}
|
||||
515
application/Espo/Tools/App/AppService.php
Normal file
515
application/Espo/Tools/App/AppService.php
Normal file
@@ -0,0 +1,515 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App;
|
||||
|
||||
use Espo\Core\Authentication\Util\MethodProvider as AuthenticationMethodProvider;
|
||||
use Espo\Core\Mail\ConfigDataProvider as EmailConfigDataProvider;
|
||||
use Espo\Core\Name\Field;
|
||||
use Espo\Core\Name\Link;
|
||||
use Espo\Core\Utils\SystemUser;
|
||||
use Espo\Entities\DashboardTemplate;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\Entities\EmailAccount;
|
||||
use Espo\Entities\EmailAddress;
|
||||
use Espo\Entities\InboundEmail;
|
||||
use Espo\Entities\Settings;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Authentication\Logins\Espo;
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\FieldUtil;
|
||||
use Espo\Core\Utils\Language;
|
||||
use Espo\Core\Utils\Log;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Entities\Preferences;
|
||||
use Espo\ORM\EntityManager;
|
||||
use stdClass;
|
||||
use Throwable;
|
||||
|
||||
class AppService
|
||||
{
|
||||
/** @var string[] */
|
||||
private array $forbiddenUserAttributeList = [
|
||||
'apiKey',
|
||||
'authTokenId',
|
||||
'password',
|
||||
'rolesIds',
|
||||
'rolesNames',
|
||||
];
|
||||
|
||||
/** @var string[] */
|
||||
private array $allowedUserAttributeList = [
|
||||
'type',
|
||||
];
|
||||
|
||||
/** @var string[] */
|
||||
private array $allowedInternalUserAttributeList = [
|
||||
'teamsIds',
|
||||
'defaultTeamId',
|
||||
'defaultTeamName',
|
||||
];
|
||||
|
||||
/** @var string[] */
|
||||
private array $allowedPortalUserAttributeList = [
|
||||
'contactId',
|
||||
'contactName',
|
||||
'accountId',
|
||||
'accountsIds',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private Config $config,
|
||||
private EntityManager $entityManager,
|
||||
private Metadata $metadata,
|
||||
private Acl $acl,
|
||||
private InjectableFactory $injectableFactory,
|
||||
private SettingsService $settingsService,
|
||||
private User $user,
|
||||
private Preferences $preferences,
|
||||
private FieldUtil $fieldUtil,
|
||||
private Log $log,
|
||||
private AuthenticationMethodProvider $authenticationMethodProvider,
|
||||
private SystemUser $systemUser,
|
||||
private EmailConfigDataProvider $emailConfigDataProvider,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getUserData(): array
|
||||
{
|
||||
$preferencesData = $this->preferences->getValueMap();
|
||||
|
||||
$this->filterPreferencesData($preferencesData);
|
||||
|
||||
$user = $this->user;
|
||||
|
||||
if (!$user->has('teamsIds')) {
|
||||
$user->loadLinkMultipleField(Field::TEAMS);
|
||||
}
|
||||
|
||||
if ($user->isPortal()) {
|
||||
$user->loadAccountField();
|
||||
$user->loadLinkMultipleField('accounts');
|
||||
}
|
||||
|
||||
$settings = $this->settingsService->getConfigData();
|
||||
|
||||
$dashboardTemplateId = $user->get('dashboardTemplateId');
|
||||
|
||||
if ($dashboardTemplateId) {
|
||||
$dashboardTemplate = $this->entityManager
|
||||
->getEntityById(DashboardTemplate::ENTITY_TYPE, $dashboardTemplateId);
|
||||
|
||||
if ($dashboardTemplate) {
|
||||
$settings->forcedDashletsOptions = $dashboardTemplate->get('dashletsOptions') ?? (object) [];
|
||||
$settings->forcedDashboardLayout = $dashboardTemplate->get('layout') ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
$language = Language::detectLanguage($this->config, $this->preferences);
|
||||
|
||||
return [
|
||||
'user' => $this->getUserDataForFrontend(),
|
||||
'acl' => $this->getAclDataForFrontend(),
|
||||
'preferences' => $preferencesData,
|
||||
'token' => $this->user->get('token'),
|
||||
'settings' => $settings,
|
||||
'language' => $language,
|
||||
'appParams' => $this->getAppParams(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getAppParams(): array
|
||||
{
|
||||
$user = $this->user;
|
||||
|
||||
$auth2FARequired =
|
||||
$user->isRegular() &&
|
||||
$this->config->get('auth2FA') &&
|
||||
$this->config->get('auth2FAForced') &&
|
||||
!$user->get('auth2FA');
|
||||
|
||||
$authenticationMethod = $this->authenticationMethodProvider->get();
|
||||
|
||||
$passwordChangeForNonAdminDisabled = $authenticationMethod !== Espo::NAME;
|
||||
$logoutWait = (bool) $this->metadata->get(['authenticationMethods', $authenticationMethod, 'logoutClassName']);
|
||||
|
||||
$timeZoneList = $this->metadata
|
||||
->get(['entityDefs', Settings::ENTITY_TYPE, 'fields', 'timeZone', 'options']) ?? [];
|
||||
|
||||
$appParams = [
|
||||
'maxUploadSize' => $this->getMaxUploadSize() / 1024.0 / 1024.0,
|
||||
'isRestrictedMode' => $this->config->get('restrictedMode'),
|
||||
'passwordChangeForNonAdminDisabled' => $passwordChangeForNonAdminDisabled,
|
||||
'timeZoneList' => $timeZoneList,
|
||||
'auth2FARequired' => $auth2FARequired,
|
||||
'logoutWait' => $logoutWait,
|
||||
'systemUserId' => $this->systemUser->getId(),
|
||||
];
|
||||
|
||||
/** @var array<string, array<string, mixed>> $map */
|
||||
$map = $this->metadata->get(['app', 'appParams']) ?? [];
|
||||
|
||||
foreach ($map as $paramKey => $item) {
|
||||
/** @var ?class-string<AppParam> $className */
|
||||
$className = $item['className'] ?? null;
|
||||
|
||||
if (!$className) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var AppParam $obj */
|
||||
$obj = $this->injectableFactory->create($className);
|
||||
|
||||
$itemParams = $obj->get();
|
||||
} catch (Throwable $e) {
|
||||
$this->log->error("AppParam $paramKey: " . $e->getMessage(), ['exception' => $e]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$appParams[$paramKey] = $itemParams;
|
||||
}
|
||||
|
||||
return $appParams;
|
||||
}
|
||||
|
||||
private function getUserDataForFrontend(): stdClass
|
||||
{
|
||||
$user = $this->user;
|
||||
|
||||
$data = $user->getValueMap();
|
||||
|
||||
$emailAddressData = $this->getEmailAddressData();
|
||||
|
||||
$data->emailAddressList = $emailAddressData['emailAddressList'];
|
||||
$data->userEmailAddressList = $emailAddressData['userEmailAddressList'];
|
||||
$data->excludeFromReplyEmailAddressList = $emailAddressData['excludeFromReplyEmailAddressList'];
|
||||
|
||||
foreach ($this->forbiddenUserAttributeList as $attribute) {
|
||||
unset($data->$attribute);
|
||||
}
|
||||
|
||||
$forbiddenAttributeList = $this->acl->getScopeForbiddenAttributeList(User::ENTITY_TYPE);
|
||||
|
||||
$isPortal = $user->isPortal();
|
||||
|
||||
foreach ($forbiddenAttributeList as $attribute) {
|
||||
if (in_array($attribute, $this->allowedUserAttributeList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($isPortal && in_array($attribute, $this->allowedPortalUserAttributeList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$isPortal && in_array($attribute, $this->allowedInternalUserAttributeList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($data->$attribute);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function getAclDataForFrontend(): stdClass
|
||||
{
|
||||
$data = $this->acl->getMapData();
|
||||
|
||||
if (!$this->user->isAdmin()) {
|
||||
$data = unserialize(serialize($data));
|
||||
|
||||
/** @var string[] $scopeList */
|
||||
$scopeList = array_keys($this->metadata->get(['scopes'], []));
|
||||
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!$this->acl->check($scope)) {
|
||||
unset($data->table->$scope);
|
||||
unset($data->fieldTable->$scope);
|
||||
unset($data->fieldTableQuickAccess->$scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* emailAddressList: string[],
|
||||
* userEmailAddressList: string[],
|
||||
* excludeFromReplyEmailAddressList: string[],
|
||||
* }
|
||||
*/
|
||||
private function getEmailAddressData(): array
|
||||
{
|
||||
$user = $this->user;
|
||||
|
||||
$systemIsShared = $this->emailConfigDataProvider->isSystemOutboundAddressShared();
|
||||
$systemAddress = $this->emailConfigDataProvider->getSystemOutboundAddress();
|
||||
|
||||
$addressList = [];
|
||||
$userAddressList = [];
|
||||
|
||||
/** @var iterable<EmailAddress> $emailAddresses */
|
||||
$emailAddresses = $this->entityManager
|
||||
->getRelation($user, Link::EMAIL_ADDRESSES)
|
||||
->find();
|
||||
|
||||
foreach ($emailAddresses as $emailAddress) {
|
||||
if ($emailAddress->isInvalid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$userAddressList[] = $emailAddress->getAddress();
|
||||
|
||||
if ($user->getEmailAddress() === $emailAddress->getAddress()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$addressList[] = $emailAddress->getAddress();
|
||||
}
|
||||
|
||||
if ($user->getEmailAddress()) {
|
||||
array_unshift($addressList, $user->getEmailAddress());
|
||||
}
|
||||
|
||||
if (!$systemIsShared) {
|
||||
$addressList = $this->filterUserEmailAddressList($user, $addressList);
|
||||
}
|
||||
|
||||
$addressList = array_merge($addressList, $this->getUserGroupEmailAddressList($user));
|
||||
|
||||
if ($systemIsShared && $systemAddress) {
|
||||
$addressList[] = $systemAddress;
|
||||
}
|
||||
|
||||
$addressList = array_values(array_unique($addressList));
|
||||
|
||||
return [
|
||||
'emailAddressList' => $addressList,
|
||||
'userEmailAddressList' => $userAddressList,
|
||||
'excludeFromReplyEmailAddressList' => $this->getExcludeFromReplyAddressList(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $emailAddressList
|
||||
* @return string[]
|
||||
*/
|
||||
private function filterUserEmailAddressList(User $user, array $emailAddressList): array
|
||||
{
|
||||
$emailAccountCollection = $this->entityManager
|
||||
->getRDBRepositoryByClass(EmailAccount::class)
|
||||
->select([
|
||||
Attribute::ID,
|
||||
Field::EMAIL_ADDRESS,
|
||||
])
|
||||
->where([
|
||||
'assignedUserId' => $user->getId(),
|
||||
'useSmtp' => true,
|
||||
'status' => EmailAccount::STATUS_ACTIVE,
|
||||
])
|
||||
->find();
|
||||
|
||||
$inAccountList = array_map(
|
||||
fn (EmailAccount $e) => $e->getEmailAddress(),
|
||||
[...$emailAccountCollection]
|
||||
);
|
||||
|
||||
return array_values(array_filter(
|
||||
$emailAddressList,
|
||||
fn (string $item) => in_array($item, $inAccountList)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getUserGroupEmailAddressList(User $user): array
|
||||
{
|
||||
$groupEmailAccountPermission = $this->acl->getPermissionLevel(Acl\Permission::GROUP_EMAIL_ACCOUNT);
|
||||
|
||||
if (!$groupEmailAccountPermission || $groupEmailAccountPermission === Acl\Table::LEVEL_NO) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($groupEmailAccountPermission === Acl\Table::LEVEL_TEAM) {
|
||||
$teamIdList = $user->getLinkMultipleIdList(Field::TEAMS);
|
||||
|
||||
if (!count($teamIdList)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$inboundEmailList = $this->entityManager
|
||||
->getRDBRepositoryByClass(InboundEmail::class)
|
||||
->where([
|
||||
'status' => InboundEmail::STATUS_ACTIVE,
|
||||
'useSmtp' => true,
|
||||
'smtpIsShared' => true,
|
||||
'teamsMiddle.teamId' => $teamIdList,
|
||||
])
|
||||
->join(Field::TEAMS)
|
||||
->distinct()
|
||||
->find();
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach ($inboundEmailList as $inboundEmail) {
|
||||
if (!$inboundEmail->getEmailAddress()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$list[] = $inboundEmail->getEmailAddress();
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
if ($groupEmailAccountPermission === Acl\Table::LEVEL_ALL) {
|
||||
$inboundEmailList = $this->entityManager
|
||||
->getRDBRepositoryByClass(InboundEmail::class)
|
||||
->where([
|
||||
'status' => InboundEmail::STATUS_ACTIVE,
|
||||
'useSmtp' => true,
|
||||
'smtpIsShared' => true,
|
||||
])
|
||||
->find();
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach ($inboundEmailList as $inboundEmail) {
|
||||
if (!$inboundEmail->getEmailAddress()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$list[] = $inboundEmail->getEmailAddress();
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
private function getMaxUploadSize()
|
||||
{
|
||||
$maxSize = 0;
|
||||
|
||||
$postMaxSize = $this->convertPHPSizeToBytes(ini_get('post_max_size'));
|
||||
|
||||
if ($postMaxSize > 0) {
|
||||
$maxSize = $postMaxSize;
|
||||
}
|
||||
|
||||
return $maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|false $size
|
||||
* @return int
|
||||
*/
|
||||
private function convertPHPSizeToBytes($size)
|
||||
{
|
||||
if (is_numeric($size)) {
|
||||
return (int) $size;
|
||||
}
|
||||
|
||||
if ($size === false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$suffix = strtoupper(substr($size, -1));
|
||||
$value = (int) substr($size, 0, -1);
|
||||
|
||||
if ($suffix == 'P') {
|
||||
$value *= pow(1024, 5);
|
||||
} else if ($suffix == 'T') {
|
||||
$value *= pow(1024, 4);
|
||||
} else if ($suffix == 'G') {
|
||||
$value *= pow(1024, 3);
|
||||
} else if ($suffix == 'M') {
|
||||
$value *= pow(1024, 2);
|
||||
} elseif ($suffix == 'K') {
|
||||
$value *= 1024;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function filterPreferencesData(stdClass $data): void
|
||||
{
|
||||
$passwordFieldList = $this->fieldUtil->getFieldByTypeList(Preferences::ENTITY_TYPE, 'password');
|
||||
|
||||
foreach ($passwordFieldList as $field) {
|
||||
unset($data->$field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getExcludeFromReplyAddressList(): array
|
||||
{
|
||||
if (!$this->acl->checkScope(Email::ENTITY_TYPE, Acl\Table::ACTION_CREATE)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var iterable<InboundEmail> $accounts */
|
||||
$accounts = $this->entityManager
|
||||
->getRDBRepositoryByClass(InboundEmail::class)
|
||||
->select('emailAddress')
|
||||
->where(['excludeFromReply' => true])
|
||||
->find();
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
if (!$account->getEmailAddress()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$list[] = $account->getEmailAddress();
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
52
application/Espo/Tools/App/Jobs/ClearCache.php
Normal file
52
application/Espo/Tools/App/Jobs/ClearCache.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App\Jobs;
|
||||
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Job\JobDataLess;
|
||||
|
||||
class ClearCache implements JobDataLess
|
||||
{
|
||||
private DataManager $dataManager;
|
||||
|
||||
public function __construct(DataManager $dataManager)
|
||||
{
|
||||
$this->dataManager = $dataManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$this->dataManager->clearCache();
|
||||
}
|
||||
}
|
||||
52
application/Espo/Tools/App/Jobs/Rebuild.php
Normal file
52
application/Espo/Tools/App/Jobs/Rebuild.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App\Jobs;
|
||||
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Job\JobDataLess;
|
||||
|
||||
class Rebuild implements JobDataLess
|
||||
{
|
||||
private DataManager $dataManager;
|
||||
|
||||
public function __construct(DataManager $dataManager)
|
||||
{
|
||||
$this->dataManager = $dataManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$this->dataManager->rebuild();
|
||||
}
|
||||
}
|
||||
69
application/Espo/Tools/App/Language/AclDependencyItem.php
Normal file
69
application/Espo/Tools/App/Language/AclDependencyItem.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App\Language;
|
||||
|
||||
class AclDependencyItem
|
||||
{
|
||||
/**
|
||||
* @param ?string[] $anyScopeList
|
||||
*/
|
||||
public function __construct(
|
||||
private string $target,
|
||||
private ?array $anyScopeList,
|
||||
private ?string $scope,
|
||||
private ?string $field
|
||||
) {}
|
||||
|
||||
/**
|
||||
* A language path to be allowed if a user has access to a specific scope/field.
|
||||
*/
|
||||
public function getTarget(): string
|
||||
{
|
||||
return $this->target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?string[]
|
||||
*/
|
||||
public function getAnyScopeList(): ?array
|
||||
{
|
||||
return $this->anyScopeList;
|
||||
}
|
||||
|
||||
public function getScope(): ?string
|
||||
{
|
||||
return $this->scope;
|
||||
}
|
||||
|
||||
public function getField(): ?string
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
}
|
||||
216
application/Espo/Tools/App/Language/AclDependencyProvider.php
Normal file
216
application/Espo/Tools/App/Language/AclDependencyProvider.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App\Language;
|
||||
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Config\SystemConfig;
|
||||
use Espo\Core\Utils\DataCache;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Defs;
|
||||
|
||||
class AclDependencyProvider
|
||||
{
|
||||
private const CACHE_KEY = 'languageAclDependency';
|
||||
|
||||
/** @var string[] */
|
||||
private array $enumFieldTypeList = [
|
||||
FieldType::ENUM,
|
||||
FieldType::MULTI_ENUM,
|
||||
FieldType::ARRAY,
|
||||
FieldType::CHECKLIST,
|
||||
];
|
||||
|
||||
/** @var ?AclDependencyItem[] */
|
||||
private ?array $data = null;
|
||||
private bool $useCache;
|
||||
|
||||
public function __construct(
|
||||
private DataCache $dataCache,
|
||||
private Metadata $metadata,
|
||||
private Defs $ormDefs,
|
||||
SystemConfig $systemConfig,
|
||||
) {
|
||||
$this->useCache = $systemConfig->useCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AclDependencyItem[]
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
if ($this->data === null) {
|
||||
$this->data = $this->loadData();
|
||||
}
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AclDependencyItem[]
|
||||
*/
|
||||
private function loadData(): array
|
||||
{
|
||||
if ($this->useCache && $this->dataCache->has(self::CACHE_KEY)) {
|
||||
/** @var array<string, mixed>[] $raw */
|
||||
$raw = $this->dataCache->get(self::CACHE_KEY);
|
||||
|
||||
return $this->buildFromRaw($raw);
|
||||
}
|
||||
|
||||
return $this->buildData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AclDependencyItem[]
|
||||
*/
|
||||
private function buildData(): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach (($this->metadata->get(['app', 'language', 'aclDependencies']) ?? []) as $target => $item) {
|
||||
$anyScopeList = $item['anyScopeList'] ?? null;
|
||||
$scope = $item['scope'] ?? null;
|
||||
$field = $item['field'] ?? null;
|
||||
|
||||
$data[] = [
|
||||
'target' => $target,
|
||||
'anyScopeList' => $anyScopeList,
|
||||
'scope' => $scope,
|
||||
'field' => $field,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($this->ormDefs->getEntityList() as $entityDefs) {
|
||||
if (!$this->metadata->get(['scopes', $entityDefs->getName(), 'object'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($entityDefs->getFieldList() as $fieldDefs) {
|
||||
$item = $this->getDataFromField($entityDefs->getName(), $fieldDefs);
|
||||
|
||||
if ($item) {
|
||||
$data[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->useCache) {
|
||||
$this->dataCache->store(self::CACHE_KEY, $data);
|
||||
}
|
||||
|
||||
return $this->buildFromRaw($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?array<string, mixed>
|
||||
*/
|
||||
private function getDataFromField(string $entityType, Defs\FieldDefs $fieldDefs): ?array
|
||||
{
|
||||
if ($fieldDefs->getType() === FieldType::FOREIGN) {
|
||||
$refEntityType = $fieldDefs->getParam('link') ?
|
||||
$this->ormDefs
|
||||
->getEntity($entityType)
|
||||
->tryGetRelation($fieldDefs->getParam('link'))
|
||||
?->tryGetForeignEntityType() :
|
||||
null;
|
||||
|
||||
$refField = $fieldDefs->getParam('field');
|
||||
|
||||
if (!$refEntityType || !$refField) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$foreignFieldType = $this->ormDefs
|
||||
->tryGetEntity($refEntityType)
|
||||
?->tryGetField($refField)
|
||||
?->getType();
|
||||
|
||||
if (
|
||||
!in_array($foreignFieldType, [
|
||||
FieldType::ENUM,
|
||||
FieldType::MULTI_ENUM,
|
||||
FieldType::ARRAY,
|
||||
FieldType::CHECKLIST,
|
||||
])
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'target' => "$refEntityType.options.$refField",
|
||||
'anyScopeList' => null,
|
||||
'scope' => $entityType,
|
||||
'field' => $fieldDefs->getName(),
|
||||
];
|
||||
}
|
||||
|
||||
if (!in_array($fieldDefs->getType(), $this->enumFieldTypeList)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$optionsReference = $fieldDefs->getParam('optionsReference');
|
||||
|
||||
if (!$optionsReference || !str_contains($optionsReference, '.')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
[$refEntityType, $refField] = explode('.', $optionsReference);
|
||||
|
||||
$target = "$refEntityType.options.$refField";
|
||||
|
||||
return [
|
||||
'target' => $target,
|
||||
'anyScopeList' => null,
|
||||
'scope' => $entityType,
|
||||
'field' => $fieldDefs->getName(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed>[] $raw
|
||||
* @return AclDependencyItem[]
|
||||
*/
|
||||
private function buildFromRaw(array $raw): array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach ($raw as $rawItem) {
|
||||
$target = $rawItem['target'] ?? null;
|
||||
$anyScopeList = $rawItem['anyScopeList'] ?? null;
|
||||
$scope = $rawItem['scope'] ?? null;
|
||||
$field = $rawItem['field'] ?? null;
|
||||
|
||||
$list[] = new AclDependencyItem($target, $anyScopeList, $scope, $field);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
272
application/Espo/Tools/App/LanguageService.php
Normal file
272
application/Espo/Tools/App/LanguageService.php
Normal file
@@ -0,0 +1,272 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App;
|
||||
|
||||
use Espo\Core\Utils\Language as LanguageUtil;
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Container;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Tools\App\Language\AclDependencyProvider;
|
||||
|
||||
class LanguageService
|
||||
{
|
||||
public function __construct(
|
||||
private Metadata $metadata,
|
||||
private Acl $acl,
|
||||
private User $user,
|
||||
private AclDependencyProvider $aclDependencyProvider,
|
||||
private Container $container
|
||||
) {}
|
||||
|
||||
// @todo Use proxy.
|
||||
protected function getDefaultLanguage(): LanguageUtil
|
||||
{
|
||||
/** @var LanguageUtil */
|
||||
return $this->container->get('defaultLanguage');
|
||||
}
|
||||
|
||||
protected function getLanguage(): LanguageUtil
|
||||
{
|
||||
/** @var LanguageUtil */
|
||||
return $this->container->get('language');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getDataForFrontendFromLanguage(LanguageUtil $language): array
|
||||
{
|
||||
$data = $language->getAll();
|
||||
|
||||
if ($this->user->isSystem()) {
|
||||
unset($data['Global']['scopeNames']);
|
||||
unset($data['Global']['scopeNamesPlural']);
|
||||
unset($data['Global']['dashlets']);
|
||||
unset($data['Global']['links']);
|
||||
|
||||
foreach ($data as $k => $item) {
|
||||
if (
|
||||
in_array($k, ['Global', 'User', 'Campaign']) ||
|
||||
$this->metadata->get(['scopes', $k, 'languageIsGlobal'])
|
||||
) {
|
||||
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 if (!$this->user->isAdmin()) {
|
||||
/** @var string[] $scopeList */
|
||||
$scopeList = array_keys($this->metadata->get(['scopes'], []));
|
||||
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!$this->metadata->get(['scopes', $scope, 'entity'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->metadata->get(['scopes', $scope, 'languageAclDisabled'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->acl->tryCheck($scope)) {
|
||||
unset($data[$scope]);
|
||||
unset($data['Global']['scopeNames'][$scope]);
|
||||
unset($data['Global']['scopeNamesPlural'][$scope]);
|
||||
} else {
|
||||
if (in_array($scope, ['EmailAccount', 'InboundEmail'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->acl->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]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->unsetEmpty($data, $scope);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->user->isAdmin()) {
|
||||
$this->prepareDataNonAdmin($data, $language);
|
||||
}
|
||||
}
|
||||
|
||||
$data['User']['fields'] = $data['User']['fields'] ?? [];
|
||||
|
||||
$data['User']['fields']['password'] = $language->translate('password', 'fields', 'User');
|
||||
$data['User']['fields']['passwordConfirm'] = $language->translate('passwordConfirm', 'fields', 'User');
|
||||
$data['User']['fields']['newPassword'] = $language->translate('newPassword', 'fields', 'User');
|
||||
$data['User']['fields']['newPasswordConfirm'] = $language->translate('newPasswordConfirm', 'fields', 'User');
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getDataForFrontend(bool $default = false): array
|
||||
{
|
||||
if ($default) {
|
||||
$languageObj = $this->getDefaultLanguage();
|
||||
} else {
|
||||
$languageObj = $this->getLanguage();
|
||||
}
|
||||
|
||||
return $this->getDataForFrontendFromLanguage($languageObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
private function unsetEmpty(array &$data, string $scope): void
|
||||
{
|
||||
if (($data[$scope]['options'] ?? null) === []) {
|
||||
unset($data[$scope]['options']);
|
||||
}
|
||||
|
||||
if (($data[$scope]['fields'] ?? null) === []) {
|
||||
unset($data[$scope]['fields']);
|
||||
}
|
||||
|
||||
if (($data[$scope]['links'] ?? null) === []) {
|
||||
unset($data[$scope]['links']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
private function prepareDataNonAdmin(array &$data, LanguageUtil $languageObj): void
|
||||
{
|
||||
unset($data['Admin']);
|
||||
unset($data['LayoutManager']);
|
||||
unset($data['EntityManager']);
|
||||
unset($data['FieldManager']);
|
||||
unset($data['Settings']);
|
||||
unset($data['ApiUser']);
|
||||
unset($data['DynamicLogic']);
|
||||
|
||||
$data['Settings'] = [
|
||||
'options' => [
|
||||
'auth2FAMethodList' => $languageObj->get(['Settings', 'options', 'auth2FAMethodList']),
|
||||
],
|
||||
];
|
||||
|
||||
$data['Admin'] = [
|
||||
'messages' => [
|
||||
'userHasNoEmailAddress' => $languageObj->translate('userHasNoEmailAddress', 'messages', 'Admin'),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($this->aclDependencyProvider->get() as $dependencyItem) {
|
||||
$target = $dependencyItem->getTarget();
|
||||
$aclScope = $dependencyItem->getScope();
|
||||
$aclField = $dependencyItem->getField();
|
||||
$anyScopeList = $dependencyItem->getAnyScopeList();
|
||||
|
||||
$targetArr = explode('.', $target);
|
||||
|
||||
$isFullScope = !str_contains($target, '.');
|
||||
|
||||
if ($isFullScope && isset($data[$target])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($anyScopeList) {
|
||||
$skip = true;
|
||||
|
||||
foreach ($anyScopeList as $itemScope) {
|
||||
if ($this->acl->tryCheck($itemScope)) {
|
||||
$skip = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($skip) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($aclScope) {
|
||||
if (!$this->acl->tryCheck($aclScope)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($aclField && in_array($aclField, $this->acl->getScopeForbiddenFieldList($aclScope))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$pointer =& $data;
|
||||
|
||||
foreach ($targetArr as $i => $k) {
|
||||
if ($i === count($targetArr) - 1) {
|
||||
$pointer[$k] = $languageObj->get($targetArr);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isset($pointer[$k])) {
|
||||
$pointer[$k] = [];
|
||||
}
|
||||
|
||||
$pointer =& $pointer[$k];
|
||||
}
|
||||
|
||||
if ($isFullScope) {
|
||||
$this->unsetEmpty($data, $target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
70
application/Espo/Tools/App/Metadata/AclDependencyItem.php
Normal file
70
application/Espo/Tools/App/Metadata/AclDependencyItem.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App\Metadata;
|
||||
|
||||
class AclDependencyItem
|
||||
{
|
||||
/**
|
||||
* @param ?string[] $anyScopeList
|
||||
*/
|
||||
public function __construct(
|
||||
private string $target,
|
||||
private ?string $scope,
|
||||
private ?string $field,
|
||||
private ?array $anyScopeList = null,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* A metadata path to be allowed if a user has access to a specific scope/field.
|
||||
*/
|
||||
public function getTarget(): string
|
||||
{
|
||||
return $this->target;
|
||||
}
|
||||
|
||||
public function getScope(): ?string
|
||||
{
|
||||
return $this->scope;
|
||||
}
|
||||
|
||||
public function getField(): ?string
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?string[]
|
||||
* @since 9.2.5
|
||||
*/
|
||||
public function getAnyScopeList(): ?array
|
||||
{
|
||||
return $this->anyScopeList;
|
||||
}
|
||||
}
|
||||
209
application/Espo/Tools/App/Metadata/AclDependencyProvider.php
Normal file
209
application/Espo/Tools/App/Metadata/AclDependencyProvider.php
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App\Metadata;
|
||||
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\DataCache;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Defs;
|
||||
|
||||
class AclDependencyProvider
|
||||
{
|
||||
private const CACHE_KEY = 'metadataAclDependency';
|
||||
|
||||
/** @var string[] */
|
||||
private array $enumFieldTypeList = [
|
||||
FieldType::ENUM,
|
||||
FieldType::MULTI_ENUM,
|
||||
FieldType::ARRAY,
|
||||
FieldType::CHECKLIST,
|
||||
];
|
||||
|
||||
/** @var ?AclDependencyItem[] */
|
||||
private ?array $data = null;
|
||||
private bool $useCache;
|
||||
|
||||
public function __construct(
|
||||
private DataCache $dataCache,
|
||||
private Metadata $metadata,
|
||||
private Defs $ormDefs,
|
||||
Config\SystemConfig $systemConfig,
|
||||
) {
|
||||
$this->useCache = $systemConfig->useCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AclDependencyItem[]
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
if ($this->data === null) {
|
||||
$this->data = $this->loadData();
|
||||
}
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AclDependencyItem[]
|
||||
*/
|
||||
private function loadData(): array
|
||||
{
|
||||
if ($this->useCache && $this->dataCache->has(self::CACHE_KEY)) {
|
||||
/** @var array<string, mixed>[] $raw */
|
||||
$raw = $this->dataCache->get(self::CACHE_KEY);
|
||||
|
||||
return $this->buildFromRaw($raw);
|
||||
}
|
||||
|
||||
return $this->buildData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AclDependencyItem[]
|
||||
*/
|
||||
private function buildData(): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach (($this->metadata->get(['app', 'metadata', 'aclDependencies']) ?? []) as $target => $item) {
|
||||
$anyScopeList = $item['anyScopeList'] ?? null;
|
||||
$scope = $item['scope'] ?? null;
|
||||
$field = $item['field'] ?? null;
|
||||
|
||||
$data[] = [
|
||||
'target' => $target,
|
||||
'anyScopeList' => $anyScopeList,
|
||||
'scope' => $scope,
|
||||
'field' => $field,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($this->ormDefs->getEntityList() as $entityDefs) {
|
||||
if (!$this->metadata->get(['scopes', $entityDefs->getName(), 'object'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($entityDefs->getFieldList() as $fieldDefs) {
|
||||
$item = $this->getDataFromField($entityDefs->getName(), $fieldDefs);
|
||||
|
||||
if ($item) {
|
||||
$data[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->useCache) {
|
||||
$this->dataCache->store(self::CACHE_KEY, $data);
|
||||
}
|
||||
|
||||
return $this->buildFromRaw($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?array<string, mixed>
|
||||
*/
|
||||
private function getDataFromField(string $entityType, Defs\FieldDefs $fieldDefs): ?array
|
||||
{
|
||||
if ($fieldDefs->getType() === FieldType::FOREIGN) {
|
||||
$refEntityType = $fieldDefs->getParam('link') ?
|
||||
$this->ormDefs
|
||||
->getEntity($entityType)
|
||||
->tryGetRelation($fieldDefs->getParam('link'))
|
||||
?->tryGetForeignEntityType() :
|
||||
null;
|
||||
|
||||
$refField = $fieldDefs->getParam('field');
|
||||
|
||||
if (!$refEntityType || !$refField) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'target' => "entityDefs.$refEntityType.fields.$refField",
|
||||
'scope' => $entityType,
|
||||
'field' => $fieldDefs->getName(),
|
||||
];
|
||||
}
|
||||
|
||||
if (!in_array($fieldDefs->getType(), $this->enumFieldTypeList)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$optionsPath = $fieldDefs->getParam('optionsPath');
|
||||
$optionsReference = $fieldDefs->getParam('optionsReference');
|
||||
|
||||
if (
|
||||
!$optionsPath &&
|
||||
$optionsReference &&
|
||||
str_contains($optionsReference, '.')
|
||||
) {
|
||||
[$refEntityType, $refField] = explode('.', $optionsReference);
|
||||
|
||||
$optionsPath = "entityDefs.$refEntityType.fields.$refField.options";
|
||||
}
|
||||
|
||||
if (!$optionsPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'target' => $optionsPath,
|
||||
'scope' => $entityType,
|
||||
'field' => $fieldDefs->getName(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed>[] $raw
|
||||
* @return AclDependencyItem[]
|
||||
*/
|
||||
private function buildFromRaw(array $raw): array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach ($raw as $rawItem) {
|
||||
$target = $rawItem['target'] ?? null;
|
||||
$scope = $rawItem['scope'] ?? null;
|
||||
$field = $rawItem['field'] ?? null;
|
||||
$anyScopeList = $rawItem['anyScopeList'] ?? null;
|
||||
|
||||
$list[] = new AclDependencyItem(
|
||||
target: $target,
|
||||
scope: $scope,
|
||||
field: $field,
|
||||
anyScopeList: $anyScopeList,
|
||||
);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
296
application/Espo/Tools/App/MetadataService.php
Normal file
296
application/Espo/Tools/App/MetadataService.php
Normal file
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App;
|
||||
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Utils\Metadata as MetadataUtil;
|
||||
use Espo\Core\Utils\ObjectUtil;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Modules\Crm\Entities\Reminder;
|
||||
use Espo\Tools\App\Metadata\AclDependencyProvider;
|
||||
use stdClass;
|
||||
|
||||
class MetadataService
|
||||
{
|
||||
private const ANY_KEY = '__ANY__';
|
||||
|
||||
public function __construct(
|
||||
private Acl $acl,
|
||||
private MetadataUtil $metadata,
|
||||
private User $user,
|
||||
private AclDependencyProvider $aclDependencyProvider
|
||||
) {}
|
||||
|
||||
public function getDataForFrontend(): stdClass
|
||||
{
|
||||
$data = $this->metadata->getAll();
|
||||
|
||||
$hiddenPathList = $this->metadata->get(['app', 'metadata', 'frontendHiddenPathList'], []);
|
||||
|
||||
foreach ($hiddenPathList as $row) {
|
||||
$this->removeDataByPath($row, $data);
|
||||
}
|
||||
|
||||
if ($this->user->isAdmin()) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$data = ObjectUtil::clone($data);
|
||||
|
||||
$hiddenPathList = $this->metadata->get(['app', 'metadata', 'frontendNonAdminHiddenPathList'], []);
|
||||
|
||||
foreach ($hiddenPathList as $row) {
|
||||
$this->removeDataByPath($row, $data);
|
||||
}
|
||||
|
||||
/** @var string[] $scopeList */
|
||||
$scopeList = array_keys($this->metadata->get(['entityDefs'], []));
|
||||
|
||||
foreach ($scopeList as $scope) {
|
||||
$isEntity = $this->metadata->get(['scopes', $scope, 'entity']);
|
||||
|
||||
if ($isEntity === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($scope === Reminder::ENTITY_TYPE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isAllowed = $isEntity !== null && $this->acl->tryCheck($scope);
|
||||
|
||||
if (!$isAllowed) {
|
||||
unset($data->entityDefs->$scope);
|
||||
unset($data->clientDefs->$scope);
|
||||
unset($data->entityAcl->$scope);
|
||||
unset($data->scopes->$scope);
|
||||
unset($data->logicDefs->$scope);
|
||||
}
|
||||
}
|
||||
|
||||
$entityTypeList = array_keys(get_object_vars($data->entityDefs));
|
||||
|
||||
foreach ($entityTypeList as $entityType) {
|
||||
$linksDefs = $this->metadata->get(['entityDefs', $entityType, 'links'], []);
|
||||
|
||||
$forbiddenFieldList = $this->acl->getScopeForbiddenFieldList($entityType);
|
||||
|
||||
foreach ($linksDefs as $link => $defs) {
|
||||
$type = $defs['type'] ?? null;
|
||||
|
||||
$hasField = (bool) $this->metadata->get(['entityDefs', $entityType, 'fields', $link]);
|
||||
|
||||
if ($type === 'belongsToParent') {
|
||||
if ($hasField) {
|
||||
$parentEntityList = $this->metadata
|
||||
->get(['entityDefs', $entityType, 'fields', $link, 'entityList']);
|
||||
|
||||
if (is_array($parentEntityList)) {
|
||||
foreach ($parentEntityList as $i => $e) {
|
||||
if (!$this->acl->tryCheck($e)) {
|
||||
unset($parentEntityList[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$parentEntityList = array_values($parentEntityList);
|
||||
|
||||
$data->entityDefs->$entityType->fields->$link->entityList = $parentEntityList;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$foreignEntityType = $defs['entity'] ?? null;
|
||||
|
||||
if ($foreignEntityType) {
|
||||
if ($this->acl->tryCheck($foreignEntityType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->user->isPortal()) {
|
||||
if ($foreignEntityType === 'Account' || $foreignEntityType === 'Contact') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasField) {
|
||||
if (!in_array($link, $forbiddenFieldList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($data->entityDefs->$entityType->fields->$link);
|
||||
}
|
||||
|
||||
unset($data->entityDefs->$entityType->links->$link);
|
||||
|
||||
if (isset($data->clientDefs->$entityType->relationshipPanels)) {
|
||||
unset($data->clientDefs->$entityType->relationshipPanels->$link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unset($data->entityDefs->Settings);
|
||||
|
||||
/** @var string[] $dashletList */
|
||||
$dashletList = array_keys($this->metadata->get(['dashlets'], []));
|
||||
|
||||
foreach ($dashletList as $item) {
|
||||
$aclScope = $this->metadata->get(['dashlets', $item, 'aclScope']);
|
||||
|
||||
if ($aclScope && !$this->acl->tryCheck($aclScope)) {
|
||||
unset($data->dashlets->$item);
|
||||
}
|
||||
}
|
||||
|
||||
unset($data->authenticationMethods);
|
||||
unset($data->formula);
|
||||
|
||||
foreach ($this->aclDependencyProvider->get() as $dependencyItem) {
|
||||
$aclScope = $dependencyItem->getScope();
|
||||
$aclField = $dependencyItem->getField();
|
||||
$anyScopeList = $dependencyItem->getAnyScopeList();
|
||||
|
||||
if ($anyScopeList) {
|
||||
$skip = true;
|
||||
|
||||
foreach ($anyScopeList as $itemScope) {
|
||||
if ($this->acl->tryCheck($itemScope)) {
|
||||
$skip = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($skip) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($aclScope) {
|
||||
if (!$this->acl->tryCheck($aclScope)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($aclField && in_array($aclField, $this->acl->getScopeForbiddenFieldList($aclScope))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$targetArr = explode('.', $dependencyItem->getTarget());
|
||||
|
||||
$pointer = $data;
|
||||
|
||||
$value = $this->metadata->getObjects($targetArr);
|
||||
|
||||
if ($value === null) {
|
||||
// Important.
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($targetArr as $i => $k) {
|
||||
if ($i === count($targetArr) - 1) {
|
||||
$pointer->$k = $value;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isset($pointer->$k)) {
|
||||
$pointer->$k = (object) [];
|
||||
}
|
||||
|
||||
$pointer = $pointer->$k;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string[] $row
|
||||
* @param stdClass $data
|
||||
*/
|
||||
private function removeDataByPath($row, &$data): void
|
||||
{
|
||||
$p = &$data;
|
||||
$path = [&$p];
|
||||
|
||||
foreach ($row as $i => $item) {
|
||||
if (is_array($item)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($item === self::ANY_KEY) {
|
||||
foreach (get_object_vars($p) as &$v) {
|
||||
$this->removeDataByPath(
|
||||
array_slice($row, $i + 1),
|
||||
$v
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!property_exists($p, $item)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($i == count($row) - 1) {
|
||||
unset($p->$item);
|
||||
|
||||
$o = &$p;
|
||||
|
||||
for ($j = $i - 1; $j > 0; $j--) {
|
||||
if (is_object($o) && !count(get_object_vars($o))) {
|
||||
$o = &$path[$j];
|
||||
$k = $row[$j];
|
||||
|
||||
unset($o->$k);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$p = &$p->$item;
|
||||
$path[] = &$p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getDataForFrontendByKey(?string $key): mixed
|
||||
{
|
||||
$data = $this->getDataForFrontend();
|
||||
|
||||
return Util::getValueByKey($data, $key);
|
||||
}
|
||||
}
|
||||
232
application/Espo/Tools/App/PreferencesService.php
Normal file
232
application/Espo/Tools/App/PreferencesService.php
Normal file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App;
|
||||
|
||||
use Espo\Core\Name\Field;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
use Espo\Repositories\Preferences as Repository;
|
||||
use Espo\Entities\Preferences;
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Acl\Table;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\FieldValidation\FieldValidationManager;
|
||||
use Espo\Core\Utils\Config;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class PreferencesService
|
||||
{
|
||||
private EntityManager $entityManager;
|
||||
private User $user;
|
||||
private Acl $acl;
|
||||
private Config $config;
|
||||
private FieldValidationManager $fieldValidationManager;
|
||||
|
||||
public function __construct(
|
||||
EntityManager $entityManager,
|
||||
User $user,
|
||||
Acl $acl,
|
||||
Config $config,
|
||||
FieldValidationManager $fieldValidationManager
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->user = $user;
|
||||
$this->acl = $acl;
|
||||
$this->config = $config;
|
||||
$this->fieldValidationManager = $fieldValidationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
protected function processAccessCheck(string $userId): void
|
||||
{
|
||||
if (!$this->user->isAdmin()) {
|
||||
if ($this->user->getId() !== $userId) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
* @throws NotFound
|
||||
*/
|
||||
public function read(string $userId): Preferences
|
||||
{
|
||||
$this->processAccessCheck($userId);
|
||||
|
||||
/** @var ?Preferences $entity */
|
||||
$entity = $this->entityManager->getEntityById(Preferences::ENTITY_TYPE, $userId);
|
||||
/** @var ?User $user */
|
||||
$user = $this->entityManager->getEntityById(User::ENTITY_TYPE, $userId);
|
||||
|
||||
if (!$entity || !$user) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$entity->set(Field::NAME, $user->getName());
|
||||
$entity->set('isPortalUser', $user->isPortal());
|
||||
|
||||
// @todo Remove.
|
||||
$entity->clear('smtpPassword');
|
||||
|
||||
$forbiddenAttributeList = $this->acl
|
||||
->getScopeForbiddenAttributeList(Preferences::ENTITY_TYPE, Table::ACTION_READ);
|
||||
|
||||
foreach ($forbiddenAttributeList as $attribute) {
|
||||
$entity->clear($attribute);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
* @throws NotFound
|
||||
* @throws BadRequest
|
||||
*/
|
||||
public function update(string $userId, stdClass $data): Preferences
|
||||
{
|
||||
$this->processAccessCheck($userId);
|
||||
|
||||
if ($this->acl->getLevel(Preferences::ENTITY_TYPE, Table::ACTION_EDIT) === Table::LEVEL_NO) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$forbiddenAttributeList = $this->acl
|
||||
->getScopeForbiddenAttributeList(Preferences::ENTITY_TYPE, Table::ACTION_EDIT);
|
||||
|
||||
foreach ($forbiddenAttributeList as $attribute) {
|
||||
unset($data->$attribute);
|
||||
}
|
||||
|
||||
/** @var ?User $user */
|
||||
$user = $this->entityManager->getEntityById(User::ENTITY_TYPE, $userId);
|
||||
|
||||
/** @var ?Preferences $entity */
|
||||
$entity = $this->entityManager->getEntityById(Preferences::ENTITY_TYPE, $userId);
|
||||
|
||||
if (!$entity || !$user) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$entity->set($data);
|
||||
|
||||
$this->fieldValidationManager->process($entity, $data);
|
||||
|
||||
$this->entityManager->saveEntity($entity);
|
||||
|
||||
$entity->set(Field::NAME, $user->getName());
|
||||
|
||||
// @todo Remove.
|
||||
$entity->clear('smtpPassword');
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
* @throws NotFound
|
||||
*/
|
||||
public function resetToDefaults(string $userId): void
|
||||
{
|
||||
$this->processAccessCheck($userId);
|
||||
|
||||
$result = $this->getRepository()->resetToDefaults($userId);
|
||||
|
||||
if (!$result) {
|
||||
throw new NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
* @throws NotFound
|
||||
*/
|
||||
public function resetDashboard(string $userId): stdClass
|
||||
{
|
||||
$this->processAccessCheck($userId);
|
||||
|
||||
if ($this->acl->getLevel(Preferences::ENTITY_TYPE, Table::ACTION_EDIT) === Table::LEVEL_NO) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
/** @var ?User $user */
|
||||
$user = $this->entityManager->getEntityById(User::ENTITY_TYPE, $userId);
|
||||
|
||||
$preferences = $this->entityManager->getEntityById(Preferences::ENTITY_TYPE, $userId);
|
||||
|
||||
if (!$user) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
if (!$preferences) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
if ($user->isPortal()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$forbiddenAttributeList = $this->acl
|
||||
->getScopeForbiddenAttributeList(Preferences::ENTITY_TYPE, Table::ACTION_EDIT);
|
||||
|
||||
if (in_array('dashboardLayout', $forbiddenAttributeList)) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$dashboardLayout = $this->config->get('dashboardLayout');
|
||||
$dashletsOptions = $this->config->get('dashletsOptions');
|
||||
|
||||
$preferences->set([
|
||||
'dashboardLayout' => $dashboardLayout,
|
||||
'dashletsOptions' => $dashletsOptions,
|
||||
]);
|
||||
|
||||
$this->entityManager->saveEntity($preferences);
|
||||
|
||||
return (object) [
|
||||
'dashboardLayout' => $preferences->get('dashboardLayout'),
|
||||
'dashletsOptions' => $preferences->get('dashletsOptions'),
|
||||
];
|
||||
}
|
||||
|
||||
private function getRepository(): Repository
|
||||
{
|
||||
/** @var Repository */
|
||||
return $this->entityManager->getRepository(Preferences::ENTITY_TYPE);
|
||||
}
|
||||
}
|
||||
404
application/Espo/Tools/App/SettingsService.php
Normal file
404
application/Espo/Tools/App/SettingsService.php
Normal file
@@ -0,0 +1,404 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://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 Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\App;
|
||||
|
||||
use Espo\Core\Mail\ConfigDataProvider as EmailConfigDataProvider;
|
||||
use Espo\Core\Utils\ThemeManager;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\Entities\Settings;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Authentication\Util\MethodProvider as AuthenticationMethodProvider;
|
||||
use Espo\Core\ApplicationState;
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\DataManager;
|
||||
use Espo\Core\FieldValidation\FieldValidationManager;
|
||||
use Espo\Core\Utils\Currency\DatabasePopulator as CurrencyDatabasePopulator;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Config\ConfigWriter;
|
||||
use Espo\Core\Utils\Config\Access;
|
||||
|
||||
use Espo\Entities\Portal;
|
||||
use Espo\Repositories\Portal as PortalRepository;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class SettingsService
|
||||
{
|
||||
public function __construct(
|
||||
private ApplicationState $applicationState,
|
||||
private Config $config,
|
||||
private ConfigWriter $configWriter,
|
||||
private Metadata $metadata,
|
||||
private Acl $acl,
|
||||
private EntityManager $entityManager,
|
||||
private DataManager $dataManager,
|
||||
private FieldValidationManager $fieldValidationManager,
|
||||
private InjectableFactory $injectableFactory,
|
||||
private Access $access,
|
||||
private AuthenticationMethodProvider $authenticationMethodProvider,
|
||||
private ThemeManager $themeManager,
|
||||
private Config\SystemConfig $systemConfig,
|
||||
private EmailConfigDataProvider $emailConfigDataProvider,
|
||||
private Acl\Cache\Clearer $aclCacheClearer,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get config data.
|
||||
*/
|
||||
public function getConfigData(): stdClass
|
||||
{
|
||||
$data = $this->config->getAllNonInternalData();
|
||||
|
||||
$this->filterDataByAccess($data);
|
||||
$this->filterData($data);
|
||||
$this->loadAdditionalParams($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata to be used in config.
|
||||
*/
|
||||
public function getMetadataConfigData(): stdClass
|
||||
{
|
||||
$data = (object) [];
|
||||
|
||||
unset($data->loginView);
|
||||
|
||||
$loginView = $this->metadata->get(['clientDefs', 'App', 'loginView']);
|
||||
|
||||
if ($loginView) {
|
||||
$data->loginView = $loginView;
|
||||
}
|
||||
|
||||
$loginData = $this->getLoginData();
|
||||
|
||||
if ($loginData) {
|
||||
$data->loginData = (object) $loginData;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?array{
|
||||
* handler: string,
|
||||
* fallback: bool,
|
||||
* data: stdClass,
|
||||
* method: string,
|
||||
* }
|
||||
*/
|
||||
private function getLoginData(): ?array
|
||||
{
|
||||
$method = $this->authenticationMethodProvider->get();
|
||||
|
||||
/** @var array<string, mixed> $mData */
|
||||
$mData = $this->metadata->get(['authenticationMethods', $method, 'login']) ?? [];
|
||||
|
||||
/** @var ?string $handler */
|
||||
$handler = $mData['handler'] ?? null;
|
||||
|
||||
if (!$handler) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$isProvider = $this->isPortalWithAuthenticationProvider();
|
||||
|
||||
if (!$isProvider && $this->applicationState->isPortal()) {
|
||||
/** @var ?bool $portal */
|
||||
$portal = $mData['portal'] ?? null;
|
||||
|
||||
if ($portal === null) {
|
||||
/** @var ?string $portalConfigParam */
|
||||
$portalConfigParam = $mData['portalConfigParam'] ?? null;
|
||||
|
||||
$portal = $portalConfigParam && $this->config->get($portalConfigParam);
|
||||
}
|
||||
|
||||
if (!$portal) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var ?bool $fallback */
|
||||
$fallback = !$this->applicationState->isPortal() ?
|
||||
($mData['fallback'] ?? null) :
|
||||
false;
|
||||
|
||||
if ($fallback === null) {
|
||||
/** @var ?string $fallbackConfigParam */
|
||||
$fallbackConfigParam = $mData['fallbackConfigParam'] ?? null;
|
||||
|
||||
$fallback = $fallbackConfigParam && $this->config->get($fallbackConfigParam);
|
||||
}
|
||||
|
||||
if ($isProvider) {
|
||||
$fallback = false;
|
||||
}
|
||||
|
||||
/** @var stdClass $data */
|
||||
$data = (object) ($mData['data'] ?? []);
|
||||
|
||||
return [
|
||||
'handler' => $handler,
|
||||
'fallback' => $fallback,
|
||||
'method' => $method,
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
private function isPortalWithAuthenticationProvider(): bool
|
||||
{
|
||||
if (!$this->applicationState->isPortal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$portal = $this->applicationState->getPortal();
|
||||
|
||||
return (bool) $this->authenticationMethodProvider->getForPortal($portal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set config data.
|
||||
*
|
||||
* @throws BadRequest
|
||||
* @throws Forbidden
|
||||
* @throws Error
|
||||
*/
|
||||
public function setConfigData(stdClass $data): void
|
||||
{
|
||||
$user = $this->applicationState->getUser();
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$ignoreItemList = array_merge(
|
||||
$this->access->getSystemParamList(),
|
||||
$this->access->getReadOnlyParamList(),
|
||||
$this->isRestrictedMode() && !$user->isSuperAdmin() ?
|
||||
$this->access->getSuperAdminParamList() : []
|
||||
);
|
||||
|
||||
foreach ($ignoreItemList as $item) {
|
||||
unset($data->$item);
|
||||
}
|
||||
|
||||
$entity = $this->entityManager->getNewEntity(Settings::ENTITY_TYPE);
|
||||
|
||||
$entity->set($data);
|
||||
$entity->setAsNotNew();
|
||||
|
||||
$this->processValidation($entity, $data);
|
||||
|
||||
if (
|
||||
isset($data->useCache) &&
|
||||
$data->useCache !== $this->systemConfig->useCache()
|
||||
) {
|
||||
$this->dataManager->clearCache();
|
||||
}
|
||||
|
||||
$this->configWriter->setMultiple(get_object_vars($data));
|
||||
$this->configWriter->save();
|
||||
|
||||
if (isset($data->personNameFormat)) {
|
||||
$this->dataManager->clearCache();
|
||||
}
|
||||
|
||||
if (property_exists($data, 'baselineRoleId')) {
|
||||
$this->aclCacheClearer->clearForAllInternalUsers();
|
||||
}
|
||||
|
||||
if (isset($data->defaultCurrency) || isset($data->baseCurrency) || isset($data->currencyRates)) {
|
||||
$this->populateDatabaseWithCurrencyRates();
|
||||
}
|
||||
}
|
||||
|
||||
private function loadAdditionalParams(stdClass $data): void
|
||||
{
|
||||
if ($this->applicationState->isPortal()) {
|
||||
$portal = $this->applicationState->getPortal();
|
||||
|
||||
$this->getPortalRepository()->loadUrlField($portal);
|
||||
|
||||
$data->siteUrl = $portal->get('url');
|
||||
}
|
||||
|
||||
if (
|
||||
(
|
||||
$this->emailConfigDataProvider->getSystemOutboundAddress() ||
|
||||
$this->config->get('internalSmtpServer')
|
||||
) &&
|
||||
!$this->config->get('passwordRecoveryDisabled')
|
||||
) {
|
||||
$data->passwordRecoveryEnabled = true;
|
||||
}
|
||||
|
||||
$data->logoSrc = $this->themeManager->getLogoSrc();
|
||||
}
|
||||
|
||||
private function filterDataByAccess(stdClass $data): void
|
||||
{
|
||||
$user = $this->applicationState->getUser();
|
||||
|
||||
$ignoreItemList = [];
|
||||
|
||||
foreach ($this->access->getSystemParamList() as $item) {
|
||||
$ignoreItemList[] = $item;
|
||||
}
|
||||
|
||||
foreach ($this->access->getInternalParamList() as $item) {
|
||||
$ignoreItemList[] = $item;
|
||||
}
|
||||
|
||||
if (!$user->isAdmin() || $user->isSystem()) {
|
||||
foreach ($this->access->getAdminParamList() as $item) {
|
||||
$ignoreItemList[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
/*if ($this->isRestrictedMode() && !$user->isSuperAdmin()) {
|
||||
// @todo Maybe add restriction level for non-super admins.
|
||||
}*/
|
||||
|
||||
foreach ($ignoreItemList as $item) {
|
||||
unset($data->$item);
|
||||
}
|
||||
|
||||
if ($user->isSystem()) {
|
||||
$globalItemList = $this->access->getGlobalParamList();
|
||||
|
||||
foreach (array_keys(get_object_vars($data)) as $item) {
|
||||
if (!in_array($item, $globalItemList)) {
|
||||
unset($data->$item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function filterEntityTypeParams(stdClass $data): void
|
||||
{
|
||||
$entityTypeListParamList = $this->metadata->get(['app', 'config', 'entityTypeListParamList']) ?? [];
|
||||
|
||||
/** @var string[] $scopeList */
|
||||
$scopeList = array_keys($this->metadata->get(['entityDefs'], []));
|
||||
|
||||
foreach ($scopeList as $scope) {
|
||||
if (!$this->metadata->get(['scopes', $scope, 'acl'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->acl->tryCheck($scope)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($entityTypeListParamList as $param) {
|
||||
$list = $data->$param ?? [];
|
||||
|
||||
foreach ($list as $i => $item) {
|
||||
if ($item === $scope) {
|
||||
unset($list[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$data->$param = array_values($list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function populateDatabaseWithCurrencyRates(): void
|
||||
{
|
||||
$this->injectableFactory->create(CurrencyDatabasePopulator::class)->process();
|
||||
}
|
||||
|
||||
private function filterData(stdClass $data): void
|
||||
{
|
||||
$user = $this->applicationState->getUser();
|
||||
|
||||
if (!$user->isAdmin() && !$user->isSystem()) {
|
||||
$this->filterEntityTypeParams($data);
|
||||
}
|
||||
|
||||
$fieldDefs = $this->metadata->get(['entityDefs', 'Settings', 'fields']);
|
||||
|
||||
foreach ($fieldDefs as $field => $fieldParams) {
|
||||
if ($fieldParams['type'] === 'password') {
|
||||
unset($data->$field);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($data->useWebSocket)) {
|
||||
unset($data->webSocketUrl);
|
||||
}
|
||||
|
||||
if ($user->isSystem()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($user->isAdmin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!$this->acl->checkScope(Email::ENTITY_TYPE, Acl\Table::ACTION_CREATE) ||
|
||||
!$this->emailConfigDataProvider->isSystemOutboundAddressShared()
|
||||
) {
|
||||
unset($data->outboundEmailFromAddress);
|
||||
unset($data->outboundEmailFromName);
|
||||
unset($data->outboundEmailBccAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private function isRestrictedMode(): bool
|
||||
{
|
||||
return (bool) $this->config->get('restrictedMode');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
*/
|
||||
private function processValidation(Entity $entity, stdClass $data): void
|
||||
{
|
||||
$this->fieldValidationManager->process($entity, $data);
|
||||
}
|
||||
|
||||
private function getPortalRepository(): PortalRepository
|
||||
{
|
||||
/** @var PortalRepository */
|
||||
return $this->entityManager->getRepository(Portal::ENTITY_TYPE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user