mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-03 02:27:01 +00:00
config internal
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
/data/preferences/*
|
||||
/data/.backup/*
|
||||
/data/config.php
|
||||
/data/config-internal.php
|
||||
/data/tmp/*
|
||||
/build
|
||||
/node_modules
|
||||
|
||||
@@ -34,15 +34,15 @@ use Espo\Core\{
|
||||
Utils\Config as BaseConfig,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
class Config extends BaseConfig
|
||||
{
|
||||
protected $portalParamsSet = false;
|
||||
private $portalParamsSet = false;
|
||||
|
||||
protected $portalData = [];
|
||||
private $portalData = [];
|
||||
|
||||
protected $portalParamList = [
|
||||
private $portalParamList = [
|
||||
'companyLogoId',
|
||||
'tabList',
|
||||
'quickCreateList',
|
||||
@@ -75,9 +75,9 @@ class Config extends BaseConfig
|
||||
return parent::has($name);
|
||||
}
|
||||
|
||||
public function getAllData(): StdClass
|
||||
public function getAllNonInternalData(): stdClass
|
||||
{
|
||||
$data = parent::getAllData();
|
||||
$data = parent::getAllNonInternalData();
|
||||
|
||||
foreach ($this->portalData as $k => $v) {
|
||||
$data->$k = $v;
|
||||
|
||||
@@ -46,6 +46,8 @@ class Config
|
||||
|
||||
protected $configPath = 'data/config.php';
|
||||
|
||||
private $internalConfigPath = 'data/config-internal.php';
|
||||
|
||||
private $cacheTimestamp = 'cacheTimestamp';
|
||||
|
||||
protected $associativeArrayAttributeList = [
|
||||
@@ -63,6 +65,8 @@ class Config
|
||||
|
||||
private $fileManager;
|
||||
|
||||
private $internalParamList = [];
|
||||
|
||||
public function __construct(ConfigFileManager $fileManager)
|
||||
{
|
||||
$this->fileManager = $fileManager;
|
||||
@@ -78,6 +82,16 @@ class Config
|
||||
return $this->configPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* A path to the internal config file.
|
||||
*
|
||||
* @todo Move to ConfigData.
|
||||
*/
|
||||
public function getInternalConfigPath(): string
|
||||
{
|
||||
return $this->internalConfigPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a parameter value.
|
||||
*
|
||||
@@ -87,7 +101,7 @@ class Config
|
||||
{
|
||||
$keys = explode('.', $name);
|
||||
|
||||
$lastBranch = $this->loadConfig();
|
||||
$lastBranch = $this->getData();
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (!is_array($lastBranch) && !is_object($lastBranch)) {
|
||||
@@ -117,7 +131,7 @@ class Config
|
||||
{
|
||||
$keys = explode('.', $name);
|
||||
|
||||
$lastBranch = $this->loadConfig();
|
||||
$lastBranch = $this->getData();
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (!is_array($lastBranch) && !is_object($lastBranch)) {
|
||||
@@ -148,7 +162,7 @@ class Config
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$this->loadConfig(true);
|
||||
$this->load();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,7 +269,7 @@ class Config
|
||||
$this->changedData = [];
|
||||
$this->removeData = [];
|
||||
|
||||
$this->loadConfig(true);
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -272,43 +286,68 @@ class Config
|
||||
return $this->fileManager->getPhpContents($this->defaultConfigPath);
|
||||
}
|
||||
|
||||
protected function loadConfig(bool $reload = false)
|
||||
private function isLoaded(): bool
|
||||
{
|
||||
if (!$reload && isset($this->data) && !empty($this->data)) {
|
||||
return isset($this->data) && !empty($this->data);
|
||||
}
|
||||
|
||||
private function getData(bool $reload = false): array
|
||||
{
|
||||
if (!$reload && $this->isLoaded()) {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
$configPath = $this->fileManager->isFile($this->configPath) ?
|
||||
$this->configPath :
|
||||
$this->defaultConfigPath;
|
||||
|
||||
$this->data = $this->fileManager->getPhpContents($configPath);
|
||||
|
||||
$systemConfig = $this->fileManager->getPhpContents($this->systemConfigPath);
|
||||
|
||||
$this->data = Util::merge($systemConfig, $this->data);
|
||||
|
||||
$this->fileManager->setConfig($this);
|
||||
$this->load();
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all parameters.
|
||||
*/
|
||||
public function getAllData(): stdClass
|
||||
private function load(): void
|
||||
{
|
||||
return (object) $this->loadConfig();
|
||||
$configPath = $this->fileManager->isFile($this->configPath) ?
|
||||
$this->configPath :
|
||||
$this->defaultConfigPath;
|
||||
|
||||
$data = $this->fileManager->getPhpContents($configPath);
|
||||
$systemData = $this->fileManager->getPhpContents($this->systemConfigPath);
|
||||
|
||||
$internalData = $this->fileManager->isFile($this->internalConfigPath) ?
|
||||
$this->fileManager->getPhpContents($this->internalConfigPath) : [];
|
||||
|
||||
$this->data = Util::merge($systemData, $data);
|
||||
$this->data = Util::merge($this->data, $internalData);
|
||||
|
||||
$this->internalParamList = array_keys($internalData);
|
||||
|
||||
$this->fileManager->setConfig($this);
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
public function getData()
|
||||
/**
|
||||
* Get all parameters excluding those that are set in the internal config.
|
||||
*/
|
||||
public function getAllNonInternalData(): stdClass
|
||||
{
|
||||
$data = $this->loadConfig();
|
||||
$data = (object) $this->getData();
|
||||
|
||||
foreach ($this->internalParamList as $param) {
|
||||
unset($data->$param);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a parameter is set in the internal config.
|
||||
*/
|
||||
public function isInternal(string $name): bool
|
||||
{
|
||||
if (!$this->isLoaded()) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return in_array($name, $this->internalParamList);
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
public function setData($data)
|
||||
{
|
||||
|
||||
@@ -35,18 +35,6 @@ use Espo\Core\Utils\FieldUtil;
|
||||
|
||||
class Access
|
||||
{
|
||||
public const DEFAULT_ACCESS_LEVEL = self::ACCESS_LEVEL_USER;
|
||||
|
||||
public const ACCESS_LEVEL_GLOBAL = 'global';
|
||||
|
||||
public const ACCESS_LEVEL_USER = 'user';
|
||||
|
||||
public const ACCESS_LEVEL_ADMIN = 'admin';
|
||||
|
||||
public const ACCESS_LEVEL_SUPER_ADMIN = 'superAdmin';
|
||||
|
||||
public const ACCESS_LEVEL_SYSTEM = 'system';
|
||||
|
||||
private $config;
|
||||
|
||||
private $metadata;
|
||||
@@ -123,6 +111,16 @@ class Access
|
||||
}
|
||||
}
|
||||
|
||||
$params = $this->metadata->get(['app', 'config', 'params']) ?? [];
|
||||
|
||||
foreach ($params as $name => $item) {
|
||||
if (empty($item['system'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$itemList[] = $name;
|
||||
}
|
||||
|
||||
return array_values($itemList);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,8 @@
|
||||
|
||||
namespace Espo\Core\Utils\Config;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\Config,
|
||||
Exceptions\Error,
|
||||
};
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use Exception;
|
||||
|
||||
@@ -54,20 +52,24 @@ class ConfigWriter
|
||||
|
||||
private $cacheTimestampParam = 'cacheTimestamp';
|
||||
|
||||
protected $config;
|
||||
private $config;
|
||||
|
||||
protected $fileManager;
|
||||
private $fileManager;
|
||||
|
||||
protected $helper;
|
||||
private $helper;
|
||||
|
||||
private $internalConfigHelper;
|
||||
|
||||
public function __construct(
|
||||
Config $config,
|
||||
ConfigWriterFileManager $fileManager,
|
||||
ConfigWriterHelper $helper
|
||||
ConfigWriterHelper $helper,
|
||||
InternalConfigHelper $internalConfigHelper
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->fileManager = $fileManager;
|
||||
$this->helper = $helper;
|
||||
$this->internalConfigHelper = $internalConfigHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,6 +114,7 @@ class ConfigWriter
|
||||
}
|
||||
|
||||
$configPath = $this->config->getConfigPath();
|
||||
$internalConfigPath = $this->config->getInternalConfigPath();
|
||||
|
||||
if (!$this->fileManager->isFile($configPath)) {
|
||||
throw new Error("Config file '{$configPath}' was not found.");
|
||||
@@ -119,6 +122,9 @@ class ConfigWriter
|
||||
|
||||
$data = $this->fileManager->getPhpContents($configPath);
|
||||
|
||||
$dataInternal = $this->fileManager->isFile($internalConfigPath) ?
|
||||
$this->fileManager->getPhpContents($internalConfigPath) : [];
|
||||
|
||||
if (!is_array($data)) {
|
||||
$data = $this->fileManager->getPhpContents($configPath);
|
||||
}
|
||||
@@ -127,39 +133,41 @@ class ConfigWriter
|
||||
throw new Error("Could not read config.");
|
||||
}
|
||||
|
||||
if (!is_array($dataInternal)) {
|
||||
throw new Error("Could not read config-internal.");
|
||||
}
|
||||
|
||||
$toSaveInternal = false;
|
||||
|
||||
foreach ($changedData as $key => $value) {
|
||||
if ($this->internalConfigHelper->isParamForInternalConfig($key)) {
|
||||
$dataInternal[$key] = $value;
|
||||
unset($data[$key]);
|
||||
|
||||
$toSaveInternal = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[$key] = $value;
|
||||
}
|
||||
|
||||
foreach ($this->removeParamList as $key) {
|
||||
if ($this->internalConfigHelper->isParamForInternalConfig($key)) {
|
||||
unset($dataInternal[$key]);
|
||||
|
||||
$toSaveInternal = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($data[$key]);
|
||||
}
|
||||
|
||||
if (!is_array($data)) {
|
||||
throw new Error("Invalid config data while saving.");
|
||||
}
|
||||
$this->saveData($configPath, $data, 'microtime');
|
||||
|
||||
$data['microtime'] = $microtime = $this->helper->generateMicrotime();
|
||||
|
||||
try {
|
||||
$this->fileManager->putPhpContents($configPath, $data);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
throw new Error("Could not save config.");
|
||||
}
|
||||
|
||||
$reloadedData = $this->fileManager->getPhpContents($configPath);
|
||||
|
||||
if (
|
||||
!is_array($reloadedData) ||
|
||||
$microtime !== ($reloadedData['microtime'] ?? null)
|
||||
) {
|
||||
try {
|
||||
$this->fileManager->putPhpContentsNoRenaming($configPath, $data);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
throw new Error("Could not save config.");
|
||||
}
|
||||
if ($toSaveInternal) {
|
||||
$this->saveData($internalConfigPath, $dataInternal, 'microtimeInternal');
|
||||
}
|
||||
|
||||
$this->changedData = [];
|
||||
@@ -168,6 +176,34 @@ class ConfigWriter
|
||||
$this->config->update();
|
||||
}
|
||||
|
||||
private function saveData(string $path, array &$data, string $timeParam): void
|
||||
{
|
||||
$data[$timeParam] = $microtime = $this->helper->generateMicrotime();
|
||||
|
||||
try {
|
||||
$this->fileManager->putPhpContents($path, $data);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
throw new Error("Could not save config.");
|
||||
}
|
||||
|
||||
$reloadedData = $this->fileManager->getPhpContents($path);
|
||||
|
||||
if (
|
||||
is_array($reloadedData) &&
|
||||
$microtime === ($reloadedData[$timeParam] ?? null)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->fileManager->putPhpContentsNoRenaming($path, $data);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
throw new Error("Could not save config.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cache timestamp.
|
||||
*
|
||||
|
||||
63
application/Espo/Core/Utils/Config/InternalConfigHelper.php
Normal file
63
application/Espo/Core/Utils/Config/InternalConfigHelper.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii 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\Config;
|
||||
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
|
||||
class InternalConfigHelper
|
||||
{
|
||||
private $config;
|
||||
|
||||
private $metadata;
|
||||
|
||||
public function __construct(Config $config, Metadata $metadata)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
|
||||
public function isParamForInternalConfig(string $name): bool
|
||||
{
|
||||
if ($this->config->isInternal($name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (in_array($name, $this->config->get('systemItems') ?? [])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->metadata->get(['app', 'config', 'params', $name, 'system'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -11,5 +11,10 @@
|
||||
"historyEntityList",
|
||||
"streamEmailNotificationsTypeList",
|
||||
"emailKeepParentTeamsEntityList"
|
||||
]
|
||||
],
|
||||
"params": {
|
||||
"awsS3Storage": {
|
||||
"system": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -634,8 +634,7 @@
|
||||
"tooltip": true
|
||||
},
|
||||
"awsS3Storage": {
|
||||
"type": "jsonObject",
|
||||
"onlySystem": true
|
||||
"type": "jsonObject"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ class Settings
|
||||
|
||||
public function getConfigData(): stdClass
|
||||
{
|
||||
$data = $this->config->getAllData();
|
||||
$data = $this->config->getAllNonInternalData();
|
||||
|
||||
$this->filterDataByAccess($data);
|
||||
$this->filterData($data);
|
||||
|
||||
@@ -27,13 +27,14 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace tests\unit\Espo\Core\Utils;
|
||||
namespace tests\unit\Espo\Core\Utils\Config;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\Config,
|
||||
Utils\Config\ConfigWriter,
|
||||
Utils\Config\ConfigWriterFileManager,
|
||||
Utils\Config\ConfigWriterHelper,
|
||||
Utils\Config\InternalConfigHelper,
|
||||
};
|
||||
|
||||
class ConfigWriterTest extends \PHPUnit\Framework\TestCase
|
||||
@@ -46,14 +47,27 @@ class ConfigWriterTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
$this->helper = $this->createMock(ConfigWriterHelper::class);
|
||||
|
||||
$this->configWriter = new ConfigWriter($this->config, $this->fileManager, $this->helper);
|
||||
$this->internalConfigHelper = $this->createMock(InternalConfigHelper::class);
|
||||
|
||||
$this->configWriter = new ConfigWriter(
|
||||
$this->config,
|
||||
$this->fileManager,
|
||||
$this->helper,
|
||||
$this->internalConfigHelper
|
||||
);
|
||||
|
||||
$this->configPath = 'somepath';
|
||||
$this->internalConfigPath = 'internalSomepath';
|
||||
|
||||
$this->config
|
||||
->expects($this->any())
|
||||
->method('getConfigPath')
|
||||
->willReturn($this->configPath);
|
||||
|
||||
$this->config
|
||||
->expects($this->any())
|
||||
->method('getInternalConfigPath')
|
||||
->willReturn($this->internalConfigPath);
|
||||
}
|
||||
|
||||
public function testSave1()
|
||||
@@ -97,10 +111,15 @@ class ConfigWriterTest extends \PHPUnit\Framework\TestCase
|
||||
->method('update');
|
||||
|
||||
$this->fileManager
|
||||
->expects($this->once())
|
||||
->method('isFile')
|
||||
->with($this->configPath)
|
||||
->willReturn(true);
|
||||
->withConsecutive(
|
||||
[$this->configPath],
|
||||
[$this->internalConfigPath],
|
||||
)
|
||||
->willReturnOnConsecutiveCalls(
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
$this->fileManager
|
||||
->expects($this->once())
|
||||
|
||||
@@ -70,20 +70,21 @@ class ConfigTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
public function testLoadConfig()
|
||||
{
|
||||
$this->assertArrayHasKey('database', $this->reflection->invokeMethod('loadConfig', array()));
|
||||
$this->assertArrayHasKey('database', $this->reflection->invokeMethod('getData', []));
|
||||
|
||||
$this->assertArrayHasKey('dateFormat', $this->reflection->invokeMethod('loadConfig', array()));
|
||||
$this->assertArrayHasKey('dateFormat', $this->reflection->invokeMethod('getData', []));
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$result = array(
|
||||
$result = [
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => 'localhost',
|
||||
'dbname' => 'espocrm',
|
||||
'user' => 'root',
|
||||
'password' => '',
|
||||
);
|
||||
];
|
||||
|
||||
$this->assertEquals($result, $this->config->get('database'));
|
||||
|
||||
$result = 'pdo_mysql';
|
||||
@@ -102,7 +103,7 @@ class ConfigTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertArrayNotHasKey('systemItems', $configDataWithoutSystem);
|
||||
$this->assertArrayNotHasKey('adminItems', $configDataWithoutSystem);
|
||||
|
||||
$configData = $this->reflection->invokeMethod('loadConfig', array());
|
||||
$configData = $this->reflection->invokeMethod('getData', []);
|
||||
|
||||
$this->assertArrayHasKey('systemItems', $configData);
|
||||
$this->assertArrayHasKey('adminItems', $configData);
|
||||
|
||||
Reference in New Issue
Block a user