diff --git a/database.sql b/database.sql index 19007e6c03..c413f2c24a 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2023.03-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1507 +-- DB_UPDATE_VERSION 1508 -- ------------------------------------------ @@ -494,18 +494,6 @@ CREATE TABLE IF NOT EXISTS `cache` ( INDEX `k_expires` (`k`,`expires`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Stores temporary data'; --- --- TABLE config --- -CREATE TABLE IF NOT EXISTS `config` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `cat` varbinary(50) NOT NULL DEFAULT '' COMMENT '', - `k` varbinary(50) NOT NULL DEFAULT '' COMMENT '', - `v` mediumtext COMMENT '', - PRIMARY KEY(`id`), - UNIQUE INDEX `cat_k` (`cat`,`k`) -) DEFAULT COLLATE utf8mb4_general_ci COMMENT='main configuration storage'; - -- -- TABLE contact-relation -- diff --git a/doc/database.md b/doc/database.md index edfb7b8226..95e0367afe 100644 --- a/doc/database.md +++ b/doc/database.md @@ -18,7 +18,6 @@ Database Tables | [arrived-activity](help/database/db_arrived-activity) | Id of arrived activities | | [attach](help/database/db_attach) | file attachments | | [cache](help/database/db_cache) | Stores temporary data | -| [config](help/database/db_config) | main configuration storage | | [contact](help/database/db_contact) | contact table | | [contact-relation](help/database/db_contact-relation) | Contact relations | | [conv](help/database/db_conv) | private messages | diff --git a/doc/database/db_config.md b/doc/database/db_config.md deleted file mode 100644 index 7d7618794a..0000000000 --- a/doc/database/db_config.md +++ /dev/null @@ -1,25 +0,0 @@ -Table config -=========== - -main configuration storage - -Fields ------- - -| Field | Description | Type | Null | Key | Default | Extra | -| ----- | ----------- | ------------- | ---- | --- | ------- | -------------- | -| id | | int unsigned | NO | PRI | NULL | auto_increment | -| cat | | varbinary(50) | NO | | | | -| k | | varbinary(50) | NO | | | | -| v | | mediumtext | YES | | NULL | | - -Indexes ------------- - -| Name | Fields | -| ------- | -------------- | -| PRIMARY | id | -| cat_k | UNIQUE, cat, k | - - -Return to [database documentation](help/database) diff --git a/src/App/BaseURL.php b/src/App/BaseURL.php index 20fd54916d..f79bae38f9 100644 --- a/src/App/BaseURL.php +++ b/src/App/BaseURL.php @@ -177,7 +177,7 @@ class BaseURL $currURLPath = $this->urlPath; if (!empty($hostname) && $hostname !== $this->hostname) { - if ($this->config->set('config', 'hostname', $hostname)) { + if ($this->config->set('config', 'hostname', $hostname, false)) { $this->hostname = $hostname; } else { return false; @@ -185,40 +185,45 @@ class BaseURL } if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) { - if ($this->config->set('system', 'ssl_policy', $sslPolicy)) { + if ($this->config->set('system', 'ssl_policy', $sslPolicy, false)) { $this->sslPolicy = $sslPolicy; } else { $this->hostname = $currHostname; - $this->config->set('config', 'hostname', $this->hostname); + $this->config->set('config', 'hostname', $this->hostname, false); + $this->config->save(); return false; } } if (isset($urlPath) && $urlPath !== $this->urlPath) { - if ($this->config->set('system', 'urlpath', $urlPath)) { + if ($this->config->set('system', 'urlpath', $urlPath, false)) { $this->urlPath = $urlPath; } else { $this->hostname = $currHostname; $this->sslPolicy = $currSSLPolicy; - $this->config->set('config', 'hostname', $this->hostname); - $this->config->set('system', 'ssl_policy', $this->sslPolicy); + $this->config->set('config', 'hostname', $this->hostname, false); + $this->config->set('system', 'ssl_policy', $this->sslPolicy, false); + $this->config->save(); return false; } } $this->determineBaseUrl(); - if (!$this->config->set('system', 'url', $this->url)) { + if (!$this->config->set('system', 'url', $this->url, false)) { $this->hostname = $currHostname; $this->sslPolicy = $currSSLPolicy; $this->urlPath = $currURLPath; $this->determineBaseUrl(); - $this->config->set('config', 'hostname', $this->hostname); - $this->config->set('system', 'ssl_policy', $this->sslPolicy); - $this->config->set('system', 'urlpath', $this->urlPath); + $this->config->set('config', 'hostname', $this->hostname, false); + $this->config->set('system', 'ssl_policy', $this->sslPolicy, false); + $this->config->set('system', 'urlpath', $this->urlPath, false); + $this->config->save(); return false; } + $this->config->save(); + return true; } @@ -295,17 +300,21 @@ class BaseURL $this->sslPolicy = $this->config->get('system', 'ssl_policy'); $this->url = $this->config->get('system', 'url'); + $savable = false; + if (empty($this->hostname)) { $this->determineHostname(); if (!empty($this->hostname)) { - $this->config->set('config', 'hostname', $this->hostname); + $this->config->set('config', 'hostname', $this->hostname, false); + $savable = true; } } if (!isset($this->urlPath)) { $this->determineURLPath(); - $this->config->set('system', 'urlpath', $this->urlPath); + $this->config->set('system', 'urlpath', $this->urlPath, false); + $savable = true; } if (!isset($this->sslPolicy)) { @@ -314,16 +323,22 @@ class BaseURL } else { $this->sslPolicy = self::DEFAULT_SSL_SCHEME; } - $this->config->set('system', 'ssl_policy', $this->sslPolicy); + $this->config->set('system', 'ssl_policy', $this->sslPolicy, false); + $savable = true; } if (empty($this->url)) { $this->determineBaseUrl(); if (!empty($this->url)) { - $this->config->set('system', 'url', $this->url); + $this->config->set('system', 'url', $this->url, false); + $savable = true; } } + + if ($savable) { + $this->config->save(); + } } /** diff --git a/src/Console/Maintenance.php b/src/Console/Maintenance.php index 97ce9c27fa..bd3aef7c29 100644 --- a/src/Console/Maintenance.php +++ b/src/Console/Maintenance.php @@ -100,16 +100,18 @@ HELP; $enabled = intval($this->getArgument(0)); - $this->config->set('system', 'maintenance', $enabled); + $this->config->set('system', 'maintenance', $enabled, false); $reason = $this->getArgument(1); if ($enabled && $this->getArgument(1)) { - $this->config->set('system', 'maintenance_reason', $this->getArgument(1)); + $this->config->set('system', 'maintenance_reason', $this->getArgument(1), false); } else { - $this->config->set('system', 'maintenance_reason', ''); + $this->config->set('system', 'maintenance_reason', '', false); } + $this->config->save(); + if ($enabled) { $mode_str = "maintenance mode"; } else { diff --git a/src/Console/Relocate.php b/src/Console/Relocate.php index 3d13e10a0b..8a76c92070 100644 --- a/src/Console/Relocate.php +++ b/src/Console/Relocate.php @@ -101,8 +101,8 @@ HELP; $old_host = str_replace('http://', '@', Strings::normaliseLink($old_url)); $this->out('Entering maintenance mode'); - $this->config->set('system', 'maintenance', true); - $this->config->set('system', 'maintenance_reason', 'Relocating node to ' . $new_url); + $this->config->set('system', 'maintenance', true, false); + $this->config->set('system', 'maintenance_reason', 'Relocating node to ' . $new_url, false); try { if (!$this->database->transaction()) { @@ -189,8 +189,9 @@ HELP; return 1; } finally { $this->out('Leaving maintenance mode'); - $this->config->set('system', 'maintenance', false); - $this->config->set('system', 'maintenance_reason', ''); + $this->config->set('system', 'maintenance', false, false); + $this->config->set('system', 'maintenance_reason', '', false); + $this->config->save(); } // send relocate diff --git a/src/Core/Config/Capability/IManageConfigValues.php b/src/Core/Config/Capability/IManageConfigValues.php index ecfb2a7aa5..27238822ae 100644 --- a/src/Core/Config/Capability/IManageConfigValues.php +++ b/src/Core/Config/Capability/IManageConfigValues.php @@ -71,12 +71,18 @@ interface IManageConfigValues * @param string $cat The category of the configuration value * @param string $key The configuration key to set * @param mixed $value The value to store + * @param bool $autosave If true, implicit save the value * * @return bool Operation success * * @throws ConfigPersistenceException In case the persistence layer throws errors */ - public function set(string $cat, string $key, $value): bool; + public function set(string $cat, string $key, $value, bool $autosave = true): bool; + + /** + * Save back the overridden values of the config cache + */ + public function save(); /** * Deletes the given key from the system configuration. @@ -85,13 +91,14 @@ interface IManageConfigValues * * @param string $cat The category of the configuration value * @param string $key The configuration key to delete + * @param bool $autosave If true, implicit save the value * * @return bool * * @throws ConfigPersistenceException In case the persistence layer throws errors * */ - public function delete(string $cat, string $key): bool; + public function delete(string $cat, string $key, bool $autosave = true): bool; /** * Returns the Config Cache diff --git a/src/Core/Config/Factory/Config.php b/src/Core/Config/Factory/Config.php index 94293dd173..fac931fac5 100644 --- a/src/Core/Config/Factory/Config.php +++ b/src/Core/Config/Factory/Config.php @@ -21,12 +21,12 @@ namespace Friendica\Core\Config\Factory; -use Friendica\Core\Config\Capability; -use Friendica\Core\Config\Repository; -use Friendica\Core\Config\Type; use Friendica\Core\Config\Util; use Friendica\Core\Config\ValueObject\Cache; +/** + * The config factory for creating either the cache or the whole model + */ class Config { /** @@ -54,9 +54,9 @@ class Config * @param string $basePath The basepath of FRIENDICA * @param array $server The $_SERVER array * - * @return Util\ConfigFileLoader + * @return Util\ConfigFileManager */ - public function createConfigFileLoader(string $basePath, array $server = []): Util\ConfigFileLoader + public function createConfigFileLoader(string $basePath, array $server = []): Util\ConfigFileManager { if (!empty($server[self::CONFIG_DIR_ENV]) && is_dir($server[self::CONFIG_DIR_ENV])) { $configDir = $server[self::CONFIG_DIR_ENV]; @@ -65,19 +65,19 @@ class Config } $staticDir = $basePath . DIRECTORY_SEPARATOR . self::STATIC_DIR; - return new Util\ConfigFileLoader($basePath, $configDir, $staticDir); + return new Util\ConfigFileManager($basePath, $configDir, $staticDir, new Util\ConfigFileTransformer()); } /** - * @param Util\ConfigFileLoader $loader The Config Cache loader (INI/config/.htconfig) - * @param array $server + * @param Util\ConfigFileManager $configFileManager The Config Cache manager (INI/config/.htconfig) + * @param array $server * * @return Cache */ - public function createCache(Util\ConfigFileLoader $loader, array $server = []): Cache + public function createCache(Util\ConfigFileManager $configFileManager, array $server = []): Cache { $configCache = new Cache(); - $loader->setupCache($configCache, $server); + $configFileManager->setupCache($configCache, $server); return $configCache; } @@ -88,12 +88,12 @@ class Config * * @return Capability\IManageConfigValues */ - public function create(Cache $configCache, Repository\Config $configRepo) + public function create(Util\ConfigFileManager $loader, Cache $configCache, Repository\Config $configRepo) { if ($configCache->get('system', 'config_adapter') === 'preload') { - $configuration = new Type\PreloadConfig($configCache, $configRepo); + $configuration = new Type\PreloadConfig($loader, $configCache, $configRepo); } else { - $configuration = new Type\JitConfig($configCache, $configRepo); + $configuration = new Type\JitConfig($loader, $configCache, $configRepo); } return $configuration; diff --git a/src/Core/Config/Repository/Config.php b/src/Core/Config/Repository/Config.php index 3bec99f843..eabff9e68f 100644 --- a/src/Core/Config/Repository/Config.php +++ b/src/Core/Config/Repository/Config.php @@ -51,7 +51,7 @@ class Config */ public function isConnected(): bool { - return $this->db->isConnected() && !$this->mode->isInstall(); + return true; } /** @@ -65,31 +65,7 @@ class Config */ public function load(?string $cat = null): array { - $return = []; - - try { - if (empty($cat)) { - $configs = $this->db->select(static::$table_name, ['cat', 'v', 'k']); - } else { - $configs = $this->db->select(static::$table_name, ['cat', 'v', 'k'], ['cat' => $cat]); - } - - while ($config = $this->db->fetch($configs)) { - $key = $config['k']; - $value = ValueConversion::toConfigValue($config['v']); - - // just save it in case it is set - if (isset($value)) { - $return[$config['cat']][$key] = $value; - } - } - } catch (\Exception $exception) { - throw new ConfigPersistenceException(sprintf('Cannot load config category %s', $cat), $exception); - } finally { - $this->db->close($configs); - } - - return $return; + return []; } /** @@ -107,24 +83,6 @@ class Config */ public function get(string $cat, string $key) { - if (!$this->isConnected()) { - return null; - } - - try { - $config = $this->db->selectFirst(static::$table_name, ['v'], ['cat' => $cat, 'k' => $key]); - if ($this->db->isResult($config)) { - $value = ValueConversion::toConfigValue($config['v']); - - // just return it in case it is set - if (isset($value)) { - return $value; - } - } - } catch (\Exception $exception) { - throw new ConfigPersistenceException(sprintf('Cannot get config with category %s and key %s', $cat, $key), $exception); - } - return null; } @@ -143,27 +101,7 @@ class Config */ public function set(string $cat, string $key, $value): bool { - if (!$this->isConnected()) { - return false; - } - - // We store our setting values in a string variable. - // So we have to do the conversion here so that the compare below works. - // The exception are array values. - $compare_value = (!is_array($value) ? (string)$value : $value); - $stored_value = $this->get($cat, $key); - - if (isset($stored_value) && ($stored_value === $compare_value)) { - return true; - } - - $dbValue = ValueConversion::toDbValue($value); - - try { - return $this->db->update(static::$table_name, ['v' => $dbValue], ['cat' => $cat, 'k' => $key], true); - } catch (\Exception $exception) { - throw new ConfigPersistenceException(sprintf('Cannot set config with category %s and key %s', $cat, $key), $exception); - } + return true; } /** @@ -178,14 +116,6 @@ class Config */ public function delete(string $cat, string $key): bool { - if (!$this->isConnected()) { - return false; - } - - try { - return $this->db->delete(static::$table_name, ['cat' => $cat, 'k' => $key]); - } catch (\Exception $exception) { - throw new ConfigPersistenceException(sprintf('Cannot delete config with category %s and key %s', $cat, $key), $exception); - } + return true; } } diff --git a/src/Core/Config/Type/AbstractConfig.php b/src/Core/Config/Type/AbstractConfig.php index 3ab4f2c5e7..fa98dd7097 100644 --- a/src/Core/Config/Type/AbstractConfig.php +++ b/src/Core/Config/Type/AbstractConfig.php @@ -22,8 +22,10 @@ namespace Friendica\Core\Config\Type; use Friendica\Core\Config\Repository\Config; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\DI; /** * This class is responsible for all system-wide configuration values in Friendica @@ -43,14 +45,19 @@ abstract class AbstractConfig implements IManageConfigValues */ protected $configRepo; + /** @var ConfigFileManager */ + protected $configFileManager; + /** + * @param ConfigFileManager $configFileManager The configuration file manager to save back configs * @param Cache $configCache The configuration cache (based on the config-files) * @param Config $configRepo The configuration repository */ - public function __construct(Cache $configCache, Config $configRepo) + public function __construct(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) { - $this->configCache = $configCache; - $this->configRepo = $configRepo; + $this->configFileManager = $configFileManager; + $this->configCache = $configCache; + $this->configRepo = $configRepo; } /** @@ -60,4 +67,9 @@ abstract class AbstractConfig implements IManageConfigValues { return $this->configCache; } + + public function save() + { + $this->configFileManager->saveData($this->configCache); + } } diff --git a/src/Core/Config/Type/JitConfig.php b/src/Core/Config/Type/JitConfig.php index 68b437b243..1ae9abd2ee 100644 --- a/src/Core/Config/Type/JitConfig.php +++ b/src/Core/Config/Type/JitConfig.php @@ -21,6 +21,7 @@ namespace Friendica\Core\Config\Type; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Core\Config\Repository\Config; @@ -39,12 +40,13 @@ class JitConfig extends AbstractConfig private $db_loaded; /** - * @param Cache $configCache The configuration cache (based on the config-files) - * @param Config $configRepo The configuration model + * @param ConfigFileManager $configFileManager The configuration file manager to save back configs + * @param Cache $configCache The configuration cache (based on the config-files) + * @param Config $configRepo The configuration model */ - public function __construct(Cache $configCache, Config $configRepo) + public function __construct(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) { - parent::__construct($configCache, $configRepo); + parent::__construct($configFileManager, $configCache, $configRepo); $this->db_loaded = []; $this->load(); @@ -69,7 +71,7 @@ class JitConfig extends AbstractConfig } // load the whole category out of the DB into the cache - $this->configCache->load($config, Cache::SOURCE_DB); + $this->configCache->load($config, Cache::SOURCE_DATA); } /** @@ -84,7 +86,7 @@ class JitConfig extends AbstractConfig $dbValue = $this->configRepo->get($cat, $key); if (isset($dbValue)) { - $this->configCache->set($cat, $key, $dbValue, Cache::SOURCE_DB); + $this->configCache->set($cat, $key, $dbValue, Cache::SOURCE_DATA); unset($dbValue); } @@ -100,10 +102,10 @@ class JitConfig extends AbstractConfig /** * {@inheritDoc} */ - public function set(string $cat, string $key, $value): bool + public function set(string $cat, string $key, $value, bool $autosave = true): bool { // set the cache first - $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DB); + $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); // If there is no connected adapter, we're finished if (!$this->configRepo->isConnected()) { @@ -114,13 +116,17 @@ class JitConfig extends AbstractConfig $this->db_loaded[$cat][$key] = $stored; + if ($autosave) { + $this->save(); + } + return $cached && $stored; } /** * {@inheritDoc} */ - public function delete(string $cat, string $key): bool + public function delete(string $cat, string $key, bool $autosave = true): bool { $cacheRemoved = $this->configCache->delete($cat, $key); @@ -134,6 +140,10 @@ class JitConfig extends AbstractConfig $storeRemoved = $this->configRepo->delete($cat, $key); + if ($autosave) { + $this->save(); + } + return $cacheRemoved || $storeRemoved; } } diff --git a/src/Core/Config/Type/PreloadConfig.php b/src/Core/Config/Type/PreloadConfig.php index 57a0d439df..6eed20af89 100644 --- a/src/Core/Config/Type/PreloadConfig.php +++ b/src/Core/Config/Type/PreloadConfig.php @@ -21,6 +21,7 @@ namespace Friendica\Core\Config\Type; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Core\Config\Repository\Config; @@ -36,12 +37,13 @@ class PreloadConfig extends AbstractConfig private $config_loaded; /** - * @param Cache $configCache The configuration cache (based on the config-files) - * @param Config $configRepo The configuration model + * @param ConfigFileManager $configFileManager The configuration file manager to save back configs + * @param Cache $configCache The configuration cache (based on the config-files) + * @param Config $configRepo The configuration model */ - public function __construct(Cache $configCache, Config $configRepo) + public function __construct(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) { - parent::__construct($configCache, $configRepo); + parent::__construct($configFileManager, $configCache, $configRepo); $this->config_loaded = false; $this->load(); @@ -68,7 +70,7 @@ class PreloadConfig extends AbstractConfig $this->config_loaded = true; // load the whole category out of the DB into the cache - $this->configCache->load($config, Cache::SOURCE_DB); + $this->configCache->load($config, Cache::SOURCE_DATA); } /** @@ -80,7 +82,7 @@ class PreloadConfig extends AbstractConfig if ($this->configRepo->isConnected()) { $config = $this->configRepo->get($cat, $key); if (isset($config)) { - $this->configCache->set($cat, $key, $config, Cache::SOURCE_DB); + $this->configCache->set($cat, $key, $config, Cache::SOURCE_DATA); } } } @@ -94,14 +96,14 @@ class PreloadConfig extends AbstractConfig /** * {@inheritDoc} */ - public function set(string $cat, string $key, $value): bool + public function set(string $cat, string $key, $value, bool $autosave = true): bool { if (!$this->config_loaded) { $this->load(); } // set the cache first - $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DB); + $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); // If there is no connected adapter, we're finished if (!$this->configRepo->isConnected()) { @@ -110,13 +112,17 @@ class PreloadConfig extends AbstractConfig $stored = $this->configRepo->set($cat, $key, $value); + if ($autosave) { + $this->save(); + } + return $cached && $stored; } /** * {@inheritDoc} */ - public function delete(string $cat, string $key): bool + public function delete(string $cat, string $key, bool $autosave = true): bool { if ($this->config_loaded) { $this->load(); @@ -130,6 +136,10 @@ class PreloadConfig extends AbstractConfig $storeRemoved = $this->configRepo->delete($cat, $key); + if ($autosave) { + $this->save(); + } + return $cacheRemoved || $storeRemoved; } } diff --git a/src/Core/Config/Util/ConfigFileLoader.php b/src/Core/Config/Util/ConfigFileManager.php similarity index 83% rename from src/Core/Config/Util/ConfigFileLoader.php rename to src/Core/Config/Util/ConfigFileManager.php index acf6efa34a..82de4a99cb 100644 --- a/src/Core/Config/Util/ConfigFileLoader.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -26,22 +26,15 @@ use Friendica\Core\Config\Exception\ConfigFileException; use Friendica\Core\Config\ValueObject\Cache; /** - * The ConfigFileLoader loads config-files and stores them in a ConfigCache ( @see Cache ) + * The ConfigFileLoader loads and saves config-files and stores them in a ConfigCache ( @see Cache ) * * It is capable of loading the following config files: * - *.config.php (current) * - *.ini.php (deprecated) * - *.htconfig.php (deprecated) */ -class ConfigFileLoader +class ConfigFileManager { - /** - * The default name of the user defined ini file - * - * @var string - */ - const CONFIG_INI = 'local'; - /** * The default name of the user defined legacy config file * @@ -49,6 +42,13 @@ class ConfigFileLoader */ const CONFIG_HTCONFIG = 'htconfig'; + /** + * The config file, where overrides per admin page/console are saved at + * + * @var string + */ + const CONFIG_DATA_FILE = 'node.config.php'; + /** * The sample string inside the configs, which shouldn't get loaded * @@ -89,7 +89,7 @@ class ConfigFileLoader * * @param Cache $config The config cache to load to * @param array $server The $_SERVER array - * @param bool $raw Setup the raw config format + * @param bool $raw Set up the raw config format * * @throws ConfigFileException */ @@ -106,6 +106,9 @@ class ConfigFileLoader // Now load every other config you find inside the 'config/' directory $this->loadCoreConfig($config); + // Now load the node.config.php file with the node specific config values (based on admin gui/console actions) + $this->loadDataConfig($config); + $config->load($this->loadEnvConfig($server), Cache::SOURCE_ENV); // In case of install mode, add the found basepath (because there isn't a basepath set yet @@ -158,6 +161,50 @@ class ConfigFileLoader } } + /** + * Tries to load the data config file with the overridden data + * + * @param Cache $config The Config cache + * + * @throws ConfigFileException In case the config file isn't loadable + */ + private function loadDataConfig(Cache $config) + { + $filename = $this->configDir . '/' . self::CONFIG_DATA_FILE; + + if (file_exists($filename)) { + $dataArray = include $filename; + + if (!is_array($dataArray)) { + throw new ConfigFileException(sprintf('Error loading config file %s', $filename)); + } + + $config->load($dataArray, Cache::SOURCE_DATA); + } + } + + /** + * Saves overridden config entries back into the data.config.phpR + * + * @param Cache $config The config cache + * + * @throws ConfigFileException In case the config file isn't writeable or the data is invalid + */ + public function saveData(Cache $config) + { + $data = $config->getDataBySource(Cache::SOURCE_DATA); + + $encodedData = ConfigFileTransformer::encode($data); + + if (!$encodedData) { + throw new ConfigFileException('config source cannot get encoded'); + } + + if (!file_put_contents($this->configDir . '/' . self::CONFIG_DATA_FILE, $encodedData)) { + throw new ConfigFileException(sprintf('Cannot save data to file %s/%s', $this->configDir, self::CONFIG_DATA_FILE)); + } + } + /** * Tries to load the specified addon-configuration and returns the config array. * @@ -353,12 +400,16 @@ class ConfigFileLoader */ private function loadConfigFile(string $filepath): array { - $config = include($filepath); + if (file_exists($filepath)) { + $config = include($filepath); - if (!is_array($config)) { - throw new ConfigFileException('Error loading config file ' . $filepath); + if (!is_array($config)) { + throw new ConfigFileException('Error loading config file ' . $filepath); + } + + return $config; + } else { + return []; } - - return $config; } } diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index 00f8ad045d..c427996c35 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -21,13 +21,13 @@ namespace Friendica\Core\Config\ValueObject; -use Friendica\Core\Config\Util\ConfigFileLoader; +use Friendica\Core\Config\Util\ConfigFileManager; use ParagonIE\HiddenString\HiddenString; /** * The Friendica config cache for the application * Initial, all *.config.php files are loaded into this cache with the - * ConfigFileLoader ( @see ConfigFileLoader ) + * ConfigFileManager ( @see ConfigFileManager ) */ class Cache { @@ -35,8 +35,8 @@ class Cache const SOURCE_STATIC = 0; /** @var int Indicates that the cache entry is set by file - Low Priority */ const SOURCE_FILE = 1; - /** @var int Indicates that the cache entry is set by the DB config table - Middle Priority */ - const SOURCE_DB = 2; + /** @var int Indicates that the cache entry is manually set by the application (per admin page/console) - Middle Priority */ + const SOURCE_DATA = 2; /** @var int Indicates that the cache entry is set by a server environment variable - High Priority */ const SOURCE_ENV = 3; /** @var int Indicates that the cache entry is fixed and must not be changed */ @@ -128,6 +128,34 @@ class Cache return $this->source[$cat][$key] ?? -1; } + /** + * Returns the whole config array based on the given source type + * + * @param int $source Indicates the source of the config entry + * + * @return array The config array part of the given source + */ + public function getDataBySource(int $source): array + { + $data = []; + + $categories = array_keys($this->source); + + foreach ($categories as $category) { + if (is_array($this->source[$category])) { + $keys = array_keys($this->source[$category]); + + foreach ($keys as $key) { + if ($this->source[$category][$key] === $source) { + $data[$category][$key] = $this->config[$category][$key]; + } + } + } + } + + return $data; + } + /** * Sets a value in the config cache. Accepts raw output from the config table * diff --git a/src/Core/KeyValueStorage/Type/DBKeyValueStorage.php b/src/Core/KeyValueStorage/Type/DBKeyValueStorage.php index 35be8b43e2..d31f3c1ced 100644 --- a/src/Core/KeyValueStorage/Type/DBKeyValueStorage.php +++ b/src/Core/KeyValueStorage/Type/DBKeyValueStorage.php @@ -21,7 +21,7 @@ namespace Friendica\Core\KeyValueStorage\Type; -use Friendica\Core\Config\Util\ValueConversion; +use Friendica\Core\PConfig\Util\ValueConversion; use Friendica\Core\KeyValueStorage\Exceptions\KeyValueStoragePersistenceException; use Friendica\Database\Database; diff --git a/src/Core/Update.php b/src/Core/Update.php index 4504381183..8da473839d 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -160,8 +160,9 @@ class Update Logger::warning('Pre update failed', ['version' => $version]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', 0); - DI::config()->set('system', 'maintenance_reason', ''); + DI::config()->set('system', 'maintenance', false, false); + DI::config()->delete('system', 'maintenance_reason', false); + DI::config()->save(); return $r; } else { Logger::notice('Pre update executed.', ['version' => $version]); @@ -181,8 +182,9 @@ class Update Logger::error('Update ERROR.', ['from' => $stored, 'to' => $current, 'retval' => $retval]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', 0); - DI::config()->set('system', 'maintenance_reason', ''); + DI::config()->set('system', 'maintenance', false, false); + DI::config()->delete('system', 'maintenance_reason', false); + DI::config()->save(); return $retval; } else { Logger::notice('Database structure update finished.', ['from' => $stored, 'to' => $current]); @@ -198,8 +200,9 @@ class Update Logger::warning('Post update failed', ['version' => $version]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', 0); - DI::config()->set('system', 'maintenance_reason', ''); + DI::config()->set('system', 'maintenance', false, false); + DI::config()->delete('system', 'maintenance_reason', false); + DI::config()->save(); return $r; } else { DI::config()->set('system', 'build', $version); @@ -210,8 +213,9 @@ class Update DI::config()->set('system', 'build', $current); DI::config()->set('system', 'update', Update::SUCCESS); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', 0); - DI::config()->set('system', 'maintenance_reason', ''); + DI::config()->set('system', 'maintenance', false, false); + DI::config()->delete('system', 'maintenance_reason', false); + DI::config()->save(); Logger::notice('Update success.', ['from' => $stored, 'to' => $current]); if ($sendMail) { diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index 645084a96f..e3af408b9e 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -74,7 +74,7 @@ class DBStructure $old_tables = ['fserver', 'gcign', 'gcontact', 'gcontact-relation', 'gfollower' ,'glink', 'item-delivery-data', 'item-activity', 'item-content', 'item_id', 'participation', 'poll', 'poll_result', 'queue', 'retriever_rule', 'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item', 'challenge', - 'auth_codes', 'tokens', 'clients', 'profile_check', 'host', 'conversation', 'fcontact']; + 'auth_codes', 'tokens', 'clients', 'profile_check', 'host', 'conversation', 'fcontact', 'config']; $tables = DBA::selectToArray('INFORMATION_SCHEMA.TABLES', ['TABLE_NAME'], ['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']); @@ -176,14 +176,15 @@ class DBStructure public static function performUpdate(bool $enable_maintenance_mode = true, bool $verbose = false): string { if ($enable_maintenance_mode) { - DI::config()->set('system', 'maintenance', 1); + DI::config()->set('system', 'maintenance', true); } $status = self::update($verbose, true); if ($enable_maintenance_mode) { - DI::config()->set('system', 'maintenance', 0); - DI::config()->set('system', 'maintenance_reason', ''); + DI::config()->set('system', 'maintenance', false, false); + DI::config()->delete('system', 'maintenance_reason', false); + DI::config()->save(); } return $status; diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index 194d2cb2c7..cf2f6c5358 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -148,7 +148,7 @@ class Site extends BaseAdmin // Has the directory url changed? If yes, then resubmit the existing profiles there if ($global_directory != DI::config()->get('system', 'directory') && ($global_directory != '')) { - DI::config()->set('system', 'directory', $global_directory); + DI::config()->set('system', 'directory', $global_directory, false); Worker::add(Worker::PRIORITY_LOW, 'Directory'); } @@ -194,131 +194,133 @@ class Site extends BaseAdmin ); } } - DI::config()->set('system', 'ssl_policy' , $ssl_policy); - DI::config()->set('system', 'maxloadavg' , $maxloadavg); - DI::config()->set('system', 'min_memory' , $min_memory); - DI::config()->set('system', 'optimize_tables' , $optimize_tables); - DI::config()->set('system', 'contact_discovery' , $contact_discovery); - DI::config()->set('system', 'synchronize_directory' , $synchronize_directory); - DI::config()->set('system', 'poco_requery_days' , $poco_requery_days); - DI::config()->set('system', 'poco_discovery' , $poco_discovery); - DI::config()->set('system', 'poco_local_search' , $poco_local_search); - DI::config()->set('system', 'nodeinfo' , $nodeinfo); - DI::config()->set('config', 'sitename' , $sitename); - DI::config()->set('config', 'sender_email' , $sender_email); - DI::config()->set('system', 'suppress_tags' , $suppress_tags); - DI::config()->set('system', 'shortcut_icon' , $shortcut_icon); - DI::config()->set('system', 'touch_icon' , $touch_icon); + DI::config()->set('system', 'ssl_policy' , $ssl_policy, false); + DI::config()->set('system', 'maxloadavg' , $maxloadavg, false); + DI::config()->set('system', 'min_memory' , $min_memory, false); + DI::config()->set('system', 'optimize_tables' , $optimize_tables, false); + DI::config()->set('system', 'contact_discovery' , $contact_discovery, false); + DI::config()->set('system', 'synchronize_directory' , $synchronize_directory, false); + DI::config()->set('system', 'poco_requery_days' , $poco_requery_days, false); + DI::config()->set('system', 'poco_discovery' , $poco_discovery, false); + DI::config()->set('system', 'poco_local_search' , $poco_local_search, false); + DI::config()->set('system', 'nodeinfo' , $nodeinfo, false); + DI::config()->set('config', 'sitename' , $sitename, false); + DI::config()->set('config', 'sender_email' , $sender_email, false); + DI::config()->set('system', 'suppress_tags' , $suppress_tags, false); + DI::config()->set('system', 'shortcut_icon' , $shortcut_icon, false); + DI::config()->set('system', 'touch_icon' , $touch_icon, false); if ($banner == "") { - DI::config()->delete('system', 'banner'); + DI::config()->set('system', 'banner', false); } else { - DI::config()->set('system', 'banner', $banner); + DI::config()->set('system', 'banner', $banner, false); } if (empty($email_banner)) { - DI::config()->delete('system', 'email_banner'); + DI::config()->set('system', 'email_banner', false); } else { - DI::config()->set('system', 'email_banner', $email_banner); + DI::config()->set('system', 'email_banner', $email_banner, false); } if (empty($additional_info)) { - DI::config()->delete('config', 'info'); + DI::config()->set('config', 'info', false); } else { - DI::config()->set('config', 'info', $additional_info); + DI::config()->set('config', 'info', $additional_info, false); } - DI::config()->set('system', 'language', $language); - DI::config()->set('system', 'theme', $theme); + DI::config()->set('system', 'language', $language, false); + DI::config()->set('system', 'theme', $theme, false); Theme::install($theme); if ($theme_mobile == '---') { - DI::config()->delete('system', 'mobile-theme'); + DI::config()->set('system', 'mobile-theme', false); } else { - DI::config()->set('system', 'mobile-theme', $theme_mobile); + DI::config()->set('system', 'mobile-theme', $theme_mobile, false); } if ($singleuser == '---') { - DI::config()->delete('system', 'singleuser'); + DI::config()->set('system', 'singleuser', false); } else { - DI::config()->set('system', 'singleuser', $singleuser); + DI::config()->set('system', 'singleuser', $singleuser, false); } if (preg_match('/\d+(?:\s*[kmg])?/i', $maximagesize)) { - DI::config()->set('system', 'maximagesize', $maximagesize); + DI::config()->set('system', 'maximagesize', $maximagesize, false); } else { DI::sysmsg()->addNotice(DI::l10n()->t('%s is no valid input for maximum image size', $maximagesize)); } - DI::config()->set('system', 'max_image_length' , $maximagelength); - DI::config()->set('system', 'jpeg_quality' , $jpegimagequality); + DI::config()->set('system', 'max_image_length' , $maximagelength, false); + DI::config()->set('system', 'jpeg_quality' , $jpegimagequality, false); - DI::config()->set('config', 'register_policy' , $register_policy); - DI::config()->set('system', 'max_daily_registrations', $daily_registrations); - DI::config()->set('system', 'account_abandon_days' , $abandon_days); - DI::config()->set('config', 'register_text' , $register_text); - DI::config()->set('system', 'allowed_sites' , $allowed_sites); - DI::config()->set('system', 'allowed_email' , $allowed_email); - DI::config()->set('system', 'forbidden_nicknames' , $forbidden_nicknames); - DI::config()->set('system', 'system_actor_name' , $system_actor_name); - DI::config()->set('system', 'no_oembed_rich_content' , $no_oembed_rich_content); - DI::config()->set('system', 'allowed_oembed' , $allowed_oembed); - DI::config()->set('system', 'block_public' , $block_public); - DI::config()->set('system', 'publish_all' , $force_publish); - DI::config()->set('system', 'newuser_private' , $newuser_private); - DI::config()->set('system', 'enotify_no_content' , $enotify_no_content); - DI::config()->set('system', 'disable_embedded' , $disable_embedded); - DI::config()->set('system', 'allow_users_remote_self', $allow_users_remote_self); - DI::config()->set('system', 'explicit_content' , $explicit_content); - DI::config()->set('system', 'proxify_content' , $proxify_content); - DI::config()->set('system', 'cache_contact_avatar' , $cache_contact_avatar); - DI::config()->set('system', 'check_new_version_url' , $check_new_version_url); + DI::config()->set('config', 'register_policy' , $register_policy, false); + DI::config()->set('system', 'max_daily_registrations', $daily_registrations, false); + DI::config()->set('system', 'account_abandon_days' , $abandon_days, false); + DI::config()->set('config', 'register_text' , $register_text, false); + DI::config()->set('system', 'allowed_sites' , $allowed_sites, false); + DI::config()->set('system', 'allowed_email' , $allowed_email, false); + DI::config()->set('system', 'forbidden_nicknames' , $forbidden_nicknames, false); + DI::config()->set('system', 'system_actor_name' , $system_actor_name, false); + DI::config()->set('system', 'no_oembed_rich_content' , $no_oembed_rich_content, false); + DI::config()->set('system', 'allowed_oembed' , $allowed_oembed, false); + DI::config()->set('system', 'block_public' , $block_public, false); + DI::config()->set('system', 'publish_all' , $force_publish, false); + DI::config()->set('system', 'newuser_private' , $newuser_private, false); + DI::config()->set('system', 'enotify_no_content' , $enotify_no_content, false); + DI::config()->set('system', 'disable_embedded' , $disable_embedded, false); + DI::config()->set('system', 'allow_users_remote_self', $allow_users_remote_self, false); + DI::config()->set('system', 'explicit_content' , $explicit_content, false); + DI::config()->set('system', 'proxify_content' , $proxify_content, false); + DI::config()->set('system', 'cache_contact_avatar' , $cache_contact_avatar, false); + DI::config()->set('system', 'check_new_version_url' , $check_new_version_url, false); - DI::config()->set('system', 'block_extended_register', !$enable_multi_reg); - DI::config()->set('system', 'no_openid' , !$enable_openid); - DI::config()->set('system', 'no_regfullname' , !$enable_regfullname); - DI::config()->set('system', 'register_notification' , $register_notification); - DI::config()->set('system', 'community_page_style' , $community_page_style); - DI::config()->set('system', 'max_author_posts_community_page', $max_author_posts_community_page); - DI::config()->set('system', 'verifyssl' , $verifyssl); - DI::config()->set('system', 'proxyuser' , $proxyuser); - DI::config()->set('system', 'proxy' , $proxy); - DI::config()->set('system', 'curl_timeout' , $timeout); - DI::config()->set('system', 'imap_disabled' , !$mail_enabled && function_exists('imap_open')); - DI::config()->set('system', 'ostatus_disabled' , !$ostatus_enabled); - DI::config()->set('system', 'diaspora_enabled' , $diaspora_enabled); + DI::config()->set('system', 'block_extended_register', !$enable_multi_reg, false); + DI::config()->set('system', 'no_openid' , !$enable_openid, false); + DI::config()->set('system', 'no_regfullname' , !$enable_regfullname, false); + DI::config()->set('system', 'register_notification' , $register_notification, false); + DI::config()->set('system', 'community_page_style' , $community_page_style, false); + DI::config()->set('system', 'max_author_posts_community_page', $max_author_posts_community_page, false); + DI::config()->set('system', 'verifyssl' , $verifyssl, false); + DI::config()->set('system', 'proxyuser' , $proxyuser, false); + DI::config()->set('system', 'proxy' , $proxy, false); + DI::config()->set('system', 'curl_timeout' , $timeout, false); + DI::config()->set('system', 'imap_disabled' , !$mail_enabled && function_exists('imap_open'), false); + DI::config()->set('system', 'ostatus_disabled' , !$ostatus_enabled, false); + DI::config()->set('system', 'diaspora_enabled' , $diaspora_enabled, false); - DI::config()->set('config', 'private_addons' , $private_addons); + DI::config()->set('config', 'private_addons' , $private_addons, false); - DI::config()->set('system', 'force_ssl' , $force_ssl); - DI::config()->set('system', 'hide_help' , !$show_help); + DI::config()->set('system', 'force_ssl' , $force_ssl, false); + DI::config()->set('system', 'hide_help' , !$show_help, false); - DI::config()->set('system', 'dbclean' , $dbclean); - DI::config()->set('system', 'dbclean-expire-days' , $dbclean_expire_days); - DI::config()->set('system', 'dbclean_expire_conversation', $dbclean_expire_conv); + DI::config()->set('system', 'dbclean' , $dbclean, false); + DI::config()->set('system', 'dbclean-expire-days' , $dbclean_expire_days, false); + DI::config()->set('system', 'dbclean_expire_conversation', $dbclean_expire_conv, false); if ($dbclean_unclaimed == 0) { $dbclean_unclaimed = $dbclean_expire_days; } - DI::config()->set('system', 'dbclean-expire-unclaimed', $dbclean_unclaimed); + DI::config()->set('system', 'dbclean-expire-unclaimed', $dbclean_unclaimed, false); - DI::config()->set('system', 'max_comments', $max_comments); - DI::config()->set('system', 'max_display_comments', $max_display_comments); + DI::config()->set('system', 'max_comments', $max_comments, false); + DI::config()->set('system', 'max_display_comments', $max_display_comments, false); if ($temppath != '') { $temppath = BasePath::getRealPath($temppath); } - DI::config()->set('system', 'temppath', $temppath); + DI::config()->set('system', 'temppath', $temppath, false); - DI::config()->set('system', 'only_tag_search' , $only_tag_search); - DI::config()->set('system', 'compute_group_counts', $compute_group_counts); + DI::config()->set('system', 'only_tag_search' , $only_tag_search, false); + DI::config()->set('system', 'compute_group_counts', $compute_group_counts, false); - DI::config()->set('system', 'worker_queues' , $worker_queues); - DI::config()->set('system', 'worker_fastlane' , $worker_fastlane); + DI::config()->set('system', 'worker_queues' , $worker_queues, false); + DI::config()->set('system', 'worker_fastlane' , $worker_fastlane, false); - DI::config()->set('system', 'relay_directly' , $relay_directly); - DI::config()->set('system', 'relay_scope' , $relay_scope); - DI::config()->set('system', 'relay_server_tags', $relay_server_tags); - DI::config()->set('system', 'relay_deny_tags' , $relay_deny_tags); - DI::config()->set('system', 'relay_user_tags' , $relay_user_tags); + DI::config()->set('system', 'relay_directly' , $relay_directly, false); + DI::config()->set('system', 'relay_scope' , $relay_scope, false); + DI::config()->set('system', 'relay_server_tags', $relay_server_tags, false); + DI::config()->set('system', 'relay_deny_tags' , $relay_deny_tags, false); + DI::config()->set('system', 'relay_user_tags' , $relay_user_tags, false); + + DI::config()->save(); DI::baseUrl()->redirect('admin/site' . $active_panel); } @@ -332,8 +334,8 @@ class Site extends BaseAdmin if (DI::config()->get('system', 'directory_submit_url') && !DI::config()->get('system', 'directory')) { - DI::config()->set('system', 'directory', dirname(DI::config()->get('system', 'directory_submit_url'))); - DI::config()->delete('system', 'directory_submit_url'); + DI::config()->set('system', 'directory', dirname(DI::config()->get('system', 'directory_submit_url')), false); + DI::config()->delete('system', 'directory_submit_url', false); } /* Installed themes */ diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 48e25961c2..018bbf2c31 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1507); + define('DB_UPDATE_VERSION', 1508); } return [ @@ -553,19 +553,6 @@ return [ "k_expires" => ["k", "expires"], ] ], - "config" => [ - "comment" => "main configuration storage", - "fields" => [ - "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => ""], - "cat" => ["type" => "varbinary(50)", "not null" => "1", "default" => "", "comment" => ""], - "k" => ["type" => "varbinary(50)", "not null" => "1", "default" => "", "comment" => ""], - "v" => ["type" => "mediumtext", "comment" => ""], - ], - "indexes" => [ - "PRIMARY" => ["id"], - "cat_k" => ["UNIQUE", "cat", "k"], - ] - ], "contact-relation" => [ "comment" => "Contact relations", "fields" => [ diff --git a/static/dependencies.config.php b/static/dependencies.config.php index c59d0478a1..08867d86d9 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -76,7 +76,7 @@ return [ $_SERVER ] ], - Config\Util\ConfigFileLoader::class => [ + Config\Util\ConfigFileManager::class => [ 'instanceOf' => Config\Factory\Config::class, 'call' => [ ['createConfigFileLoader', [ diff --git a/update.php b/update.php index 356877ff9b..fda04b6ce4 100644 --- a/update.php +++ b/update.php @@ -1175,3 +1175,22 @@ function update_1505() return DBA::delete('config', $conditions) ? Update::SUCCESS : Update::FAILED; } + +function update_1508() +{ + $categories = DBA::toArray(DBA::p("SELECT DISTINCT `cat` AS 'cat' FROM `config`")); + + foreach ($categories as $category) { + DI::config()->load($category['cat']); + } + + $config = DBA::selectToArray('config'); + + foreach ($config as $entry) { + DI::config()->set($entry['cat'], $entry['k'], $entry['v'], false); + } + + DI::config()->save(); + + DBA::e("DELETE FROM `config`"); +}