From 8bbf9e93c61079bfa556d51b3b6e19f80ab8d795 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 31 Dec 2022 11:59:19 -0500 Subject: [PATCH 01/71] Remove dependency to the second parameter of XML::fromArray --- mod/oexchange.php | 8 +++----- src/Core/System.php | 2 +- src/Module/Api/ApiResponse.php | 2 +- src/Module/OpenSearch.php | 2 -- src/Module/ReallySimpleDiscovery.php | 3 +-- src/Module/WellKnown/HostMeta.php | 1 - src/Module/Xrd.php | 8 +++----- src/Protocol/Diaspora.php | 7 +++---- src/Protocol/Salmon.php | 10 +++------- src/Util/XML.php | 15 ++++++++------- 10 files changed, 23 insertions(+), 35 deletions(-) diff --git a/mod/oexchange.php b/mod/oexchange.php index c531eefdf6..4fbf7ffeb8 100644 --- a/mod/oexchange.php +++ b/mod/oexchange.php @@ -36,9 +36,7 @@ function oexchange_init(App $a) $baseURL = DI::baseUrl()->get(); - $xml = null; - - XML::fromArray([ + $xmlString = XML::fromArray([ 'XRD' => [ '@attributes' => [ 'xmlns' => 'http://docs.oasis-open.org/ns/xri/xrd-1.0', @@ -90,9 +88,9 @@ function oexchange_init(App $a) ] ], ], - ], $xml); + ]); - System::httpExit($xml->saveXML(), Response::TYPE_XML, 'application/xrd+xml'); + System::httpExit($xmlString, Response::TYPE_XML, 'application/xrd+xml'); } function oexchange_content(App $a) diff --git a/src/Core/System.php b/src/Core/System.php index 4f06bcf520..f94004a880 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -294,7 +294,7 @@ class System } DI::apiResponse()->setType(Response::TYPE_XML); - DI::apiResponse()->addContent(XML::fromArray(["result" => $result], $xml)); + DI::apiResponse()->addContent(XML::fromArray(['result' => $result])); DI::page()->exit(DI::apiResponse()->generate()); self::exit(); diff --git a/src/Module/Api/ApiResponse.php b/src/Module/Api/ApiResponse.php index a113dab84f..2d54bc1765 100644 --- a/src/Module/Api/ApiResponse.php +++ b/src/Module/Api/ApiResponse.php @@ -99,7 +99,7 @@ class ApiResponse extends Response $data3 = [$root_element => $data2]; - return XML::fromArray($data3, $xml, false, $namespaces); + return XML::fromArray($data3, $dummy, false, $namespaces); } /** diff --git a/src/Module/OpenSearch.php b/src/Module/OpenSearch.php index d5426cf384..36e8713d99 100644 --- a/src/Module/OpenSearch.php +++ b/src/Module/OpenSearch.php @@ -43,8 +43,6 @@ class OpenSearch extends BaseModule $baseUrl = DI::baseUrl()->get(); /** @var DOMDocument $xml */ - $xml = null; - XML::fromArray([ 'OpenSearchDescription' => [ '@attributes' => [ diff --git a/src/Module/ReallySimpleDiscovery.php b/src/Module/ReallySimpleDiscovery.php index 11ab3596d6..d76094003f 100644 --- a/src/Module/ReallySimpleDiscovery.php +++ b/src/Module/ReallySimpleDiscovery.php @@ -34,7 +34,6 @@ class ReallySimpleDiscovery extends BaseModule { protected function rawContent(array $request = []) { - $xml = null; $content = XML::fromArray([ 'rsd' => [ '@attributes' => [ @@ -67,7 +66,7 @@ class ReallySimpleDiscovery extends BaseModule ], ], ], - ], $xml); + ]); System::httpExit($content, Response::TYPE_XML); } } diff --git a/src/Module/WellKnown/HostMeta.php b/src/Module/WellKnown/HostMeta.php index fdebb05d71..fa6619920f 100644 --- a/src/Module/WellKnown/HostMeta.php +++ b/src/Module/WellKnown/HostMeta.php @@ -48,7 +48,6 @@ class HostMeta extends BaseModule $domain = DI::baseUrl()->get(); - $xml = null; XML::fromArray([ 'XRD' => [ '@attributes' => [ diff --git a/src/Module/Xrd.php b/src/Module/Xrd.php index 7b008549d6..ad2b259025 100644 --- a/src/Module/Xrd.php +++ b/src/Module/Xrd.php @@ -230,9 +230,7 @@ class Xrd extends BaseModule { $baseURL = $this->baseUrl->get(); - $xml = null; - - XML::fromArray([ + $xmlString = XML::fromArray([ 'XRD' => [ '@attributes' => [ 'xmlns' => 'http://docs.oasis-open.org/ns/xri/xrd-1.0', @@ -319,10 +317,10 @@ class Xrd extends BaseModule ] ], ], - ], $xml); + ]); header('Access-Control-Allow-Origin: *'); - System::httpExit($xml->saveXML(), Response::TYPE_XML, 'application/xrd+xml'); + System::httpExit($xmlString, Response::TYPE_XML, 'application/xrd+xml'); } } diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index bf6bfdd63d..c0d0bc0510 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -2855,7 +2855,7 @@ class Diaspora $namespaces = ['me' => ActivityNamespace::SALMON_ME]; - return XML::fromArray($xmldata, $xml, false, $namespaces); + return XML::fromArray($xmldata, $dummy, false, $namespaces); } /** @@ -2974,12 +2974,11 @@ class Diaspora * @param array $message The message data * * @return string The post XML + * @throws \Exception */ public static function buildPostXml(string $type, array $message): string { - $data = [$type => $message]; - - return XML::fromArray($data, $xml); + return XML::fromArray([$type => $message]); } /** diff --git a/src/Protocol/Salmon.php b/src/Protocol/Salmon.php index 7685c1dd97..50d1b931c9 100644 --- a/src/Protocol/Salmon.php +++ b/src/Protocol/Salmon.php @@ -160,7 +160,7 @@ class Salmon $namespaces = ['me' => ActivityNamespace::SALMON_ME]; - $salmon = XML::fromArray($xmldata, $xml, false, $namespaces); + $salmon = XML::fromArray($xmldata, $dummy, false, $namespaces); // slap them $postResult = DI::httpClient()->post($url, $salmon, [ @@ -187,9 +187,7 @@ class Salmon ] ]; - $namespaces = ['me' => ActivityNamespace::SALMON_ME]; - - $salmon = XML::fromArray($xmldata, $xml, false, $namespaces); + $salmon = XML::fromArray($xmldata, $dummy, false, $namespaces); // slap them $postResult = DI::httpClient()->post($url, $salmon, [ @@ -214,9 +212,7 @@ class Salmon ] ]; - $namespaces = ['me' => ActivityNamespace::SALMON_ME]; - - $salmon = XML::fromArray($xmldata, $xml, false, $namespaces); + $salmon = XML::fromArray($xmldata, $dummy, false, $namespaces); // slap them $postResult = DI::httpClient()->post($url, $salmon, [ diff --git a/src/Util/XML.php b/src/Util/XML.php index 50ecc6d2cb..0cdf1f34ed 100644 --- a/src/Util/XML.php +++ b/src/Util/XML.php @@ -37,14 +37,15 @@ class XML /** * Creates an XML structure out of a given array * - * @param array $array The array of the XML structure that will be generated - * @param object $xml The created XML will be returned by reference - * @param bool $remove_header Should the XML header be removed or not? - * @param array $namespaces List of namespaces - * @param bool $root interally used parameter. Mustn't be used from outside. + * @param array $array The array of the XML structure that will be generated + * @param object|null $xml The created XML will be returned by reference + * @param bool $remove_header Should the XML header be removed or not? + * @param array $namespaces List of namespaces + * @param bool $root interally used parameter. Mustn't be used from outside. * @return string + * @throws \Exception */ - public static function fromArray(array $array, &$xml, bool $remove_header = false, array $namespaces = [], bool $root = true): string + public static function fromArray(array $array, object &$xml = null, bool $remove_header = false, array $namespaces = [], bool $root = true): string { if ($root) { foreach ($array as $key => $value) { @@ -125,7 +126,7 @@ class XML if (!is_array($value)) { $element = $xml->addChild($key, self::escape($value ?? ''), $namespace); - } elseif (is_array($value)) { + } else { $element = $xml->addChild($key, null, $namespace); self::fromArray($value, $element, $remove_header, $namespaces, false); } From 9c256ad76f191149c0917719a59b019aa301dfa3 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 31 Dec 2022 12:55:13 -0500 Subject: [PATCH 02/71] Remove obsolete OExchange module and reference - Protocol looks unsupported for a decade now --- mod/oexchange.php | 126 ------------------------------ src/Module/WellKnown/HostMeta.php | 7 -- 2 files changed, 133 deletions(-) delete mode 100644 mod/oexchange.php diff --git a/mod/oexchange.php b/mod/oexchange.php deleted file mode 100644 index 4fbf7ffeb8..0000000000 --- a/mod/oexchange.php +++ /dev/null @@ -1,126 +0,0 @@ -. - * - */ - -use Friendica\App; -use Friendica\Content\Text\BBCode; -use Friendica\Content\Text\HTML; -use Friendica\Core\System; -use Friendica\DI; -use Friendica\Module\Response; -use Friendica\Module\Security\Login; -use Friendica\Util\XML; - -function oexchange_init(App $a) -{ - if ((DI::args()->getArgc() <= 1) || (DI::args()->getArgv()[1] != 'xrd')) { - return; - } - - $baseURL = DI::baseUrl()->get(); - - $xmlString = XML::fromArray([ - 'XRD' => [ - '@attributes' => [ - 'xmlns' => 'http://docs.oasis-open.org/ns/xri/xrd-1.0', - ], - 'Subject' => $baseURL, - '1:Property' => [ - '@attributes' => [ - 'type' => 'http://www.oexchange.org/spec/0.8/prop/vendor', - ], - 'Friendica' - ], - '2:Property' => [ - '@attributes' => [ - 'type' => 'http://www.oexchange.org/spec/0.8/prop/title', - ], - 'Friendica Social Network' - ], - '3:Property' => [ - '@attributes' => [ - 'type' => 'http://www.oexchange.org/spec/0.8/prop/name', - ], - 'Friendica' - ], - '4:Property' => [ - '@attributes' => [ - 'type' => 'http://www.oexchange.org/spec/0.8/prop/prompt', - ], - 'Send to Friendica' - ], - '1:link' => [ - '@attributes' => [ - 'rel' => 'icon', - 'type' => 'image/png', - 'href' => $baseURL . '/images/friendica-16.png' - ] - ], - '2:link' => [ - '@attributes' => [ - 'rel' => 'icon32', - 'type' => 'image/png', - 'href' => $baseURL . '/images/friendica-32.png' - ] - ], - '3:link' => [ - '@attributes' => [ - 'rel' => 'http://www.oexchange.org/spec/0.8/rel/offer', - 'type' => 'text/html', - 'href' => $baseURL . '/oexchange' - ] - ], - ], - ]); - - System::httpExit($xmlString, Response::TYPE_XML, 'application/xrd+xml'); -} - -function oexchange_content(App $a) -{ - if (!DI::userSession()->getLocalUserId()) { - $o = Login::form(); - return $o; - } - - if ((DI::args()->getArgc() > 1) && DI::args()->getArgv()[1] === 'done') { - return; - } - - $url = !empty($_REQUEST['url']) ? trim($_REQUEST['url']) : ''; - $title = !empty($_REQUEST['title']) ? trim($_REQUEST['title']) : ''; - $description = !empty($_REQUEST['description']) ? trim($_REQUEST['description']) : ''; - $tags = !empty($_REQUEST['tags']) ? trim($_REQUEST['tags']) : ''; - - $s = BBCode::embedURL($url, true, $title, $description, $tags); - - if (!strlen($s)) { - return; - } - - $post = []; - - $post['return'] = '/oexchange/done'; - $post['body'] = HTML::toBBCode($s); - - $_REQUEST = $post; - require_once 'mod/item.php'; - item_post($a); -} diff --git a/src/Module/WellKnown/HostMeta.php b/src/Module/WellKnown/HostMeta.php index fa6619920f..512ff8dd0e 100644 --- a/src/Module/WellKnown/HostMeta.php +++ b/src/Module/WellKnown/HostMeta.php @@ -80,13 +80,6 @@ class HostMeta extends BaseModule 'href' => $domain . '/amcd' ] ], - '5:link' => [ - '@attributes' => [ - 'rel' => 'http://oexchange.org/spec/0.8/rel/resident-target', - 'type' => 'application/xrd+xml', - 'href' => $domain . '/oexchange/xrd' - ] - ], 'Property' => [ '@attributes' => [ 'type' => 'http://salmon-protocol.org/ns/magic-key', From fea4b202c15b8890da671c9983a07b344c884ca2 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 1 Jan 2023 18:50:02 +0100 Subject: [PATCH 03/71] Introduce ConfigFileTransformer for Config files --- .../Config/Util/ConfigFileTransformer.php | 85 +++++++++++++++++++ tests/datasets/config/A.node.config.php | 22 +++++ tests/datasets/config/B.node.config.php | 38 +++++++++ .../Config/Util/ConfigFileTransformerTest.php | 54 ++++++++++++ 4 files changed, 199 insertions(+) create mode 100644 src/Core/Config/Util/ConfigFileTransformer.php create mode 100644 tests/datasets/config/A.node.config.php create mode 100644 tests/datasets/config/B.node.config.php create mode 100644 tests/src/Core/Config/Util/ConfigFileTransformerTest.php diff --git a/src/Core/Config/Util/ConfigFileTransformer.php b/src/Core/Config/Util/ConfigFileTransformer.php new file mode 100644 index 0000000000..9b80991af6 --- /dev/null +++ b/src/Core/Config/Util/ConfigFileTransformer.php @@ -0,0 +1,85 @@ +. + * + */ + +namespace Friendica\Core\Config\Util; + +/** + * Util to transform back the config array into a string + */ +class ConfigFileTransformer +{ + public static function encode(array $data): string + { + $dataString = ' [" . PHP_EOL; + + if (is_array($data[$category])) { + $keys = array_keys($data[$category]); + + foreach ($keys as $key) { + $dataString .= static::mapConfigValue($key, $data[$category][$key]); + } + } + $dataString .= "\t]," . PHP_EOL; + } + + $dataString .= "];" . PHP_EOL; + + return $dataString; + } + + protected static function extractArray(array $config, int $level = 0): string + { + $string = ''; + + foreach ($config as $configKey => $configValue) { + $string .= static::mapConfigValue($configKey, $configValue, $level); + } + + return $string; + } + + protected static function mapConfigValue(string $key, $value, $level = 0): string + { + $string = str_repeat("\t", $level + 2) . "'$key' => "; + + if (is_array($value)) { + $string .= "[" . PHP_EOL; + $string .= static::extractArray($value, ++$level); + $string .= str_repeat("\t", $level + 1) . '],'; + } elseif (is_bool($value)) { + $string .= ($value ? 'true' : 'false') . ","; + } elseif (is_numeric($value)) { + $string .= $value . ","; + } else { + $string .= sprintf('\'%s\',', $value); + } + + $string .= PHP_EOL; + + return $string; + } +} diff --git a/tests/datasets/config/A.node.config.php b/tests/datasets/config/A.node.config.php new file mode 100644 index 0000000000..b81e01737e --- /dev/null +++ b/tests/datasets/config/A.node.config.php @@ -0,0 +1,22 @@ + [ + 'hostname' => 'testhost', + 'username' => 'testuser', + 'password' => 'testpw', + 'database' => 'testdb', + 'charset' => 'utf8mb4', + ], + 'config' => [ + 'admin_email' => 'admin@test.it', + 'sitename' => 'Friendica Social Network', + 'register_policy' => 2, + 'register_text' => '', + ], + 'system' => [ + 'default_timezone' => 'UTC', + 'language' => 'en', + 'theme' => 'frio', + ], +]; diff --git a/tests/datasets/config/B.node.config.php b/tests/datasets/config/B.node.config.php new file mode 100644 index 0000000000..94b2e3f12e --- /dev/null +++ b/tests/datasets/config/B.node.config.php @@ -0,0 +1,38 @@ + [ + 'hostname' => 'testhost', + 'username' => 'testuser', + 'password' => 'testpw', + 'database' => 'testdb', + 'charset' => 'utf8mb4', + ], + 'config' => [ + 'admin_email' => 'admin@test.it', + 'sitename' => 'Friendica Social Network', + 'register_policy' => 2, + 'register_text' => '', + 'test' => [ + 'a' => [ + 'next' => 'value', + 'bool' => false, + 'innerArray' => [ + 'a' => 4.55, + 'b' => false, + 'string2' => 'false', + ], + ], + 'v' => true, + 'v3' => 1, + 'v4' => 5.6443, + ], + ], + 'system' => [ + 'default_timezone' => 'UTC', + 'language' => 'en', + 'theme' => 'frio', + 'int' => 23, + 'float' => 2.5, + ], +]; diff --git a/tests/src/Core/Config/Util/ConfigFileTransformerTest.php b/tests/src/Core/Config/Util/ConfigFileTransformerTest.php new file mode 100644 index 0000000000..6cd5bc7064 --- /dev/null +++ b/tests/src/Core/Config/Util/ConfigFileTransformerTest.php @@ -0,0 +1,54 @@ +. + * + */ + +namespace Friendica\Test\src\Core\Config\Util; + +use Friendica\Core\Config\Util\ConfigFileTransformer; +use Friendica\Test\MockedTest; + +class ConfigFileTransformerTest extends MockedTest +{ + public function dataTests() + { + return [ + 'default' => [ + 'configFile' => (dirname(__DIR__, 4) . '/datasets/config/A.node.config.php'), + ], + 'extended' => [ + 'configFile' => (dirname(__DIR__, 4) . '/datasets/config/B.node.config.php'), + ], + ]; + } + + /** + * Tests if the given config will be decoded into an array and encoded into the same string again + * + * @dataProvider dataTests + */ + public function testConfigFile(string $configFile) + { + $dataArray = include $configFile; + + $newConfig = ConfigFileTransformer::encode($dataArray); + + self::assertEquals(file_get_contents($configFile), $newConfig); + } +} From 0f91d1cbde9e36b10d66fe756229ec8a9635f7eb Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 1 Jan 2023 21:10:37 +0100 Subject: [PATCH 04/71] Introduce ConfigFileManager for config files --- database.sql | 14 +- doc/database.md | 1 - doc/database/db_config.md | 25 --- src/App/BaseURL.php | 43 +++-- src/Console/Maintenance.php | 8 +- src/Console/Relocate.php | 9 +- .../Config/Capability/IManageConfigValues.php | 11 +- src/Core/Config/Factory/Config.php | 26 +-- src/Core/Config/Repository/Config.php | 78 +------- src/Core/Config/Type/AbstractConfig.php | 18 +- src/Core/Config/Type/JitConfig.php | 28 ++- src/Core/Config/Type/PreloadConfig.php | 28 ++- ...igFileLoader.php => ConfigFileManager.php} | 81 +++++++-- src/Core/Config/ValueObject/Cache.php | 36 +++- .../Type/DBKeyValueStorage.php | 2 +- src/Core/Update.php | 20 +- src/Database/DBStructure.php | 9 +- src/Module/Admin/Site.php | 172 +++++++++--------- static/dbstructure.config.php | 15 +- static/dependencies.config.php | 2 +- update.php | 19 ++ 21 files changed, 343 insertions(+), 302 deletions(-) delete mode 100644 doc/database/db_config.md rename src/Core/Config/Util/{ConfigFileLoader.php => ConfigFileManager.php} (83%) 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`"); +} From b871e1d264aced03c2c986fe93bff33f3575ee7f Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 28 Dec 2022 00:18:29 +0100 Subject: [PATCH 05/71] Introduce lightweight Config model --- src/Core/Config/Model/Config.php | 98 ++++++++++++++++++++++++++++++++ static/dependencies.config.php | 5 +- 2 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 src/Core/Config/Model/Config.php diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php new file mode 100644 index 0000000000..3af2f7e4b1 --- /dev/null +++ b/src/Core/Config/Model/Config.php @@ -0,0 +1,98 @@ +. + * + */ + +namespace Friendica\Core\Config\Model; + +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Config\Util\ConfigFileManager; +use Friendica\Core\Config\ValueObject\Cache; + +/** + * Configuration model, which manages the whole system configuration + */ +class Config implements IManageConfigValues +{ + /** + * @var Cache + */ + protected $configCache; + + /** @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) + */ + public function __construct(ConfigFileManager $configFileManager, Cache $configCache) + { + $this->configFileManager = $configFileManager; + $this->configCache = $configCache; + } + + /** + * {@inheritDoc} + */ + public function getCache(): Cache + { + return $this->configCache; + } + + public function save() + { + $this->configFileManager->saveData($this->configCache); + } + + public function load(string $cat = 'config') + { + $configCache = new Cache(); + + $this->configFileManager->setupCache($configCache, $_SERVER); + $this->configCache = $configCache; + } + + public function get(string $cat, string $key, $default_value = null, bool $refresh = false) + { + return $this->configCache->get($cat, $key) ?? $default_value; + } + + public function set(string $cat, string $key, $value, bool $autosave = true): bool + { + $stored = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); + + if ($stored && $autosave) { + $this->save(); + } + + return $stored; + } + + public function delete(string $cat, string $key, bool $autosave = true): bool + { + $removed = $this->configCache->delete($cat, $key); + + if ($removed && $autosave) { + $this->save(); + } + + return $removed; + } +} diff --git a/static/dependencies.config.php b/static/dependencies.config.php index 08867d86d9..90f01feede 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -98,10 +98,7 @@ return [ ], ], Config\Capability\IManageConfigValues::class => [ - 'instanceOf' => Config\Factory\Config::class, - 'call' => [ - ['create', [], Dice::CHAIN_CALL], - ], + 'instanceOf' => Config\Model\Config::class, ], PConfig\Capability\IManagePersonalConfigValues::class => [ 'instanceOf' => PConfig\Factory\PConfig::class, From d272e8c3c78b88c45a0eb860f8491ccd6f3d7a62 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 28 Dec 2022 00:20:20 +0100 Subject: [PATCH 06/71] Remove unnecessary classes --- src/Core/Config/Repository/Config.php | 121 -------------- src/Core/Config/Type/AbstractConfig.php | 75 --------- src/Core/Config/Type/JitConfig.php | 149 ------------------ src/Core/Config/Type/PreloadConfig.php | 145 ----------------- src/Core/PConfig/Repository/PConfig.php | 2 +- .../Util/ValueConversion.php | 2 +- 6 files changed, 2 insertions(+), 492 deletions(-) delete mode 100644 src/Core/Config/Repository/Config.php delete mode 100644 src/Core/Config/Type/AbstractConfig.php delete mode 100644 src/Core/Config/Type/JitConfig.php delete mode 100644 src/Core/Config/Type/PreloadConfig.php rename src/Core/{Config => PConfig}/Util/ValueConversion.php (98%) diff --git a/src/Core/Config/Repository/Config.php b/src/Core/Config/Repository/Config.php deleted file mode 100644 index eabff9e68f..0000000000 --- a/src/Core/Config/Repository/Config.php +++ /dev/null @@ -1,121 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Config\Repository; - -use Friendica\App\Mode; -use Friendica\Core\Config\Exception\ConfigPersistenceException; -use Friendica\Core\Config\Util\ValueConversion; -use Friendica\Database\Database; - -/** - * The Config Repository, which is using the general DB-model backend for configs - */ -class Config -{ - /** @var Database */ - protected $db; - /** @var Mode */ - protected $mode; - - public function __construct(Database $db, Mode $mode) - { - $this->db = $db; - $this->mode = $mode; - } - - protected static $table_name = 'config'; - - /** - * Checks if the model is currently connected - * - * @return bool - */ - public function isConnected(): bool - { - return true; - } - - /** - * Loads all configuration values and returns the loaded category as an array. - * - * @param string|null $cat The category of the configuration values to load - * - * @return array The config array - * - * @throws ConfigPersistenceException In case the persistence layer throws errors - */ - public function load(?string $cat = null): array - { - return []; - } - - /** - * Get a particular, system-wide config variable out of the DB with the - * given category name ($cat) and a key ($key). - * - * Note: Boolean variables are defined as 0/1 in the database - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to query - * - * @return array|string|null Stored value or null if it does not exist - * - * @throws ConfigPersistenceException In case the persistence layer throws errors - */ - public function get(string $cat, string $key) - { - return null; - } - - /** - * Stores a config value ($value) in the category ($cat) under the key ($key). - * - * Note: Please do not store booleans - convert to 0/1 integer values! - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to set - * @param mixed $value The value to store - * - * @return bool Operation success - * - * @throws ConfigPersistenceException In case the persistence layer throws errors - */ - public function set(string $cat, string $key, $value): bool - { - return true; - } - - /** - * Removes the configured value from the database. - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to delete - * - * @return bool Operation success - * - * @throws ConfigPersistenceException In case the persistence layer throws errors - */ - public function delete(string $cat, string $key): bool - { - return true; - } -} diff --git a/src/Core/Config/Type/AbstractConfig.php b/src/Core/Config/Type/AbstractConfig.php deleted file mode 100644 index fa98dd7097..0000000000 --- a/src/Core/Config/Type/AbstractConfig.php +++ /dev/null @@ -1,75 +0,0 @@ -. - * - */ - -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 - * There are two types of storage - * - The Config-Files (loaded into the FileCache @see Cache) - * - The Config-Repository (per Config-Repository @see Config ) - */ -abstract class AbstractConfig implements IManageConfigValues -{ - /** - * @var Cache - */ - protected $configCache; - - /** - * @var Config - */ - 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(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) - { - $this->configFileManager = $configFileManager; - $this->configCache = $configCache; - $this->configRepo = $configRepo; - } - - /** - * {@inheritDoc} - */ - public function getCache(): Cache - { - 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 deleted file mode 100644 index 1ae9abd2ee..0000000000 --- a/src/Core/Config/Type/JitConfig.php +++ /dev/null @@ -1,149 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Config\Type; - -use Friendica\Core\Config\Util\ConfigFileManager; -use Friendica\Core\Config\ValueObject\Cache; -use Friendica\Core\Config\Repository\Config; - -/** - * This class implements the Just-In-Time configuration, which will cache - * config values in a cache, once they are retrieved. - * - * Default Configuration type. - * Provides the best performance for pages loading few configuration variables. - */ -class JitConfig extends AbstractConfig -{ - /** - * @var array Array of already loaded db values (even if there was no value) - */ - private $db_loaded; - - /** - * @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(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) - { - parent::__construct($configFileManager, $configCache, $configRepo); - $this->db_loaded = []; - - $this->load(); - } - - /** - * {@inheritDoc} - */ - public function load(string $cat = 'config') - { - // If not connected, do nothing - if (!$this->configRepo->isConnected()) { - return; - } - - $config = $this->configRepo->load($cat); - - if (!empty($config[$cat])) { - foreach ($config[$cat] as $key => $value) { - $this->db_loaded[$cat][$key] = true; - } - } - - // load the whole category out of the DB into the cache - $this->configCache->load($config, Cache::SOURCE_DATA); - } - - /** - * {@inheritDoc} - */ - public function get(string $cat, string $key, $default_value = null, bool $refresh = false) - { - // if the value isn't loaded or refresh is needed, load it to the cache - if ($this->configRepo->isConnected() && - (empty($this->db_loaded[$cat][$key]) || - $refresh)) { - $dbValue = $this->configRepo->get($cat, $key); - - if (isset($dbValue)) { - $this->configCache->set($cat, $key, $dbValue, Cache::SOURCE_DATA); - unset($dbValue); - } - - $this->db_loaded[$cat][$key] = true; - } - - // use the config cache for return - $result = $this->configCache->get($cat, $key); - - return (isset($result)) ? $result : $default_value; - } - - /** - * {@inheritDoc} - */ - 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_DATA); - - // If there is no connected adapter, we're finished - if (!$this->configRepo->isConnected()) { - return $cached; - } - - $stored = $this->configRepo->set($cat, $key, $value); - - $this->db_loaded[$cat][$key] = $stored; - - if ($autosave) { - $this->save(); - } - - return $cached && $stored; - } - - /** - * {@inheritDoc} - */ - public function delete(string $cat, string $key, bool $autosave = true): bool - { - $cacheRemoved = $this->configCache->delete($cat, $key); - - if (isset($this->db_loaded[$cat][$key])) { - unset($this->db_loaded[$cat][$key]); - } - - if (!$this->configRepo->isConnected()) { - return $cacheRemoved; - } - - $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 deleted file mode 100644 index 6eed20af89..0000000000 --- a/src/Core/Config/Type/PreloadConfig.php +++ /dev/null @@ -1,145 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Config\Type; - -use Friendica\Core\Config\Util\ConfigFileManager; -use Friendica\Core\Config\ValueObject\Cache; -use Friendica\Core\Config\Repository\Config; - -/** - * This class implements the preload configuration, which will cache - * all config values per call in a cache. - * - * Minimizes the number of database queries to retrieve configuration values at the cost of memory. - */ -class PreloadConfig extends AbstractConfig -{ - /** @var bool */ - private $config_loaded; - - /** - * @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(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) - { - parent::__construct($configFileManager, $configCache, $configRepo); - $this->config_loaded = false; - - $this->load(); - } - - /** - * {@inheritDoc} - * - * This loads all config values everytime load is called - */ - public function load(string $cat = 'config') - { - // Don't load the whole configuration twice - if ($this->config_loaded) { - return; - } - - // If not connected, do nothing - if (!$this->configRepo->isConnected()) { - return; - } - - $config = $this->configRepo->load(); - $this->config_loaded = true; - - // load the whole category out of the DB into the cache - $this->configCache->load($config, Cache::SOURCE_DATA); - } - - /** - * {@inheritDoc} - */ - public function get(string $cat, string $key, $default_value = null, bool $refresh = false) - { - if ($refresh) { - if ($this->configRepo->isConnected()) { - $config = $this->configRepo->get($cat, $key); - if (isset($config)) { - $this->configCache->set($cat, $key, $config, Cache::SOURCE_DATA); - } - } - } - - // use the config cache for return - $result = $this->configCache->get($cat, $key); - - return (isset($result)) ? $result : $default_value; - } - - /** - * {@inheritDoc} - */ - 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_DATA); - - // If there is no connected adapter, we're finished - if (!$this->configRepo->isConnected()) { - return $cached; - } - - $stored = $this->configRepo->set($cat, $key, $value); - - if ($autosave) { - $this->save(); - } - - return $cached && $stored; - } - - /** - * {@inheritDoc} - */ - public function delete(string $cat, string $key, bool $autosave = true): bool - { - if ($this->config_loaded) { - $this->load(); - } - - $cacheRemoved = $this->configCache->delete($cat, $key); - - if (!$this->configRepo->isConnected()) { - return $cacheRemoved; - } - - $storeRemoved = $this->configRepo->delete($cat, $key); - - if ($autosave) { - $this->save(); - } - - return $cacheRemoved || $storeRemoved; - } -} diff --git a/src/Core/PConfig/Repository/PConfig.php b/src/Core/PConfig/Repository/PConfig.php index 5c9d2d51d1..506aaeb5a7 100644 --- a/src/Core/PConfig/Repository/PConfig.php +++ b/src/Core/PConfig/Repository/PConfig.php @@ -22,7 +22,7 @@ namespace Friendica\Core\PConfig\Repository; use Friendica\App\Mode; -use Friendica\Core\Config\Util\ValueConversion; +use Friendica\Core\PConfig\Util\ValueConversion; use Friendica\Core\PConfig\Exception\PConfigPersistenceException; use Friendica\Database\Database; diff --git a/src/Core/Config/Util/ValueConversion.php b/src/Core/PConfig/Util/ValueConversion.php similarity index 98% rename from src/Core/Config/Util/ValueConversion.php rename to src/Core/PConfig/Util/ValueConversion.php index a3d9d0146a..64a4bfc51f 100644 --- a/src/Core/Config/Util/ValueConversion.php +++ b/src/Core/PConfig/Util/ValueConversion.php @@ -19,7 +19,7 @@ * */ -namespace Friendica\Core\Config\Util; +namespace Friendica\Core\PConfig\Util; /** * Util class to help to convert from/to (p)config values From 326566638fcc90ef20a87047ea55fb846c7d46e3 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 1 Jan 2023 21:10:41 +0100 Subject: [PATCH 07/71] adapt tests --- tests/FixtureTest.php | 13 +- tests/Util/VFSTrait.php | 15 ++ tests/datasets/api.fixture.php | 27 -- tests/datasets/config/node.config.php | 13 + tests/functional/DependencyCheckTest.php | 10 +- tests/src/Core/Config/Cache/CacheTest.php | 70 +++-- .../Config/Cache/ConfigFileLoaderTest.php | 61 ++++- tests/src/Core/Config/ConfigTest.php | 178 ++++-------- tests/src/Core/Config/JitConfigTest.php | 255 ------------------ tests/src/Core/Config/PreloadConfigTest.php | 213 --------------- tests/src/Core/Lock/SemaphoreLockTest.php | 10 +- .../Storage/Repository/StorageManagerTest.php | 6 +- tests/src/Database/DBATest.php | 5 +- tests/src/Database/DBStructureTest.php | 28 +- .../Api/GnuSocial/GnuSocial/ConfigTest.php | 1 + tests/src/Util/BaseURLTest.php | 61 +++-- 16 files changed, 254 insertions(+), 712 deletions(-) create mode 100644 tests/datasets/config/node.config.php delete mode 100644 tests/src/Core/Config/JitConfigTest.php delete mode 100644 tests/src/Core/Config/PreloadConfigTest.php diff --git a/tests/FixtureTest.php b/tests/FixtureTest.php index a57d3aa766..efb9280310 100644 --- a/tests/FixtureTest.php +++ b/tests/FixtureTest.php @@ -25,20 +25,24 @@ namespace Friendica\Test; use Dice\Dice; use Friendica\App\Arguments; use Friendica\App\Router; +use Friendica\Core\Config\Factory\Config; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; -use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Session\Capability\IHandleSessions; use Friendica\Core\Session\Type\Memory; use Friendica\Database\Database; use Friendica\Database\DBStructure; use Friendica\DI; use Friendica\Test\Util\Database\StaticDatabase; +use Friendica\Test\Util\VFSTrait; /** * Parent class for test cases requiring fixtures */ abstract class FixtureTest extends DatabaseTest { + use VFSTrait; + /** @var Dice */ protected $dice; @@ -47,6 +51,8 @@ abstract class FixtureTest extends DatabaseTest */ protected function setUp(): void { + $this->setUpVfsDir(); + parent::setUp(); $server = $_SERVER; @@ -54,6 +60,10 @@ abstract class FixtureTest extends DatabaseTest $this->dice = (new Dice()) ->addRules(include __DIR__ . '/../static/dependencies.config.php') + ->addRule(ConfigFileManager::class, [ + 'instanceOf' => Config::class, + 'call' => [['createConfigFileLoader', [$this->root->url(), $server,], + Dice::CHAIN_CALL]]]) ->addRule(Database::class, ['instanceOf' => StaticDatabase::class, 'shared' => true]) ->addRule(IHandleSessions::class, ['instanceOf' => Memory::class, 'shared' => true, 'call' => null]) ->addRule(Arguments::class, [ @@ -64,7 +74,6 @@ abstract class FixtureTest extends DatabaseTest ]); DI::init($this->dice); - /** @var IManageConfigValues $config */ $configCache = $this->dice->create(Cache::class); $configCache->set('database', 'disable_pdo', true); diff --git a/tests/Util/VFSTrait.php b/tests/Util/VFSTrait.php index 30c27451eb..e36cc1234c 100644 --- a/tests/Util/VFSTrait.php +++ b/tests/Util/VFSTrait.php @@ -54,6 +54,21 @@ trait VFSTrait $this->setConfigFile('defaults.config.php', true); $this->setConfigFile('settings.config.php', true); $this->setConfigFile('local.config.php'); + $this->setDataFile('node.config.php'); + } + + protected function setDataFile(string $filename) + { + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + $filename; + + if (file_exists($file)) { + vfsStream::newFile($filename) + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($file)); + } } /** diff --git a/tests/datasets/api.fixture.php b/tests/datasets/api.fixture.php index a8119106ee..b50f706251 100644 --- a/tests/datasets/api.fixture.php +++ b/tests/datasets/api.fixture.php @@ -36,33 +36,6 @@ return [ 'mail', 'post-delivery-data', // Base test config to avoid notice messages - 'config' => [ - [ - 'cat' => 'system', - 'k' => 'url', - 'v' => 'http://localhost', - ], - [ - 'cat' => 'config', - 'k' => 'hostname', - 'v' => 'localhost', - ], - [ - 'cat' => 'system', - 'k' => 'worker_dont_fork', - 'v' => '1', - ], - [ - 'cat' => 'system', - 'k' => 'curl_timeout', - 'v' => '1', - ], - [ - 'cat' => 'system', - 'k' => 'xrd_timeout', - 'v' => '1', - ], - ], 'user' => [ [ 'uid' => 42, diff --git a/tests/datasets/config/node.config.php b/tests/datasets/config/node.config.php new file mode 100644 index 0000000000..335ef19c9d --- /dev/null +++ b/tests/datasets/config/node.config.php @@ -0,0 +1,13 @@ + [ + 'hostname' => 'localhost', + ], + 'system' => [ + 'url' => 'http://localhost', + "worker_dont_fork" => 1, + "curl_timeout"=> 1, + "xrd_timeout"=> 1, + ], +]; diff --git a/tests/functional/DependencyCheckTest.php b/tests/functional/DependencyCheckTest.php index 3b21f75845..9bc7624d6a 100644 --- a/tests/functional/DependencyCheckTest.php +++ b/tests/functional/DependencyCheckTest.php @@ -31,7 +31,7 @@ use Friendica\Core\Lock\Capability\ICanLock; use Friendica\Database\Database; use Friendica\Test\Util\VFSTrait; use Friendica\Util\BasePath; -use Friendica\Core\Config\Util\ConfigFileLoader; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Util\Profiler; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -73,13 +73,13 @@ class DependencyCheckTest extends TestCase */ public function testConfigFileLoader() { - /** @var ConfigFileLoader $configFileLoader */ - $configFileLoader = $this->dice->create(ConfigFileLoader::class); + /** @var ConfigFileManager $configFileManager */ + $configFileManager = $this->dice->create(ConfigFileManager::class); - self::assertInstanceOf(ConfigFileLoader::class, $configFileLoader); + self::assertInstanceOf(ConfigFileManager::class, $configFileManager); $configCache = new Cache(); - $configFileLoader->setupCache($configCache); + $configFileManager->setupCache($configCache); self::assertNotEmpty($configCache->getAll()); self::assertArrayHasKey('database', $configCache->getAll()); diff --git a/tests/src/Core/Config/Cache/CacheTest.php b/tests/src/Core/Config/Cache/CacheTest.php index 6de87be5d4..8b2c24da3e 100644 --- a/tests/src/Core/Config/Cache/CacheTest.php +++ b/tests/src/Core/Config/Cache/CacheTest.php @@ -21,7 +21,7 @@ namespace Friendica\Test\src\Core\Config\Cache; -use Friendica\Core\Config\Cache; +use Friendica\Core\Config\ValueObject\Cache; use Friendica\Test\MockedTest; use ParagonIE\HiddenString\HiddenString; use stdClass; @@ -49,7 +49,7 @@ class CacheTest extends MockedTest ]; } - private function assertConfigValues($data, \Friendica\Core\Config\ValueObject\Cache $configCache) + private function assertConfigValues($data, Cache $configCache) { foreach ($data as $cat => $values) { foreach ($values as $key => $value) { @@ -64,7 +64,7 @@ class CacheTest extends MockedTest */ public function testLoadConfigArray($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configCache->load($data); self::assertConfigValues($data, $configCache); @@ -83,27 +83,27 @@ class CacheTest extends MockedTest ] ]; - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); - $configCache->load($data, \Friendica\Core\Config\ValueObject\Cache::SOURCE_DB); + $configCache = new Cache(); + $configCache->load($data, Cache::SOURCE_DATA); // doesn't override - Low Priority due Config file - $configCache->load($override, \Friendica\Core\Config\ValueObject\Cache::SOURCE_FILE); + $configCache->load($override, Cache::SOURCE_FILE); self::assertConfigValues($data, $configCache); // override the value - High Prio due Server Env - $configCache->load($override, \Friendica\Core\Config\ValueObject\Cache::SOURCE_ENV); + $configCache->load($override, Cache::SOURCE_ENV); self::assertEquals($override['system']['test'], $configCache->get('system', 'test')); self::assertEquals($override['system']['boolTrue'], $configCache->get('system', 'boolTrue')); // Don't overwrite server ENV variables - even in load mode - $configCache->load($data, \Friendica\Core\Config\ValueObject\Cache::SOURCE_DB); + $configCache->load($data, Cache::SOURCE_DATA); self::assertEquals($override['system']['test'], $configCache->get('system', 'test')); self::assertEquals($override['system']['boolTrue'], $configCache->get('system', 'boolTrue')); // Overwrite ENV variables with ENV variables - $configCache->load($data, \Friendica\Core\Config\ValueObject\Cache::SOURCE_ENV); + $configCache->load($data, Cache::SOURCE_ENV); self::assertConfigValues($data, $configCache); self::assertNotEquals($override['system']['test'], $configCache->get('system', 'test')); @@ -115,7 +115,7 @@ class CacheTest extends MockedTest */ public function testLoadConfigArrayWrong() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); // empty dataset $configCache->load([]); @@ -136,7 +136,7 @@ class CacheTest extends MockedTest */ public function testGetAll($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configCache->load($data); $all = $configCache->getAll(); @@ -151,7 +151,7 @@ class CacheTest extends MockedTest */ public function testSetGet($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); foreach ($data as $cat => $values) { foreach ($values as $key => $value) { @@ -167,7 +167,7 @@ class CacheTest extends MockedTest */ public function testGetEmpty() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); self::assertNull($configCache->get('something', 'value')); } @@ -177,7 +177,7 @@ class CacheTest extends MockedTest */ public function testGetCat() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'system' => [ 'key1' => 'value1', 'key2' => 'value2', @@ -205,7 +205,7 @@ class CacheTest extends MockedTest */ public function testDelete($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache($data); + $configCache = new Cache($data); foreach ($data as $cat => $values) { foreach ($values as $key => $value) { @@ -222,7 +222,7 @@ class CacheTest extends MockedTest */ public function testKeyDiffWithResult($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache($data); + $configCache = new Cache($data); $diffConfig = [ 'fakeCat' => [ @@ -239,7 +239,7 @@ class CacheTest extends MockedTest */ public function testKeyDiffWithoutResult($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache($data); + $configCache = new Cache($data); $diffConfig = $configCache->getAll(); @@ -251,7 +251,7 @@ class CacheTest extends MockedTest */ public function testPasswordHide() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'database' => [ 'password' => 'supersecure', 'username' => 'notsecured', @@ -268,7 +268,7 @@ class CacheTest extends MockedTest */ public function testPasswordShow() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'database' => [ 'password' => 'supersecure', 'username' => 'notsecured', @@ -285,7 +285,7 @@ class CacheTest extends MockedTest */ public function testEmptyPassword() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'database' => [ 'password' => '', 'username' => '', @@ -299,7 +299,7 @@ class CacheTest extends MockedTest public function testWrongTypePassword() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'database' => [ 'password' => new stdClass(), 'username' => '', @@ -309,7 +309,7 @@ class CacheTest extends MockedTest self::assertNotEmpty($configCache->get('database', 'password')); self::assertEmpty($configCache->get('database', 'username')); - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'database' => [ 'password' => 23, 'username' => '', @@ -327,19 +327,35 @@ class CacheTest extends MockedTest public function testSetOverrides($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); - $configCache->load($data, \Friendica\Core\Config\ValueObject\Cache::SOURCE_DB); + $configCache = new Cache(); + $configCache->load($data, Cache::SOURCE_DATA); // test with wrong override - self::assertFalse($configCache->set('system', 'test', '1234567', \Friendica\Core\Config\ValueObject\Cache::SOURCE_FILE)); + self::assertFalse($configCache->set('system', 'test', '1234567', Cache::SOURCE_FILE)); self::assertEquals($data['system']['test'], $configCache->get('system', 'test')); // test with override (equal) - self::assertTrue($configCache->set('system', 'test', '8910', \Friendica\Core\Config\ValueObject\Cache::SOURCE_DB)); + self::assertTrue($configCache->set('system', 'test', '8910', Cache::SOURCE_DATA)); self::assertEquals('8910', $configCache->get('system', 'test')); // test with override (over) - self::assertTrue($configCache->set('system', 'test', '111213', \Friendica\Core\Config\ValueObject\Cache::SOURCE_ENV)); + self::assertTrue($configCache->set('system', 'test', '111213', Cache::SOURCE_ENV)); self::assertEquals('111213', $configCache->get('system', 'test')); } + + /** + * @dataProvider dataTests + * + * @return void + */ + public function testSetData($data) + { + $configCache = new Cache(); + $configCache->load($data, Cache::SOURCE_FILE); + + $configCache->set('system', 'test_2','with_data', Cache::SOURCE_DATA); + + $this->assertEquals(['system' => ['test_2' => 'with_data']], $configCache->getDataBySource(Cache::SOURCE_DATA)); + $this->assertEquals($data, $configCache->getDataBySource(Cache::SOURCE_FILE)); + } } diff --git a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php b/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php index be1c7def68..e8443611f2 100644 --- a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php +++ b/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php @@ -25,7 +25,7 @@ use Friendica\Core\Config\Cache; use Friendica\Core\Config\Factory\Config; use Friendica\Test\MockedTest; use Friendica\Test\Util\VFSTrait; -use Friendica\Core\Config\Util\ConfigFileLoader; +use Friendica\Core\Config\Util\ConfigFileManager; use org\bovigo\vfs\vfsStream; class ConfigFileLoaderTest extends MockedTest @@ -46,7 +46,7 @@ class ConfigFileLoaderTest extends MockedTest { $this->delConfigFile('local.config.php'); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -72,7 +72,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent('root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -101,7 +101,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($file)); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -138,7 +138,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($file)); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -174,7 +174,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root) ->setContent(file_get_contents($file)); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -228,7 +228,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('addon')->getChild('test')->getChild('config')) ->setContent(file_get_contents($file)); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -265,7 +265,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($fileDir . 'B.config.php')); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -299,7 +299,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($fileDir . 'B.ini.php')); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -333,7 +333,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($fileDir . 'B.ini.php')); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -386,4 +386,45 @@ class ConfigFileLoaderTest extends MockedTest self::assertEquals('newValue', $configCache->get('system', 'newKey')); } + + public function testSaveData() + { + $this->delConfigFile('local.config.php'); + + $fileDir = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR; + + vfsStream::newFile('B.config.php') + ->at($this->root->getChild('config2')) + ->setContent(file_get_contents($fileDir . 'B.config.php')); + + $configFileLoader = (new Config())->createConfigFileLoader($this->root->url(), ['FRIENDICA_CONFIG_DIR' => $this->root->getChild('config2')->url()]); + $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + + $configFileLoader->setupCache($configCache); + + // overwrite some data and save it back to the config file + $configCache->set('system', 'test', 'it', \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); + $configCache->set('config', 'test', 'it', \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); + $configCache->set('system', 'test_2', 2, \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); + $configFileLoader->saveData($configCache); + + // Reload the configCache with the new values + $configCache2 = new \Friendica\Core\Config\ValueObject\Cache(); + $configFileLoader->setupCache($configCache2); + + self::assertEquals($configCache, $configCache2); + self::assertEquals([ + 'system' => [ + 'test' => 'it', + 'test_2' => 2 + ], + 'config' => [ + 'test' => 'it' + ]], $configCache2->getDataBySource(\Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA)); + } } diff --git a/tests/src/Core/Config/ConfigTest.php b/tests/src/Core/Config/ConfigTest.php index bdcba724e6..c85cf3f708 100644 --- a/tests/src/Core/Config/ConfigTest.php +++ b/tests/src/Core/Config/ConfigTest.php @@ -23,22 +23,25 @@ namespace Friendica\Test\src\Core\Config; use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts; use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Config\Repository\Config as ConfigModel; +use Friendica\Core\Config\Model\Config; +use Friendica\Core\Config\Util\ConfigFileManager; +use Friendica\Core\Config\Util\ConfigFileTransformer; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Test\MockedTest; -use Mockery\MockInterface; -use Mockery; +use Friendica\Test\Util\VFSTrait; +use org\bovigo\vfs\vfsStream; -abstract class ConfigTest extends MockedTest +class ConfigTest extends MockedTest { use ArraySubsetAsserts; - - /** @var ConfigModel|MockInterface */ - protected $configModel; + use VFSTrait; /** @var Cache */ protected $configCache; + /** @var ConfigFileManager */ + protected $configFileManager; + /** @var IManageConfigValues */ protected $testedConfig; @@ -60,17 +63,22 @@ abstract class ConfigTest extends MockedTest protected function setUp(): void { + $this->setUpVfsDir(); + parent::setUp(); - // Create the config model - $this->configModel = Mockery::mock(ConfigModel::class); $this->configCache = new Cache(); + $this->configFileManager = new ConfigFileManager($this->root->url(), $this->root->url() . '/config/', $this->root->url() . '/static/'); } /** * @return IManageConfigValues */ - abstract public function getInstance(); + public function getInstance() + { + $this->configFileManager->setupCache($this->configCache, []); + return new Config($this->configFileManager, $this->configCache); + } public function dataTests() { @@ -156,12 +164,13 @@ abstract class ConfigTest extends MockedTest /** * Test the configuration initialization + * @dataProvider dataConfigLoad */ public function testSetUp(array $data) { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->once(); + vfsStream::newFile(ConfigFileManager::CONFIG_DATA_FILE) + ->at($this->root->getChild('config')) + ->setContent(ConfigFileTransformer::encode($data)); $this->testedConfig = $this->getInstance(); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); @@ -171,19 +180,23 @@ abstract class ConfigTest extends MockedTest } /** - * Test the configuration load() method + * Test the configuration reload() method * * @param array $data * @param array $load + * + * @dataProvider dataConfigLoad */ - public function testLoad(array $data, array $load) + public function testReload(array $data, array $load) { + vfsStream::newFile(ConfigFileManager::CONFIG_DATA_FILE) + ->at($this->root->getChild('config')) + ->setContent(ConfigFileTransformer::encode($data)); + $this->testedConfig = $this->getInstance(); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); - foreach ($load as $loadedCats) { - $this->testedConfig->load($loadedCats); - } + $this->testedConfig->reload(); // Assert at least loaded cats are loaded foreach ($load as $loadedCats) { @@ -256,23 +269,31 @@ abstract class ConfigTest extends MockedTest /** * Test the configuration load() method with overwrite + * + * @dataProvider dataDoubleLoad */ public function testCacheLoadDouble(array $data1, array $data2, array $expect = []) { + vfsStream::newFile(ConfigFileManager::CONFIG_DATA_FILE) + ->at($this->root->getChild('config')) + ->setContent(ConfigFileTransformer::encode($data1)); + $this->testedConfig = $this->getInstance(); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); - foreach ($data1 as $cat => $data) { - $this->testedConfig->load($cat); - } - // Assert at least loaded cats are loaded foreach ($data1 as $cat => $data) { self::assertConfig($cat, $data); } + vfsStream::newFile(ConfigFileManager::CONFIG_DATA_FILE) + ->at($this->root->getChild('config')) + ->setContent(ConfigFileTransformer::encode($data2)); + + $this->testedConfig->reload(); + foreach ($data2 as $cat => $data) { - $this->testedConfig->load($cat); + self::assertConfig($cat, $data); } } @@ -281,44 +302,19 @@ abstract class ConfigTest extends MockedTest */ public function testLoadWrong() { - $this->configModel->shouldReceive('isConnected')->andReturn(true)->once(); - $this->configModel->shouldReceive('load')->withAnyArgs()->andReturn([])->once(); - - $this->testedConfig = $this->getInstance(); + $this->testedConfig = new Config($this->configFileManager, new Cache()); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); self::assertEmpty($this->testedConfig->getCache()->getAll()); } /** - * Test the configuration get() and set() methods without adapter + * Test the configuration get() and set() methods * * @dataProvider dataTests */ - public function testSetGetWithoutDB($data) + public function testSetGet($data) { - $this->configModel->shouldReceive('isConnected') - ->andReturn(false) - ->times(3); - - $this->testedConfig = $this->getInstance(); - self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); - - self::assertTrue($this->testedConfig->set('test', 'it', $data)); - - self::assertEquals($data, $this->testedConfig->get('test', 'it')); - self::assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); - } - - /** - * Test the configuration get() and set() methods with a model/db - * - * @dataProvider dataTests - */ - public function testSetGetWithDB($data) - { - $this->configModel->shouldReceive('set')->with('test', 'it', $data)->andReturn(true)->once(); - $this->testedConfig = $this->getInstance(); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); @@ -349,41 +345,16 @@ abstract class ConfigTest extends MockedTest self::assertEquals('default', $this->testedConfig->get('test', 'it', 'default', true)); } - /** - * Test the configuration get() method with refresh - * - * @dataProvider dataTests - */ - public function testGetWithRefresh($data) - { - $this->configCache->load(['test' => ['it' => 'now']], Cache::SOURCE_FILE); - - $this->testedConfig = $this->getInstance(); - self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); - - // without refresh - self::assertEquals('now', $this->testedConfig->get('test', 'it')); - self::assertEquals('now', $this->testedConfig->getCache()->get('test', 'it')); - - // with refresh - self::assertEquals($data, $this->testedConfig->get('test', 'it', null, true)); - self::assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); - - // without refresh and wrong value and default - self::assertEquals('default', $this->testedConfig->get('test', 'not', 'default')); - self::assertNull($this->testedConfig->getCache()->get('test', 'not')); - } - /** * Test the configuration delete() method without a model/db * * @dataProvider dataTests */ - public function testDeleteWithoutDB($data) + public function testDelete($data) { $this->configCache->load(['test' => ['it' => $data]], Cache::SOURCE_FILE); - $this->testedConfig = $this->getInstance(); + $this->testedConfig = new Config($this->configFileManager, $this->configCache); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); self::assertEquals($data, $this->testedConfig->get('test', 'it')); @@ -396,51 +367,6 @@ abstract class ConfigTest extends MockedTest self::assertEmpty($this->testedConfig->getCache()->getAll()); } - /** - * Test the configuration delete() method with a model/db - */ - public function testDeleteWithDB() - { - $this->configCache->load(['test' => ['it' => 'now', 'quarter' => 'true']], Cache::SOURCE_FILE); - - $this->configModel->shouldReceive('delete') - ->with('test', 'it') - ->andReturn(false) - ->once(); - $this->configModel->shouldReceive('delete') - ->with('test', 'second') - ->andReturn(true) - ->once(); - $this->configModel->shouldReceive('delete') - ->with('test', 'third') - ->andReturn(false) - ->once(); - $this->configModel->shouldReceive('delete') - ->with('test', 'quarter') - ->andReturn(true) - ->once(); - - $this->testedConfig = $this->getInstance(); - self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); - - // directly set the value to the cache - $this->testedConfig->getCache()->set('test', 'it', 'now'); - - self::assertEquals('now', $this->testedConfig->get('test', 'it')); - self::assertEquals('now', $this->testedConfig->getCache()->get('test', 'it')); - - // delete from cache only - self::assertTrue($this->testedConfig->delete('test', 'it')); - // delete from db only - self::assertTrue($this->testedConfig->delete('test', 'second')); - // no delete - self::assertFalse($this->testedConfig->delete('test', 'third')); - // delete both - self::assertTrue($this->testedConfig->delete('test', 'quarter')); - - self::assertEmpty($this->testedConfig->getCache()->getAll()); - } - /** * Test the configuration get() and set() method where the db value has a higher prio than the config file */ @@ -462,6 +388,12 @@ abstract class ConfigTest extends MockedTest */ public function testSetGetLowPrio() { + vfsStream::newFile(ConfigFileManager::CONFIG_DATA_FILE) + ->at($this->root->getChild('config')) + ->setContent(ConfigFileTransformer::encode([ + 'config' => ['test' => 'it'], + ])); + $this->testedConfig = $this->getInstance(); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); self::assertEquals('it', $this->testedConfig->get('config', 'test')); diff --git a/tests/src/Core/Config/JitConfigTest.php b/tests/src/Core/Config/JitConfigTest.php deleted file mode 100644 index a468ae7276..0000000000 --- a/tests/src/Core/Config/JitConfigTest.php +++ /dev/null @@ -1,255 +0,0 @@ -. - * - */ - -namespace Friendica\Test\src\Core\Config; - -use Friendica\Core\Config\Type\JitConfig; - -class JitConfigTest extends ConfigTest -{ - public function getInstance() - { - return new JitConfig($this->configCache, $this->configModel); - } - - /** - * @dataProvider dataConfigLoad - */ - public function testSetUp(array $data) - { - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => $data['config']]) - ->once(); - - parent::testSetUp($data); - } - - /** - * @dataProvider dataConfigLoad - * - * @param array $data - * @param array $load - */ - public function testLoad(array $data, array $load) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(count($load) + 1); - - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => $data['config']]) - ->once(); - - foreach ($load as $loadCat) { - $this->configModel->shouldReceive('load') - ->with($loadCat) - ->andReturn([$loadCat => $data[$loadCat]]) - ->once(); - } - - parent::testLoad($data, $load); - } - - /** - * @dataProvider dataDoubleLoad - */ - public function testCacheLoadDouble(array $data1, array $data2, array $expect = []) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(count($data1) + count($data2) + 1); - - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => $data1['config']]) - ->once(); - - foreach ($data1 as $cat => $data) { - $this->configModel->shouldReceive('load') - ->with($cat) - ->andReturn([$cat => $data]) - ->once(); - } - - - foreach ($data2 as $cat => $data) { - $this->configModel->shouldReceive('load') - ->with($cat) - ->andReturn([$cat => $data]) - ->once(); - } - - parent::testCacheLoadDouble($data1, $data2); - - // Assert the expected categories - foreach ($data2 as $cat => $data) { - self::assertConfig($cat, $expect[$cat]); - } - } - - /** - * @dataProvider dataTests - */ - public function testSetGetWithDB($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(3); - - $this->configModel->shouldReceive('load')->with('config')->andReturn(['config' => []])->once(); - - parent::testSetGetWithDB($data); - } - - /** - * @dataProvider dataTests - */ - public function testGetWithRefresh($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(4); - - // constructor loading - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => []]) - ->once(); - - // mocking one get without result - $this->configModel->shouldReceive('get') - ->with('test', 'it') - ->andReturn(null) - ->once(); - - // mocking the data get - $this->configModel->shouldReceive('get') - ->with('test', 'it') - ->andReturn($data) - ->once(); - - // mocking second get - $this->configModel->shouldReceive('get') - ->with('test', 'not') - ->andReturn(null) - ->once(); - - parent::testGetWithRefresh($data); - } - - public function testGetWrongWithoutDB() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(false) - ->times(4); - - parent::testGetWrongWithoutDB(); - } - - /** - * @dataProvider dataTests - */ - public function testDeleteWithoutDB($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(false) - ->times(4); - - parent::testDeleteWithoutDB($data); - } - - public function testDeleteWithDB() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(6); - - // constructor loading - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => []]) - ->once(); - - // mocking one get without result - $this->configModel->shouldReceive('get') - ->with('test', 'it') - ->andReturn(null) - ->once(); - - parent::testDeleteWithDB(); - } - - public function testSetGetHighPrio() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true); - - // constructor loading - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => []]) - ->once(); - - $this->configModel->shouldReceive('get') - ->with('config', 'test') - ->andReturn('prio') - ->once(); - - $this->configModel->shouldReceive('set') - ->with('config', 'test', '123') - ->andReturn(true) - ->once(); - - $this->configModel->shouldReceive('get') - ->with('config', 'test') - ->andReturn('123') - ->once(); - - parent::testSetGetHighPrio(); - } - - public function testSetGetLowPrio() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true); - - // constructor loading - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => ['test' => 'it']]) - ->once(); - - $this->configModel->shouldReceive('set') - ->with('config', 'test', '123') - ->andReturn(true) - ->once(); - - // mocking one get without result - $this->configModel->shouldReceive('get') - ->with('config', 'test') - ->andReturn('it') - ->once(); - - parent::testSetGetLowPrio(); - } -} diff --git a/tests/src/Core/Config/PreloadConfigTest.php b/tests/src/Core/Config/PreloadConfigTest.php deleted file mode 100644 index 1f51427613..0000000000 --- a/tests/src/Core/Config/PreloadConfigTest.php +++ /dev/null @@ -1,213 +0,0 @@ -. - * - */ - -namespace Friendica\Test\src\Core\Config; - -use Friendica\Core\Config\Type\PreloadConfig; - -class PreloadConfigTest extends ConfigTest -{ - public function getInstance() - { - return new PreloadConfig($this->configCache, $this->configModel); - } - - /** - * @dataProvider dataConfigLoad - */ - public function testSetUp(array $data) - { - $this->configModel->shouldReceive('load') - ->andReturn($data) - ->once(); - - parent::testSetUp($data); - } - - /** - * @dataProvider dataConfigLoad - * - * @param array $data - * @param array $load - */ - public function testLoad(array $data, array $load) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->once(); - - $this->configModel->shouldReceive('load') - ->andReturn($data) - ->once(); - - parent::testLoad($data, $load); - - // Assert that every category is loaded everytime - foreach ($data as $cat => $values) { - self::assertConfig($cat, $values); - } - } - - /** - * @dataProvider dataDoubleLoad - * - * @param array $data1 - * @param array $data2 - */ - public function testCacheLoadDouble(array $data1, array $data2, array $expect = []) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->once(); - - $this->configModel->shouldReceive('load') - ->andReturn($data1) - ->once(); - - parent::testCacheLoadDouble($data1, $data2); - - // Assert that every category is loaded everytime and is NOT overwritten - foreach ($data1 as $cat => $values) { - self::assertConfig($cat, $values); - } - } - - /** - * @dataProvider dataTests - */ - public function testSetGetWithDB($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(2); - - $this->configModel->shouldReceive('load')->andReturn(['config' => []])->once(); - - parent::testSetGetWithDB($data); - } - - /** - * @dataProvider dataTests - */ - public function testGetWithRefresh($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(2); - - // constructor loading - $this->configModel->shouldReceive('load') - ->andReturn(['config' => []]) - ->once(); - - // mocking one get - $this->configModel->shouldReceive('get') - ->with('test', 'it') - ->andReturn($data) - ->once(); - - parent::testGetWithRefresh($data); - } - - - public function testGetWrongWithoutDB() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(false) - ->times(2); - - parent::testGetWrongWithoutDB(); - } - - /** - * @dataProvider dataTests - */ - public function testDeleteWithoutDB($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(false) - ->times(2); - - parent::testDeleteWithoutDB($data); - } - - public function testDeleteWithDB() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(5); - - // constructor loading - $this->configModel->shouldReceive('load') - ->andReturn(['config' => []]) - ->once(); - - parent::testDeleteWithDB(); - } - - - public function testSetGetHighPrio() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true); - - // constructor loading - $this->configModel->shouldReceive('load') - ->andReturn(['config' => []]) - ->once(); - - $this->configModel->shouldReceive('set') - ->with('config', 'test', '123') - ->andReturn(true) - ->once(); - - $this->configModel->shouldReceive('get') - ->with('config', 'test') - ->andReturn('123') - ->once(); - - parent::testSetGetHighPrio(); - } - - public function testSetGetLowPrio() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true); - - // constructor loading - $this->configModel->shouldReceive('load') - ->andReturn(['config' => ['test' => 'it']]) - ->once(); - - $this->configModel->shouldReceive('set') - ->with('config', 'test', '123') - ->andReturn(true) - ->once(); - - // mocking one get without result - $this->configModel->shouldReceive('get') - ->with('config', 'test') - ->andReturn('it') - ->once(); - - parent::testSetGetLowPrio(); - } -} diff --git a/tests/src/Core/Lock/SemaphoreLockTest.php b/tests/src/Core/Lock/SemaphoreLockTest.php index a8f95f6fc6..6473935cab 100644 --- a/tests/src/Core/Lock/SemaphoreLockTest.php +++ b/tests/src/Core/Lock/SemaphoreLockTest.php @@ -24,7 +24,10 @@ namespace Friendica\Test\src\Core\Lock; use Dice\Dice; use Friendica\App; use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Config\Model\Config; use Friendica\Core\Config\Type\JitConfig; +use Friendica\Core\Config\Util\ConfigFileManager; +use Friendica\Core\Config\ValueObject\Cache; use Friendica\Core\Lock\Type\SemaphoreLock; use Friendica\Core\System; use Friendica\DI; @@ -42,11 +45,8 @@ class SemaphoreLockTest extends LockTest $app->shouldReceive('getHostname')->andReturn('friendica.local'); $dice->shouldReceive('create')->with(App::class)->andReturn($app); - $configMock = Mockery::mock(JitConfig::class); - $configMock - ->shouldReceive('get') - ->with('system', 'temppath') - ->andReturn('/tmp/'); + $configCache = new Cache(['system' => ['temppath' => '/tmp']]); + $configMock = new Config(Mockery::mock(ConfigFileManager::class), $configCache); $dice->shouldReceive('create')->with(IManageConfigValues::class)->andReturn($configMock); // @todo Because "get_temppath()" is using static methods, we have to initialize the BaseObject diff --git a/tests/src/Core/Storage/Repository/StorageManagerTest.php b/tests/src/Core/Storage/Repository/StorageManagerTest.php index af198706f6..db4ca8ef2b 100644 --- a/tests/src/Core/Storage/Repository/StorageManagerTest.php +++ b/tests/src/Core/Storage/Repository/StorageManagerTest.php @@ -22,9 +22,7 @@ namespace Friendica\Test\src\Core\Storage\Repository; use Dice\Dice; -use Friendica\App\Mode; use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Config\Type\PreloadConfig; use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Core\Session\Capability\IHandleSessions; @@ -41,7 +39,6 @@ use Friendica\Database\Definition\DbaDefinition; use Friendica\Database\Definition\ViewDefinition; use Friendica\DI; use Friendica\Core\Config\Factory\Config; -use Friendica\Core\Config\Repository; use Friendica\Core\Storage\Type; use Friendica\Test\DatabaseTest; use Friendica\Test\Util\Database\StaticDatabase; @@ -89,8 +86,7 @@ class StorageManagerTest extends DatabaseTest $this->dba = new StaticDatabase($configCache, $profiler, $dbaDefinition, $viewDefinition); - $configModel = new Repository\Config($this->dba, new Mode(Mode::DBCONFIGAVAILABLE)); - $this->config = new PreloadConfig($configCache, $configModel); + $this->config = new \Friendica\Core\Config\Model\Config($loader, $configCache); $this->config->set('storage', 'name', 'Database'); $this->config->set('storage', 'filesystem_path', $this->root->getChild(Type\FilesystemConfig::DEFAULT_BASE_FOLDER)->url()); diff --git a/tests/src/Database/DBATest.php b/tests/src/Database/DBATest.php index 44a6e01d3b..0c3c5e1604 100644 --- a/tests/src/Database/DBATest.php +++ b/tests/src/Database/DBATest.php @@ -52,10 +52,7 @@ class DBATest extends DatabaseTest */ public function testExists() { - self::assertTrue(DBA::exists('config', [])); + self::assertTrue(DBA::exists('user', [])); self::assertFalse(DBA::exists('notable', [])); - - self::assertTrue(DBA::exists('config', ['k' => 'hostname'])); - self::assertFalse(DBA::exists('config', ['k' => 'nonsense'])); } } diff --git a/tests/src/Database/DBStructureTest.php b/tests/src/Database/DBStructureTest.php index 67455f8512..dfb46514fd 100644 --- a/tests/src/Database/DBStructureTest.php +++ b/tests/src/Database/DBStructureTest.php @@ -44,30 +44,30 @@ class DBStructureTest extends DatabaseTest * @small */ public function testExists() { - self::assertTrue(DBStructure::existsTable('config')); + self::assertTrue(DBStructure::existsTable('user')); self::assertFalse(DBStructure::existsTable('notatable')); - self::assertTrue(DBStructure::existsColumn('config', ['k'])); - self::assertFalse(DBStructure::existsColumn('config', ['nonsense'])); - self::assertFalse(DBStructure::existsColumn('config', ['k', 'nonsense'])); + self::assertTrue(DBStructure::existsColumn('user', ['uid'])); + self::assertFalse(DBStructure::existsColumn('user', ['nonsense'])); + self::assertFalse(DBStructure::existsColumn('user', ['uid', 'nonsense'])); } /** * @small */ public function testRename() { - $fromColumn = 'k'; - $toColumn = 'key'; - $fromType = 'varbinary(255) not null'; - $toType = 'varbinary(255) not null comment \'Test To Type\''; + $fromColumn = 'email'; + $toColumn = 'email_key'; + $fromType = 'varchar(255) NOT NULL DEFAULT \'\' COMMENT \'the users email address\''; + $toType = 'varchar(255) NOT NULL DEFAULT \'\' COMMENT \'Adapted column\''; - self::assertTrue(DBStructure::rename('config', [ $fromColumn => [ $toColumn, $toType ]])); - self::assertTrue(DBStructure::existsColumn('config', [ $toColumn ])); - self::assertFalse(DBStructure::existsColumn('config', [ $fromColumn ])); + self::assertTrue(DBStructure::rename('user', [ $fromColumn => [ $toColumn, $toType ]])); + self::assertTrue(DBStructure::existsColumn('user', [ $toColumn ])); + self::assertFalse(DBStructure::existsColumn('user', [ $fromColumn ])); - self::assertTrue(DBStructure::rename('config', [ $toColumn => [ $fromColumn, $fromType ]])); - self::assertTrue(DBStructure::existsColumn('config', [ $fromColumn ])); - self::assertFalse(DBStructure::existsColumn('config', [ $toColumn ])); + self::assertTrue(DBStructure::rename('user', [ $toColumn => [ $fromColumn, $fromType ]])); + self::assertTrue(DBStructure::existsColumn('user', [ $fromColumn ])); + self::assertFalse(DBStructure::existsColumn('user', [ $toColumn ])); } /** diff --git a/tests/src/Module/Api/GnuSocial/GnuSocial/ConfigTest.php b/tests/src/Module/Api/GnuSocial/GnuSocial/ConfigTest.php index 034e1fc993..0d0afa64bc 100644 --- a/tests/src/Module/Api/GnuSocial/GnuSocial/ConfigTest.php +++ b/tests/src/Module/Api/GnuSocial/GnuSocial/ConfigTest.php @@ -26,6 +26,7 @@ use Friendica\App\Router; use Friendica\DI; use Friendica\Module\Api\GNUSocial\GNUSocial\Config; use Friendica\Test\src\Module\Api\ApiTest; +use Friendica\Test\Util\VFSTrait; class ConfigTest extends ApiTest { diff --git a/tests/src/Util/BaseURLTest.php b/tests/src/Util/BaseURLTest.php index 0be83be0a7..2733096e33 100644 --- a/tests/src/Util/BaseURLTest.php +++ b/tests/src/Util/BaseURLTest.php @@ -199,24 +199,34 @@ class BaseURLTest extends MockedTest $configMock->shouldReceive('get')->with('system', 'ssl_policy')->andReturn($input['sslPolicy']); $configMock->shouldReceive('get')->with('system', 'url')->andReturn($input['url']); + $savable = false; + // If we don't have an urlPath as an input, we assert it, we will save it to the DB for the next time if (!isset($input['urlPath']) && isset($assert['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $assert['urlPath'])->once(); + $configMock->shouldReceive('set')->with('system', 'urlpath', $assert['urlPath'], false)->once(); + $savable = true; } // If we don't have the ssl_policy as an input, we assert it, we will save it to the DB for the next time if (!isset($input['sslPolicy']) && isset($assert['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $assert['sslPolicy'])->once(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $assert['sslPolicy'], false)->once(); + $savable = true; } // If we don't have the hostname as an input, we assert it, we will save it to the DB for the next time if (empty($input['hostname']) && !empty($assert['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $assert['hostname'])->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', $assert['hostname'], false)->once(); + $savable = true; } // If we don't have an URL at first, but we assert it, we will save it to the DB for the next time if (empty($input['url']) && !empty($assert['url'])) { - $configMock->shouldReceive('set')->with('system', 'url', $assert['url'])->once(); + $configMock->shouldReceive('set')->with('system', 'url', $assert['url'], false)->once(); + $savable = true; + } + + if ($savable) { + $configMock->shouldReceive('save')->once(); } $baseUrl = new BaseURL($configMock, $server); @@ -325,18 +335,20 @@ class BaseURLTest extends MockedTest $baseUrl = new BaseURL($configMock, []); if (isset($save['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'], false)->andReturn(true)->once(); } if (isset($save['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'], false)->andReturn(true)->once(); } if (isset($save['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'], false)->andReturn(true)->once(); } - $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'url', $url, false)->andReturn(true)->once(); + + $configMock->shouldReceive('save')->once(); $baseUrl->save($save['hostname'], $save['sslPolicy'], $save['urlPath']); @@ -363,18 +375,20 @@ class BaseURLTest extends MockedTest $baseUrl = new BaseURL($configMock, []); if (isset($save['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'], false)->andReturn(true)->once(); } if (isset($save['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'], false)->andReturn(true)->once(); } if (isset($save['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'], false)->andReturn(true)->once(); } - $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'url', $url, false)->andReturn(true)->once(); + + $configMock->shouldReceive('save')->once(); $baseUrl->saveByURL($url); @@ -531,22 +545,25 @@ class BaseURLTest extends MockedTest switch ($fail) { case 'hostname': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(false)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(false)->once(); break; case 'sslPolicy': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(false)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(false)->once(); + $configMock->shouldReceive('save')->once(); break; case 'urlPath': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(false)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any(), false)->andReturn(false)->once(); + $configMock->shouldReceive('save')->once(); break; case 'url': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'url', \Mockery::any())->andReturn(false)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'url', \Mockery::any(), false)->andReturn(false)->once(); + $configMock->shouldReceive('save')->once(); break; } From 1e574d5383e18de5b80445558a77b5330e7663be Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 28 Dec 2022 02:07:38 +0100 Subject: [PATCH 08/71] Refactor IManageConfigValues interface --- bin/daemon.php | 2 +- src/App.php | 3 +- src/Console/Config.php | 4 +-- .../Config/Capability/IManageConfigValues.php | 12 +++---- src/Core/Config/Factory/Config.php | 17 ---------- src/Core/Config/Model/Config.php | 32 +++++++++++++++---- src/Core/Update.php | 8 ++--- src/Core/Worker.php | 2 +- src/Module/Friendica.php | 2 +- src/Worker/DBUpdate.php | 2 +- static/dependencies.config.php | 3 ++ update.php | 6 ---- view/theme/frio/style.php | 2 +- 13 files changed, 46 insertions(+), 49 deletions(-) diff --git a/bin/daemon.php b/bin/daemon.php index 385f725e34..f9ed693f38 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -71,7 +71,7 @@ if (DI::mode()->isInstall()) { DI::mode()->setExecutor(Mode::DAEMON); -DI::config()->load(); +DI::config()->reload(); if (empty(DI::config()->get('system', 'pidfile'))) { die(<<config->getCache()->get('system', 'basepath'); + return $this->config->get('system', 'basepath'); } /** diff --git a/src/Console/Config.php b/src/Console/Config.php index b57cdbeec0..8df7c28f21 100644 --- a/src/Console/Config.php +++ b/src/Console/Config.php @@ -157,7 +157,7 @@ HELP; if (count($this->args) == 1) { $cat = $this->getArgument(0); - $this->config->load($cat); + $this->config->reload(); $configCache = $this->config->getCache(); if ($configCache->get($cat) !== null) { @@ -178,7 +178,7 @@ HELP; } if (count($this->args) == 0) { - $this->config->load(); + $this->config->reload(); if ($this->config->get('system', 'config_adapter') == 'jit' && $this->appMode->has(App\Mode::DBCONFIGAVAILABLE)) { $this->out('Warning: The JIT (Just In Time) Config adapter doesn\'t support loading the entire configuration, showing file config only'); diff --git a/src/Core/Config/Capability/IManageConfigValues.php b/src/Core/Config/Capability/IManageConfigValues.php index 27238822ae..88fa96314b 100644 --- a/src/Core/Config/Capability/IManageConfigValues.php +++ b/src/Core/Config/Capability/IManageConfigValues.php @@ -30,36 +30,32 @@ use Friendica\Core\Config\ValueObject\Cache; interface IManageConfigValues { /** - * Loads all configuration values of family into a cached storage. + * Reloads all configuration values (from filesystem and environment variables) * * All configuration values of the system are stored in the cache. * - * @param string $cat The category of the configuration value - * * @return void * * @throws ConfigPersistenceException In case the persistence layer throws errors */ - public function load(string $cat = 'config'); + public function reload(); /** * Get a particular user's config variable given the category name * ($cat) and a $key. * * Get a particular config value from the given category ($cat) - * and the $key from a cached storage either from the database or from the cache. * * @param string $cat The category of the configuration value * @param string $key The configuration key to query * @param mixed $default_value Deprecated, use `Config->get($cat, $key, null, $refresh) ?? $default_value` instead - * @param boolean $refresh optional, If true the config is loaded from the db and not from the cache (default: false) * * @return mixed Stored value or null if it does not exist * * @throws ConfigPersistenceException In case the persistence layer throws errors * */ - public function get(string $cat, string $key, $default_value = null, bool $refresh = false); + public function get(string $cat, string $key, $default_value = null); /** * Sets a configuration value for system config @@ -81,6 +77,8 @@ interface IManageConfigValues /** * Save back the overridden values of the config cache + * + * @throws ConfigPersistenceException In case the persistence layer throws errors */ public function save(); diff --git a/src/Core/Config/Factory/Config.php b/src/Core/Config/Factory/Config.php index fac931fac5..e1094fd70d 100644 --- a/src/Core/Config/Factory/Config.php +++ b/src/Core/Config/Factory/Config.php @@ -81,21 +81,4 @@ class Config return $configCache; } - - /** - * @param Cache $configCache The config cache of this adapter - * @param Repository\Config $configRepo The configuration repository - * - * @return Capability\IManageConfigValues - */ - public function create(Util\ConfigFileManager $loader, Cache $configCache, Repository\Config $configRepo) - { - if ($configCache->get('system', 'config_adapter') === 'preload') { - $configuration = new Type\PreloadConfig($loader, $configCache, $configRepo); - } else { - $configuration = new Type\JitConfig($loader, $configCache, $configRepo); - } - - return $configuration; - } } diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php index 3af2f7e4b1..3de97158f2 100644 --- a/src/Core/Config/Model/Config.php +++ b/src/Core/Config/Model/Config.php @@ -22,6 +22,8 @@ namespace Friendica\Core\Config\Model; use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Config\Exception\ConfigFileException; +use Friendica\Core\Config\Exception\ConfigPersistenceException; use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; @@ -38,14 +40,19 @@ class Config implements IManageConfigValues /** @var ConfigFileManager */ protected $configFileManager; + /** @var array */ + protected $server; + /** * @param ConfigFileManager $configFileManager The configuration file manager to save back configs - * @param Cache $configCache The configuration cache (based on the config-files) + * @param Cache $configCache The configuration cache (based on the config-files) + * @param array $server The $_SERVER variable */ - public function __construct(ConfigFileManager $configFileManager, Cache $configCache) + public function __construct(ConfigFileManager $configFileManager, Cache $configCache, array $server = []) { $this->configFileManager = $configFileManager; $this->configCache = $configCache; + $this->server = $server; } /** @@ -56,24 +63,36 @@ class Config implements IManageConfigValues return $this->configCache; } + /** {@inheritDoc} */ public function save() { - $this->configFileManager->saveData($this->configCache); + try { + $this->configFileManager->saveData($this->configCache); + } catch (ConfigFileException $e) { + throw new ConfigPersistenceException('Cannot save config', $e); + } } - public function load(string $cat = 'config') + /** {@inheritDoc} */ + public function reload() { $configCache = new Cache(); - $this->configFileManager->setupCache($configCache, $_SERVER); + try { + $this->configFileManager->setupCache($configCache, $this->server); + } catch (ConfigFileException $e) { + throw new ConfigPersistenceException('Cannot reload config', $e); + } $this->configCache = $configCache; } - public function get(string $cat, string $key, $default_value = null, bool $refresh = false) + /** {@inheritDoc} */ + public function get(string $cat, string $key, $default_value = null) { return $this->configCache->get($cat, $key) ?? $default_value; } + /** {@inheritDoc} */ public function set(string $cat, string $key, $value, bool $autosave = true): bool { $stored = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); @@ -85,6 +104,7 @@ class Config implements IManageConfigValues return $stored; } + /** {@inheritDoc} */ public function delete(string $cat, string $key, bool $autosave = true): bool { $removed = $this->configCache->delete($cat, $key); diff --git a/src/Core/Update.php b/src/Core/Update.php index 8da473839d..9a2ebe1bba 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -54,7 +54,7 @@ class Update } // Don't check the status if the last update was failed - if (DI::config()->get('system', 'update', Update::SUCCESS, true) == Update::FAILED) { + if (DI::config()->get('system', 'update', Update::SUCCESS) == Update::FAILED) { return; } @@ -119,7 +119,7 @@ class Update DI::lock()->release('dbupdate', true); } - $build = DI::config()->get('system', 'build', null, true); + $build = DI::config()->get('system', 'build', null); if (empty($build) || ($build > DB_UPDATE_VERSION)) { $build = DB_UPDATE_VERSION - 1; @@ -132,7 +132,7 @@ class Update $stored = intval($build); $current = intval(DB_UPDATE_VERSION); if ($stored < $current || $force) { - DI::config()->load('database'); + DI::config()->reload(); // Compare the current structure with the defined structure // If the Lock is acquired, never release it automatically to avoid double updates @@ -141,7 +141,7 @@ class Update Logger::notice('Update starting.', ['from' => $stored, 'to' => $current]); // Checks if the build changed during Lock acquiring (so no double update occurs) - $retryBuild = DI::config()->get('system', 'build', null, true); + $retryBuild = DI::config()->get('system', 'build', null); if ($retryBuild !== $build) { Logger::notice('Update already done.', ['from' => $stored, 'to' => $current]); DI::lock()->release('dbupdate'); diff --git a/src/Core/Worker.php b/src/Core/Worker.php index 8fc74f1d83..2db9256d13 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -331,7 +331,7 @@ class Worker $mypid = getmypid(); // Quit when in maintenance - if (DI::config()->get('system', 'maintenance', false, true)) { + if (DI::config()->get('system', 'maintenance', false)) { Logger::notice('Maintenance mode - quit process', ['pid' => $mypid]); return false; } diff --git a/src/Module/Friendica.php b/src/Module/Friendica.php index 72360ddcaf..bbcccd7dad 100644 --- a/src/Module/Friendica.php +++ b/src/Module/Friendica.php @@ -157,7 +157,7 @@ class Friendica extends BaseModule $visible_addons = Addon::getVisibleList(); - $config->load('feature_lock'); + $config->reload(); $locked_features = []; $featureLocks = $config->get('config', 'feature_lock'); if (isset($featureLocks)) { diff --git a/src/Worker/DBUpdate.php b/src/Worker/DBUpdate.php index e11f7bf40d..7b7c3b8c92 100644 --- a/src/Worker/DBUpdate.php +++ b/src/Worker/DBUpdate.php @@ -32,7 +32,7 @@ class DBUpdate public static function execute() { // Just in case the last update wasn't failed - if (DI::config()->get('system', 'update', Update::SUCCESS, true) != Update::FAILED) { + if (DI::config()->get('system', 'update', Update::SUCCESS) != Update::FAILED) { Update::run(DI::app()->getBasePath()); } } diff --git a/static/dependencies.config.php b/static/dependencies.config.php index 90f01feede..1844001bb4 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -99,6 +99,9 @@ return [ ], Config\Capability\IManageConfigValues::class => [ 'instanceOf' => Config\Model\Config::class, + 'constructParams' => [ + $_SERVER, + ], ], PConfig\Capability\IManagePersonalConfigValues::class => [ 'instanceOf' => PConfig\Factory\PConfig::class, diff --git a/update.php b/update.php index fda04b6ce4..b9edbabd75 100644 --- a/update.php +++ b/update.php @@ -1178,12 +1178,6 @@ function update_1505() 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) { diff --git a/view/theme/frio/style.php b/view/theme/frio/style.php index 6e05c6b328..8feefdc930 100644 --- a/view/theme/frio/style.php +++ b/view/theme/frio/style.php @@ -49,7 +49,7 @@ $login_bg_color = ''; $modified = time(); if (DI::mode()->has(\Friendica\App\Mode::MAINTENANCEDISABLED)) { - DI::config()->load('frio'); + DI::config()->reload('frio'); // Default to hard-coded values for empty settings $scheme = DI::config()->get('frio', 'scheme', DI::config()->get('frio', 'schema')); From 10f3de0aa2efca1c94b5780b2da21e06cd5129be Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 28 Dec 2022 02:57:57 +0100 Subject: [PATCH 09/71] Remove deprecated Mode::DBCONFIGAVAILABLE --- src/App/Mode.php | 16 +++--------- src/Console/Cache.php | 2 +- src/Console/Config.php | 8 ------ src/Console/Lock.php | 2 +- tests/functional/DependencyCheckTest.php | 1 - tests/src/App/ModeTest.php | 31 ------------------------ 6 files changed, 5 insertions(+), 55 deletions(-) diff --git a/src/App/Mode.php b/src/App/Mode.php index ca78ebd5bc..514df91c64 100644 --- a/src/App/Mode.php +++ b/src/App/Mode.php @@ -149,16 +149,7 @@ class Mode $mode |= Mode::DBAVAILABLE; - if ($database->fetchFirst("SHOW TABLES LIKE 'config'") === false) { - return new Mode($mode); - } - - $mode |= Mode::DBCONFIGAVAILABLE; - - if (!empty($configCache->get('system', 'maintenance')) || - // Don't use Config or Configuration here because we're possibly BEFORE initializing the Configuration, - // so this could lead to a dependency circle - !empty($database->selectFirst('config', ['v'], ['cat' => 'system', 'k' => 'maintenance'])['v'])) { + if (!empty($configCache->get('system', 'maintenance'))) { return new Mode($mode); } @@ -232,14 +223,14 @@ class Mode } /** - * Install mode is when the local config file is missing or the DB schema hasn't been installed yet. + * Install mode is when the local config file is missing or the database isn't available. * * @return bool Whether installation mode is active (local/database configuration files present or not) */ public function isInstall(): bool { return !$this->has(Mode::LOCALCONFIGPRESENT) || - !$this->has(MODE::DBCONFIGAVAILABLE); + !$this->has(MODE::DBAVAILABLE); } /** @@ -251,7 +242,6 @@ class Mode { return $this->has(Mode::LOCALCONFIGPRESENT) && $this->has(Mode::DBAVAILABLE) && - $this->has(Mode::DBCONFIGAVAILABLE) && $this->has(Mode::MAINTENANCEDISABLED); } diff --git a/src/Console/Cache.php b/src/Console/Cache.php index e4d1135335..c553716993 100644 --- a/src/Console/Cache.php +++ b/src/Console/Cache.php @@ -99,7 +99,7 @@ HELP; $this->out('Options: ' . var_export($this->options, true)); } - if (!$this->appMode->has(App\Mode::DBCONFIGAVAILABLE)) { + if (!$this->appMode->has(App\Mode::DBAVAILABLE)) { $this->out('Database isn\'t ready or populated yet, database cache won\'t be available'); } diff --git a/src/Console/Config.php b/src/Console/Config.php index 8df7c28f21..efb795360c 100644 --- a/src/Console/Config.php +++ b/src/Console/Config.php @@ -115,10 +115,6 @@ HELP; throw new CommandArgsException('Too many arguments'); } - if (!$this->appMode->has(App\Mode::DBCONFIGAVAILABLE)) { - $this->out('Database isn\'t ready or populated yet, showing file config only'); - } - if (count($this->args) == 3) { $cat = $this->getArgument(0); $key = $this->getArgument(1); @@ -180,10 +176,6 @@ HELP; if (count($this->args) == 0) { $this->config->reload(); - if ($this->config->get('system', 'config_adapter') == 'jit' && $this->appMode->has(App\Mode::DBCONFIGAVAILABLE)) { - $this->out('Warning: The JIT (Just In Time) Config adapter doesn\'t support loading the entire configuration, showing file config only'); - } - $config = $this->config->getCache()->getAll(); foreach ($config as $cat => $section) { if (is_array($section)) { diff --git a/src/Console/Lock.php b/src/Console/Lock.php index 4e324691ca..0acede5df8 100644 --- a/src/Console/Lock.php +++ b/src/Console/Lock.php @@ -93,7 +93,7 @@ HELP; $this->out('Options: ' . var_export($this->options, true)); } - if (!$this->appMode->has(App\Mode::DBCONFIGAVAILABLE)) { + if (!$this->appMode->has(App\Mode::DBAVAILABLE)) { $this->out('Database isn\'t ready or populated yet, database cache won\'t be available'); } diff --git a/tests/functional/DependencyCheckTest.php b/tests/functional/DependencyCheckTest.php index 9bc7624d6a..a371ffea4c 100644 --- a/tests/functional/DependencyCheckTest.php +++ b/tests/functional/DependencyCheckTest.php @@ -150,7 +150,6 @@ class DependencyCheckTest extends TestCase self::assertTrue($mode->has(App\Mode::LOCALCONFIGPRESENT), 'No local config present'); self::assertTrue($mode->has(App\Mode::DBAVAILABLE), 'Database is not available'); - self::assertTrue($mode->has(App\Mode::DBCONFIGAVAILABLE), 'Database config is not available'); self::assertTrue($mode->has(App\Mode::MAINTENANCEDISABLED), 'In maintenance mode'); self::assertTrue($mode->isNormal(), 'Not in normal mode'); diff --git a/tests/src/App/ModeTest.php b/tests/src/App/ModeTest.php index 86a6c7763d..aecd2b1752 100644 --- a/tests/src/App/ModeTest.php +++ b/tests/src/App/ModeTest.php @@ -102,29 +102,11 @@ class ModeTest extends MockedTest self::assertFalse($mode->has(Mode::DBAVAILABLE)); } - public function testWithoutDatabaseSetup() - { - $this->basePathMock->shouldReceive('getPath')->andReturn($this->root->url())->once(); - - $this->databaseMock->shouldReceive('connected')->andReturn(true)->once(); - $this->databaseMock->shouldReceive('fetchFirst') - ->with('SHOW TABLES LIKE \'config\'')->andReturn(false)->once(); - - $mode = (new Mode())->determine($this->basePathMock, $this->databaseMock, $this->configCacheMock); - - self::assertFalse($mode->isNormal()); - self::assertTrue($mode->isInstall()); - - self::assertTrue($mode->has(Mode::LOCALCONFIGPRESENT)); - } - public function testWithMaintenanceMode() { $this->basePathMock->shouldReceive('getPath')->andReturn($this->root->url())->once(); $this->databaseMock->shouldReceive('connected')->andReturn(true)->once(); - $this->databaseMock->shouldReceive('fetchFirst') - ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once(); $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance') ->andReturn(true)->once(); @@ -133,7 +115,6 @@ class ModeTest extends MockedTest self::assertFalse($mode->isNormal()); self::assertFalse($mode->isInstall()); - self::assertTrue($mode->has(Mode::DBCONFIGAVAILABLE)); self::assertFalse($mode->has(Mode::MAINTENANCEDISABLED)); } @@ -142,20 +123,14 @@ class ModeTest extends MockedTest $this->basePathMock->shouldReceive('getPath')->andReturn($this->root->url())->once(); $this->databaseMock->shouldReceive('connected')->andReturn(true)->once(); - $this->databaseMock->shouldReceive('fetchFirst') - ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once(); $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance') ->andReturn(false)->once(); - $this->databaseMock->shouldReceive('selectFirst') - ->with('config', ['v'], ['cat' => 'system', 'k' => 'maintenance']) - ->andReturn(['v' => null])->once(); $mode = (new Mode())->determine($this->basePathMock, $this->databaseMock, $this->configCacheMock); self::assertTrue($mode->isNormal()); self::assertFalse($mode->isInstall()); - self::assertTrue($mode->has(Mode::DBCONFIGAVAILABLE)); self::assertTrue($mode->has(Mode::MAINTENANCEDISABLED)); } @@ -167,20 +142,14 @@ class ModeTest extends MockedTest $this->basePathMock->shouldReceive('getPath')->andReturn($this->root->url())->once(); $this->databaseMock->shouldReceive('connected')->andReturn(true)->once(); - $this->databaseMock->shouldReceive('fetchFirst') - ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once(); $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance') ->andReturn(false)->once(); - $this->databaseMock->shouldReceive('selectFirst') - ->with('config', ['v'], ['cat' => 'system', 'k' => 'maintenance']) - ->andReturn(['v' => '0'])->once(); $mode = (new Mode())->determine($this->basePathMock, $this->databaseMock, $this->configCacheMock); self::assertTrue($mode->isNormal()); self::assertFalse($mode->isInstall()); - self::assertTrue($mode->has(Mode::DBCONFIGAVAILABLE)); self::assertTrue($mode->has(Mode::MAINTENANCEDISABLED)); } From 88b3effc180c5477bc8002b9d9ea8f3f628588dd Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 28 Dec 2022 03:32:52 +0100 Subject: [PATCH 10/71] Use toConfigValue in case of serialized, legacy data --- src/Core/Config/ValueObject/Cache.php | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index c427996c35..2bac625ad2 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -182,6 +182,8 @@ class Cache $key == 'password' && is_string($value)) { $this->config[$cat][$key] = new HiddenString((string)$value); + } else if (is_string($value)) { + $this->config[$cat][$key] = self::toConfigValue($value); } else { $this->config[$cat][$key] = $value; } @@ -191,6 +193,35 @@ class Cache return true; } + /** + * Formats a DB value to a config value + * - null = The db-value isn't set + * - bool = The db-value is either '0' or '1' + * - array = The db-value is a serialized array + * - string = The db-value is a string + * + * Keep in mind that there aren't any numeric/integer config values in the database + * + * @param string|null $value + * + * @return null|array|string + */ + public static function toConfigValue(?string $value) + { + if (!isset($value)) { + return null; + } + + switch (true) { + // manage array value + case preg_match("|^a:[0-9]+:{.*}$|s", $value): + return unserialize($value); + + default: + return $value; + } + } + /** * Deletes a value from the config cache. * From 4585335db93777463bf917604a8c8339b61eac91 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 1 Jan 2023 22:09:40 +0100 Subject: [PATCH 11/71] Check if table exists --- update.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/update.php b/update.php index b9edbabd75..ad33dde012 100644 --- a/update.php +++ b/update.php @@ -1148,6 +1148,10 @@ function update_1502() function update_1505() { + if (!DBStructure::existsTable('config')) { + return Update::SUCCESS; + } + $conditions = [ "((`cat` = ?) AND ((`k` LIKE ?) OR (`k` = ?) OR (`k` LIKE ?) OR (`k` = ?))) OR " . "((`cat` != ?) AND (`k` LIKE ?)) OR " . From 5ff1d431aa313c8e69d97dcdf23c69a38b69ba02 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 1 Jan 2023 22:18:57 +0100 Subject: [PATCH 12/71] add another test --- tests/datasets/config/C.node.config.php | 48 +++++++++++++++++++ .../Config/Util/ConfigFileTransformerTest.php | 3 ++ 2 files changed, 51 insertions(+) create mode 100644 tests/datasets/config/C.node.config.php diff --git a/tests/datasets/config/C.node.config.php b/tests/datasets/config/C.node.config.php new file mode 100644 index 0000000000..6d93d8d7ad --- /dev/null +++ b/tests/datasets/config/C.node.config.php @@ -0,0 +1,48 @@ + [ + 'hostname' => 'friendica.local', + ], + 'system' => [ + 'disable_email_validation' => 1, + 'disable_password_exposed' => 1, + 'throttle_limit_day' => 100, + 'throttle_limit_week' => 100, + 'throttle_limit_month' => 100, + 'temppath' => '/tmp/friendica.local', + 'theme' => 'frio', + 'url' => 'https://friendica.local', + 'urlpath' => '', + 'build' => 1508, + 'maintenance' => false, + 'dbupdate' => 1, + 'update' => 0, + 'spoolpath' => '/tmp/localhost/spool', + 'actor_name' => 'friendica', + 'site_prvkey' => '-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALgypZoZ2X7rYCHT +pXZRPKZYOJrtzAZoAD6a2FESfax/mW7tGF8/XGcsu4E8a0WUs18CDb09iDlECs0r +WFkyxOsS55FyDPVNOVheU6ziqmjTNggr1qR8iIpPW2xHAnFjCfvJxgaUfszdoeUV +mhA++BrxFGRpfcH49O+dVcjisJEVAgMBAAECgYEAq0QsRkSSvjgMgmdQCdsvEVwm +BafldG9vCsbfK0KOJ73c5A8AAk/fku88yMVs2J2SylwWekakStrBUFNlKkrSXEv3 +r1l0b+dfniaTGJkawqgRh+U/0G9LN+cdZYt5EuhNhCbmIQB+FOI12jAx6COwEENi +824zrrwn0BU1VTOMwwECQQDnBqq0J9JCJAtjqX+3xd8DvTRYjYvtvXlQ8fwrGxBc +GwURNG8aMGTaj+sn2kVWNt4wLQqj/CTJ42y0bkKYMJftAkEAzBwSqfuMZD/+nEkM +wDQb1G2z+BaxLh5JJQo80WX9tORbspOkbEuPgFprO6NB0/vNH5m4AaL3jymokH1p +JfVoyQJBAN+GSsmOMdf+qepeiA0V7OXgPXJkWXvHtEZGK1bFk7maBvgThF+RbTMu +xjZD8IwvACEao03wWuPfIEEe4V4Avi0CQCc5FdUYg+gX7CO4XfzphpeR5U29fprw +MvotN3a99L04TO7KNISjGJZ/ya+SNeo4rzhtX9DgslYOmVf64aPrvxECQQDB79vF +Kx2HyacBSErXrlqqkPdFo8KxhKCAVhrs0VBU1WPswolzsZvRdFuielhGP49DjjE7 +JV1Als1hl1xTduNb +-----END PRIVATE KEY----- + ', + 'site_pubkey' => '-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4MqWaGdl+62Ah06V2UTymWDia +7cwGaAA+mthREn2sf5lu7RhfP1xnLLuBPGtFlLNfAg29PYg5RArNK1hZMsTrEueR +cgz1TTlYXlOs4qpo0zYIK9akfIiKT1tsRwJxYwn7ycYGlH7M3aHlFZoQPvga8RRk +aX3B+PTvnVXI4rCRFQIDAQAB +-----END PUBLIC KEY----- + ', + ], +]; diff --git a/tests/src/Core/Config/Util/ConfigFileTransformerTest.php b/tests/src/Core/Config/Util/ConfigFileTransformerTest.php index 6cd5bc7064..5a0ab96a46 100644 --- a/tests/src/Core/Config/Util/ConfigFileTransformerTest.php +++ b/tests/src/Core/Config/Util/ConfigFileTransformerTest.php @@ -35,6 +35,9 @@ class ConfigFileTransformerTest extends MockedTest 'extended' => [ 'configFile' => (dirname(__DIR__, 4) . '/datasets/config/B.node.config.php'), ], + 'friendica.local' => [ + 'configFile' => (dirname(__DIR__, 4) . '/datasets/config/C.node.config.php'), + ], ]; } From ae1533e31269b0ecab121ea82cf3b77eede96ba8 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 2 Jan 2023 02:25:41 +0100 Subject: [PATCH 13/71] Apply suggestions from code review Co-authored-by: Hypolite Petovan --- src/Core/Config/Model/Config.php | 4 +--- src/Core/Config/Util/ConfigFileManager.php | 2 +- src/Core/Config/ValueObject/Cache.php | 11 ++++------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php index 3de97158f2..7829b75ffd 100644 --- a/src/Core/Config/Model/Config.php +++ b/src/Core/Config/Model/Config.php @@ -32,9 +32,7 @@ use Friendica\Core\Config\ValueObject\Cache; */ class Config implements IManageConfigValues { - /** - * @var Cache - */ + /** @var Cache */ protected $configCache; /** @var ConfigFileManager */ diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index 82de4a99cb..378d7fbd4e 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -401,7 +401,7 @@ class ConfigFileManager private function loadConfigFile(string $filepath): array { if (file_exists($filepath)) { - $config = include($filepath); + $config = include $filepath; if (!is_array($config)) { throw new ConfigFileException('Error loading config file ' . $filepath); diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index 2bac625ad2..a074414bfc 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -212,13 +212,10 @@ class Cache return null; } - switch (true) { - // manage array value - case preg_match("|^a:[0-9]+:{.*}$|s", $value): - return unserialize($value); - - default: - return $value; + if (preg_match("|^a:[0-9]+:{.*}$|s", $value)) { + return unserialize($value); + } else { + return $value; } } From 376e0a93972efb84bd8e5fbfb87d2670105a97aa Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 2 Jan 2023 02:27:05 +0100 Subject: [PATCH 14/71] Fix Admin\Site --- src/Module/Admin/Site.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index cf2f6c5358..f572d3e725 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -211,19 +211,19 @@ class Site extends BaseAdmin DI::config()->set('system', 'touch_icon' , $touch_icon, false); if ($banner == "") { - DI::config()->set('system', 'banner', false); + DI::config()->delete('system', 'banner', false); } else { DI::config()->set('system', 'banner', $banner, false); } if (empty($email_banner)) { - DI::config()->set('system', 'email_banner', false); + DI::config()->delete('system', 'email_banner', false); } else { DI::config()->set('system', 'email_banner', $email_banner, false); } if (empty($additional_info)) { - DI::config()->set('config', 'info', false); + DI::config()->delete('config', 'info', false); } else { DI::config()->set('config', 'info', $additional_info, false); } @@ -232,12 +232,12 @@ class Site extends BaseAdmin Theme::install($theme); if ($theme_mobile == '---') { - DI::config()->set('system', 'mobile-theme', false); + DI::config()->delete('system', 'mobile-theme', false); } else { DI::config()->set('system', 'mobile-theme', $theme_mobile, false); } if ($singleuser == '---') { - DI::config()->set('system', 'singleuser', false); + DI::config()->delete('system', 'singleuser', false); } else { DI::config()->set('system', 'singleuser', $singleuser, false); } From 4d4b4a8858cfb7edb47adda460720e3655f104ca Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 13:16:19 +0100 Subject: [PATCH 15/71] Revert BaseURL --- src/App/BaseURL.php | 43 ++++++++---------------- tests/src/Util/BaseURLTest.php | 61 ++++++++++++---------------------- 2 files changed, 36 insertions(+), 68 deletions(-) diff --git a/src/App/BaseURL.php b/src/App/BaseURL.php index f79bae38f9..20fd54916d 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, false)) { + if ($this->config->set('config', 'hostname', $hostname)) { $this->hostname = $hostname; } else { return false; @@ -185,45 +185,40 @@ class BaseURL } if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) { - if ($this->config->set('system', 'ssl_policy', $sslPolicy, false)) { + if ($this->config->set('system', 'ssl_policy', $sslPolicy)) { $this->sslPolicy = $sslPolicy; } else { $this->hostname = $currHostname; - $this->config->set('config', 'hostname', $this->hostname, false); - $this->config->save(); + $this->config->set('config', 'hostname', $this->hostname); return false; } } if (isset($urlPath) && $urlPath !== $this->urlPath) { - if ($this->config->set('system', 'urlpath', $urlPath, false)) { + if ($this->config->set('system', 'urlpath', $urlPath)) { $this->urlPath = $urlPath; } else { $this->hostname = $currHostname; $this->sslPolicy = $currSSLPolicy; - $this->config->set('config', 'hostname', $this->hostname, false); - $this->config->set('system', 'ssl_policy', $this->sslPolicy, false); - $this->config->save(); + $this->config->set('config', 'hostname', $this->hostname); + $this->config->set('system', 'ssl_policy', $this->sslPolicy); return false; } } $this->determineBaseUrl(); - if (!$this->config->set('system', 'url', $this->url, false)) { + if (!$this->config->set('system', 'url', $this->url)) { $this->hostname = $currHostname; $this->sslPolicy = $currSSLPolicy; $this->urlPath = $currURLPath; $this->determineBaseUrl(); - $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(); + $this->config->set('config', 'hostname', $this->hostname); + $this->config->set('system', 'ssl_policy', $this->sslPolicy); + $this->config->set('system', 'urlpath', $this->urlPath); return false; } - $this->config->save(); - return true; } @@ -300,21 +295,17 @@ 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, false); - $savable = true; + $this->config->set('config', 'hostname', $this->hostname); } } if (!isset($this->urlPath)) { $this->determineURLPath(); - $this->config->set('system', 'urlpath', $this->urlPath, false); - $savable = true; + $this->config->set('system', 'urlpath', $this->urlPath); } if (!isset($this->sslPolicy)) { @@ -323,22 +314,16 @@ class BaseURL } else { $this->sslPolicy = self::DEFAULT_SSL_SCHEME; } - $this->config->set('system', 'ssl_policy', $this->sslPolicy, false); - $savable = true; + $this->config->set('system', 'ssl_policy', $this->sslPolicy); } if (empty($this->url)) { $this->determineBaseUrl(); if (!empty($this->url)) { - $this->config->set('system', 'url', $this->url, false); - $savable = true; + $this->config->set('system', 'url', $this->url); } } - - if ($savable) { - $this->config->save(); - } } /** diff --git a/tests/src/Util/BaseURLTest.php b/tests/src/Util/BaseURLTest.php index 2733096e33..0be83be0a7 100644 --- a/tests/src/Util/BaseURLTest.php +++ b/tests/src/Util/BaseURLTest.php @@ -199,34 +199,24 @@ class BaseURLTest extends MockedTest $configMock->shouldReceive('get')->with('system', 'ssl_policy')->andReturn($input['sslPolicy']); $configMock->shouldReceive('get')->with('system', 'url')->andReturn($input['url']); - $savable = false; - // If we don't have an urlPath as an input, we assert it, we will save it to the DB for the next time if (!isset($input['urlPath']) && isset($assert['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $assert['urlPath'], false)->once(); - $savable = true; + $configMock->shouldReceive('set')->with('system', 'urlpath', $assert['urlPath'])->once(); } // If we don't have the ssl_policy as an input, we assert it, we will save it to the DB for the next time if (!isset($input['sslPolicy']) && isset($assert['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $assert['sslPolicy'], false)->once(); - $savable = true; + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $assert['sslPolicy'])->once(); } // If we don't have the hostname as an input, we assert it, we will save it to the DB for the next time if (empty($input['hostname']) && !empty($assert['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $assert['hostname'], false)->once(); - $savable = true; + $configMock->shouldReceive('set')->with('config', 'hostname', $assert['hostname'])->once(); } // If we don't have an URL at first, but we assert it, we will save it to the DB for the next time if (empty($input['url']) && !empty($assert['url'])) { - $configMock->shouldReceive('set')->with('system', 'url', $assert['url'], false)->once(); - $savable = true; - } - - if ($savable) { - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('system', 'url', $assert['url'])->once(); } $baseUrl = new BaseURL($configMock, $server); @@ -335,20 +325,18 @@ class BaseURLTest extends MockedTest $baseUrl = new BaseURL($configMock, []); if (isset($save['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); } if (isset($save['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); } if (isset($save['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); } - $configMock->shouldReceive('set')->with('system', 'url', $url, false)->andReturn(true)->once(); - - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); $baseUrl->save($save['hostname'], $save['sslPolicy'], $save['urlPath']); @@ -375,20 +363,18 @@ class BaseURLTest extends MockedTest $baseUrl = new BaseURL($configMock, []); if (isset($save['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); } if (isset($save['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); } if (isset($save['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); } - $configMock->shouldReceive('set')->with('system', 'url', $url, false)->andReturn(true)->once(); - - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); $baseUrl->saveByURL($url); @@ -545,25 +531,22 @@ class BaseURLTest extends MockedTest switch ($fail) { case 'hostname': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(false)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(false)->once(); break; case 'sslPolicy': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(false)->once(); - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(false)->once(); break; case 'urlPath': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any(), false)->andReturn(false)->once(); - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(false)->once(); break; case 'url': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'url', \Mockery::any(), false)->andReturn(false)->once(); - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'url', \Mockery::any())->andReturn(false)->once(); break; } From 65d79d4c9350665fedbf434799fed335de64688e Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 14:18:53 +0100 Subject: [PATCH 16/71] Introduce ISetConfigValuesTransactional for transactional config behaviour --- src/Console/Maintenance.php | 10 +- src/Console/Relocate.php | 7 +- .../Config/Capability/IManageConfigValues.php | 29 ++- .../ISetConfigValuesTransactional.php | 84 +++++++++ src/Core/Config/Model/Config.php | 43 +++-- src/Core/Config/Model/TransactionalConfig.php | 89 +++++++++ src/Core/Config/ValueObject/Cache.php | 67 +++++++ src/Core/Update.php | 28 +-- src/Database/DBStructure.php | 7 +- src/Module/Admin/Site.php | 176 +++++++++--------- tests/src/Core/Config/Cache/CacheTest.php | 48 +++++ .../Config/Cache/ConfigFileLoaderTest.php | 34 ++-- .../Core/Config/TransactionalConfigTest.php | 110 +++++++++++ update.php | 6 +- 14 files changed, 588 insertions(+), 150 deletions(-) create mode 100644 src/Core/Config/Capability/ISetConfigValuesTransactional.php create mode 100644 src/Core/Config/Model/TransactionalConfig.php create mode 100644 tests/src/Core/Config/TransactionalConfigTest.php diff --git a/src/Console/Maintenance.php b/src/Console/Maintenance.php index bd3aef7c29..6a11eb2bb5 100644 --- a/src/Console/Maintenance.php +++ b/src/Console/Maintenance.php @@ -100,17 +100,19 @@ HELP; $enabled = intval($this->getArgument(0)); - $this->config->set('system', 'maintenance', $enabled, false); + $transactionConfig = $this->config->transactional(); + + $transactionConfig->set('system', 'maintenance', $enabled); $reason = $this->getArgument(1); if ($enabled && $this->getArgument(1)) { - $this->config->set('system', 'maintenance_reason', $this->getArgument(1), false); + $transactionConfig->set('system', 'maintenance_reason', $this->getArgument(1)); } else { - $this->config->set('system', 'maintenance_reason', '', false); + $transactionConfig->delete('system', 'maintenance_reason'); } - $this->config->save(); + $transactionConfig->save(); if ($enabled) { $mode_str = "maintenance mode"; diff --git a/src/Console/Relocate.php b/src/Console/Relocate.php index 8a76c92070..7a2ef1d071 100644 --- a/src/Console/Relocate.php +++ b/src/Console/Relocate.php @@ -189,9 +189,10 @@ HELP; return 1; } finally { $this->out('Leaving maintenance mode'); - $this->config->set('system', 'maintenance', false, false); - $this->config->set('system', 'maintenance_reason', '', false); - $this->config->save(); + $this->config->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); } // send relocate diff --git a/src/Core/Config/Capability/IManageConfigValues.php b/src/Core/Config/Capability/IManageConfigValues.php index 88fa96314b..42ebea0004 100644 --- a/src/Core/Config/Capability/IManageConfigValues.php +++ b/src/Core/Config/Capability/IManageConfigValues.php @@ -22,6 +22,7 @@ namespace Friendica\Core\Config\Capability; use Friendica\Core\Config\Exception\ConfigPersistenceException; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; /** @@ -57,6 +58,20 @@ interface IManageConfigValues */ public function get(string $cat, string $key, $default_value = null); + /** + * Load all configuration values from a given cache and saves it back in the configuration node store + * @see ConfigFileManager::CONFIG_DATA_FILE + * + * All configuration values of the system are stored in the cache. + * + * @param Cache $cache a new cache + * + * @return void + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + */ + public function load(Cache $cache); + /** * Sets a configuration value for system config * @@ -67,20 +82,21 @@ 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 $autosave = true): bool; + public function set(string $cat, string $key, $value): bool; /** - * Save back the overridden values of the config cache + * Creates a transactional config value store, which is used to set a bunch of values at once * - * @throws ConfigPersistenceException In case the persistence layer throws errors + * It relies on the current instance, so after save(), the values of this config class will get altered at once too. + * + * @return ISetConfigValuesTransactional */ - public function save(); + public function transactional(): ISetConfigValuesTransactional; /** * Deletes the given key from the system configuration. @@ -89,14 +105,13 @@ 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 $autosave = true): bool; + public function delete(string $cat, string $key): bool; /** * Returns the Config Cache diff --git a/src/Core/Config/Capability/ISetConfigValuesTransactional.php b/src/Core/Config/Capability/ISetConfigValuesTransactional.php new file mode 100644 index 0000000000..9c58427a04 --- /dev/null +++ b/src/Core/Config/Capability/ISetConfigValuesTransactional.php @@ -0,0 +1,84 @@ +. + * + */ + +namespace Friendica\Core\Config\Capability; + +use Friendica\Core\Config\Exception\ConfigPersistenceException; + +/** + * Interface for transactional saving of config values + * It buffers every set/delete until "save()" is called + */ +interface ISetConfigValuesTransactional +{ + /** + * Get a particular user's config variable given the category name + * ($cat) and a $key. + * + * Get a particular config value from the given category ($cat) + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to query + * + * @return mixed Stored value or null if it does not exist + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + * + */ + public function get(string $cat, string $key); + + /** + * Sets a configuration value for system config + * + * Stores a config value ($value) in the category ($cat) under the key ($key) + * + * Note: Please do not store booleans - convert to 0/1 integer values! + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to set + * @param mixed $value The value to store + * + * @return static the current instance + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + */ + public function set(string $cat, string $key, $value): self; + + /** + * Deletes the given key from the system configuration. + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to delete + * + * @return static the current instance + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + * + */ + public function delete(string $cat, string $key): self; + + /** + * Saves the node specific config values + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + */ + public function save(): void; +} diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php index 7829b75ffd..24f5fd3b59 100644 --- a/src/Core/Config/Model/Config.php +++ b/src/Core/Config/Model/Config.php @@ -22,6 +22,7 @@ namespace Friendica\Core\Config\Model; use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Config\Capability\ISetConfigValuesTransactional; use Friendica\Core\Config\Exception\ConfigFileException; use Friendica\Core\Config\Exception\ConfigPersistenceException; use Friendica\Core\Config\Util\ConfigFileManager; @@ -61,8 +62,17 @@ class Config implements IManageConfigValues return $this->configCache; } - /** {@inheritDoc} */ - public function save() + /** {@inheritDoc} */ + public function transactional(): ISetConfigValuesTransactional + { + return new TransactionalConfig($this); + } + + /** + * Saves the current Configuration back into the data config. + * @see ConfigFileManager::CONFIG_DATA_FILE + */ + protected function save() { try { $this->configFileManager->saveData($this->configCache); @@ -84,6 +94,13 @@ class Config implements IManageConfigValues $this->configCache = $configCache; } + /** {@inheritDoc} */ + public function load(Cache $cache) + { + $this->configCache = $cache; + $this->save(); + } + /** {@inheritDoc} */ public function get(string $cat, string $key, $default_value = null) { @@ -91,26 +108,24 @@ class Config implements IManageConfigValues } /** {@inheritDoc} */ - public function set(string $cat, string $key, $value, bool $autosave = true): bool + public function set(string $cat, string $key, $value): bool { - $stored = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); - - if ($stored && $autosave) { + if ($this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA)) { $this->save(); + return true; + } else { + return false; } - - return $stored; } /** {@inheritDoc} */ - public function delete(string $cat, string $key, bool $autosave = true): bool + public function delete(string $cat, string $key): bool { - $removed = $this->configCache->delete($cat, $key); - - if ($removed && $autosave) { + if ($this->configCache->delete($cat, $key)) { $this->save(); + return true; + } else { + return false; } - - return $removed; } } diff --git a/src/Core/Config/Model/TransactionalConfig.php b/src/Core/Config/Model/TransactionalConfig.php new file mode 100644 index 0000000000..e9aa71160f --- /dev/null +++ b/src/Core/Config/Model/TransactionalConfig.php @@ -0,0 +1,89 @@ +. + * + */ + +namespace Friendica\Core\Config\Model; + +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Config\Capability\ISetConfigValuesTransactional; +use Friendica\Core\Config\Exception\ConfigPersistenceException; +use Friendica\Core\Config\ValueObject\Cache; + +/** + * config class, which sets values into a temporary buffer until "save()" is called + */ +class TransactionalConfig implements ISetConfigValuesTransactional +{ + /** @var IManageConfigValues */ + protected $config; + /** @var Cache */ + protected $cache; + /** @var Cache */ + protected $delCache; + + public function __construct(IManageConfigValues $config) + { + $this->config = $config; + $this->cache = new Cache(); + $this->delCache = new Cache(); + } + + /** {@inheritDoc} */ + public function get(string $cat, string $key) + { + return !$this->delCache->get($cat, $key) ? + ($this->cache->get($cat, $key) ?? $this->config->get($cat, $key)) : + null; + } + + /** {@inheritDoc} */ + public function set(string $cat, string $key, $value): ISetConfigValuesTransactional + { + $this->cache->set($cat, $key, $value, Cache::SOURCE_DATA); + + return $this; + } + + + /** {@inheritDoc} */ + public function delete(string $cat, string $key): ISetConfigValuesTransactional + { + $this->cache->delete($cat, $key); + $this->delCache->set($cat, $key, 'deleted'); + + return $this; + } + + /** {@inheritDoc} */ + public function save(): void + { + try { + $newCache = $this->config->getCache()->merge($this->cache); + $newCache = $newCache->diff($this->delCache); + $this->config->load($newCache); + + // flush current cache + $this->cache = new Cache(); + $this->delCache = new Cache(); + } catch (\Exception $e) { + throw new ConfigPersistenceException('Cannot save config', $e); + } + } +} diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index a074414bfc..305c00d330 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -279,4 +279,71 @@ class Cache return $return; } + + /** + * Merges a new Cache into the existing one and returns the merged Cache + * + * @param Cache $cache The cache, which should get merged into this Cache + * + * @return Cache The merged Cache + */ + public function merge(Cache $cache): Cache + { + $newConfig = $this->config; + $newSource = $this->source; + + $categories = array_keys($cache->config); + + foreach ($categories as $category) { + if (is_array($cache->config[$category])) { + $keys = array_keys($cache->config[$category]); + + foreach ($keys as $key) { + $newConfig[$category][$key] = $cache->config[$category][$key]; + $newSource[$category][$key] = $cache->source[$category][$key]; + } + } + } + + $newCache = new Cache(); + $newCache->config = $newConfig; + $newCache->source = $newSource; + + return $newCache; + } + + + /** + * Diffs a new Cache into the existing one and returns the diffed Cache + * + * @param Cache $cache The cache, which should get deleted for the current Cache + * + * @return Cache The diffed Cache + */ + public function diff(Cache $cache): Cache + { + $newConfig = $this->config; + $newSource = $this->source; + + $categories = array_keys($cache->config); + + foreach ($categories as $category) { + if (is_array($cache->config[$category])) { + $keys = array_keys($cache->config[$category]); + + foreach ($keys as $key) { + if (!is_null($newConfig[$category][$key] ?? null)) { + unset($newConfig[$category][$key]); + unset($newSource[$category][$key]); + } + } + } + } + + $newCache = new Cache(); + $newCache->config = $newConfig; + $newCache->source = $newSource; + + return $newCache; + } } diff --git a/src/Core/Update.php b/src/Core/Update.php index 9a2ebe1bba..a026457833 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -160,9 +160,10 @@ 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', false, false); - DI::config()->delete('system', 'maintenance_reason', false); - DI::config()->save(); + DI::config()->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); return $r; } else { Logger::notice('Pre update executed.', ['version' => $version]); @@ -182,9 +183,10 @@ 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', false, false); - DI::config()->delete('system', 'maintenance_reason', false); - DI::config()->save(); + DI::config()->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); return $retval; } else { Logger::notice('Database structure update finished.', ['from' => $stored, 'to' => $current]); @@ -200,9 +202,10 @@ 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', false, false); - DI::config()->delete('system', 'maintenance_reason', false); - DI::config()->save(); + DI::config()->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); return $r; } else { DI::config()->set('system', 'build', $version); @@ -213,9 +216,10 @@ class Update DI::config()->set('system', 'build', $current); DI::config()->set('system', 'update', Update::SUCCESS); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', false, false); - DI::config()->delete('system', 'maintenance_reason', false); - DI::config()->save(); + DI::config()->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); Logger::notice('Update success.', ['from' => $stored, 'to' => $current]); if ($sendMail) { diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index e3af408b9e..ed2a5e30e7 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -182,9 +182,10 @@ class DBStructure $status = self::update($verbose, true); if ($enable_maintenance_mode) { - DI::config()->set('system', 'maintenance', false, false); - DI::config()->delete('system', 'maintenance_reason', false); - DI::config()->save(); + DI::config()->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); } return $status; diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index f572d3e725..50a7ee8686 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -48,8 +48,6 @@ class Site extends BaseAdmin self::checkFormSecurityTokenRedirectOnError('/admin/site', 'admin_site'); - $a = DI::app(); - if (!empty($_POST['republish_directory'])) { Worker::add(Worker::PRIORITY_LOW, 'Directory'); return; @@ -146,9 +144,11 @@ class Site extends BaseAdmin $relay_user_tags = !empty($_POST['relay_user_tags']); $active_panel = (!empty($_POST['active_panel']) ? "#" . trim($_POST['active_panel']) : ''); + $transactionConfig = DI::config()->transactional(); + // 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, false); + $transactionConfig->set('system', 'directory', $global_directory); Worker::add(Worker::PRIORITY_LOW, 'Directory'); } @@ -194,133 +194,133 @@ class Site extends BaseAdmin ); } } - 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); + $transactionConfig->set('system', 'ssl_policy' , $ssl_policy); + $transactionConfig->set('system', 'maxloadavg' , $maxloadavg); + $transactionConfig->set('system', 'min_memory' , $min_memory); + $transactionConfig->set('system', 'optimize_tables' , $optimize_tables); + $transactionConfig->set('system', 'contact_discovery' , $contact_discovery); + $transactionConfig->set('system', 'synchronize_directory' , $synchronize_directory); + $transactionConfig->set('system', 'poco_requery_days' , $poco_requery_days); + $transactionConfig->set('system', 'poco_discovery' , $poco_discovery); + $transactionConfig->set('system', 'poco_local_search' , $poco_local_search); + $transactionConfig->set('system', 'nodeinfo' , $nodeinfo); + $transactionConfig->set('config', 'sitename' , $sitename); + $transactionConfig->set('config', 'sender_email' , $sender_email); + $transactionConfig->set('system', 'suppress_tags' , $suppress_tags); + $transactionConfig->set('system', 'shortcut_icon' , $shortcut_icon); + $transactionConfig->set('system', 'touch_icon' , $touch_icon); if ($banner == "") { - DI::config()->delete('system', 'banner', false); + $transactionConfig->delete('system', 'banner'); } else { - DI::config()->set('system', 'banner', $banner, false); + $transactionConfig->set('system', 'banner', $banner); } if (empty($email_banner)) { - DI::config()->delete('system', 'email_banner', false); + $transactionConfig->delete('system', 'email_banner'); } else { - DI::config()->set('system', 'email_banner', $email_banner, false); + $transactionConfig->set('system', 'email_banner', $email_banner); } if (empty($additional_info)) { - DI::config()->delete('config', 'info', false); + $transactionConfig->delete('config', 'info'); } else { - DI::config()->set('config', 'info', $additional_info, false); + $transactionConfig->set('config', 'info', $additional_info); } - DI::config()->set('system', 'language', $language, false); - DI::config()->set('system', 'theme', $theme, false); + $transactionConfig->set('system', 'language', $language); + $transactionConfig->set('system', 'theme', $theme); Theme::install($theme); if ($theme_mobile == '---') { - DI::config()->delete('system', 'mobile-theme', false); + $transactionConfig->delete('system', 'mobile-theme'); } else { - DI::config()->set('system', 'mobile-theme', $theme_mobile, false); + $transactionConfig->set('system', 'mobile-theme', $theme_mobile); } if ($singleuser == '---') { - DI::config()->delete('system', 'singleuser', false); + $transactionConfig->delete('system', 'singleuser'); } else { - DI::config()->set('system', 'singleuser', $singleuser, false); + $transactionConfig->set('system', 'singleuser', $singleuser); } if (preg_match('/\d+(?:\s*[kmg])?/i', $maximagesize)) { - DI::config()->set('system', 'maximagesize', $maximagesize, false); + $transactionConfig->set('system', 'maximagesize', $maximagesize); } 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, false); - DI::config()->set('system', 'jpeg_quality' , $jpegimagequality, false); + $transactionConfig->set('system', 'max_image_length' , $maximagelength); + $transactionConfig->set('system', 'jpeg_quality' , $jpegimagequality); - 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); + $transactionConfig->set('config', 'register_policy' , $register_policy); + $transactionConfig->set('system', 'max_daily_registrations', $daily_registrations); + $transactionConfig->set('system', 'account_abandon_days' , $abandon_days); + $transactionConfig->set('config', 'register_text' , $register_text); + $transactionConfig->set('system', 'allowed_sites' , $allowed_sites); + $transactionConfig->set('system', 'allowed_email' , $allowed_email); + $transactionConfig->set('system', 'forbidden_nicknames' , $forbidden_nicknames); + $transactionConfig->set('system', 'system_actor_name' , $system_actor_name); + $transactionConfig->set('system', 'no_oembed_rich_content' , $no_oembed_rich_content); + $transactionConfig->set('system', 'allowed_oembed' , $allowed_oembed); + $transactionConfig->set('system', 'block_public' , $block_public); + $transactionConfig->set('system', 'publish_all' , $force_publish); + $transactionConfig->set('system', 'newuser_private' , $newuser_private); + $transactionConfig->set('system', 'enotify_no_content' , $enotify_no_content); + $transactionConfig->set('system', 'disable_embedded' , $disable_embedded); + $transactionConfig->set('system', 'allow_users_remote_self', $allow_users_remote_self); + $transactionConfig->set('system', 'explicit_content' , $explicit_content); + $transactionConfig->set('system', 'proxify_content' , $proxify_content); + $transactionConfig->set('system', 'cache_contact_avatar' , $cache_contact_avatar); + $transactionConfig->set('system', 'check_new_version_url' , $check_new_version_url); - 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); + $transactionConfig->set('system', 'block_extended_register', !$enable_multi_reg); + $transactionConfig->set('system', 'no_openid' , !$enable_openid); + $transactionConfig->set('system', 'no_regfullname' , !$enable_regfullname); + $transactionConfig->set('system', 'register_notification' , $register_notification); + $transactionConfig->set('system', 'community_page_style' , $community_page_style); + $transactionConfig->set('system', 'max_author_posts_community_page', $max_author_posts_community_page); + $transactionConfig->set('system', 'verifyssl' , $verifyssl); + $transactionConfig->set('system', 'proxyuser' , $proxyuser); + $transactionConfig->set('system', 'proxy' , $proxy); + $transactionConfig->set('system', 'curl_timeout' , $timeout); + $transactionConfig->set('system', 'imap_disabled' , !$mail_enabled && function_exists('imap_open')); + $transactionConfig->set('system', 'ostatus_disabled' , !$ostatus_enabled); + $transactionConfig->set('system', 'diaspora_enabled' , $diaspora_enabled); - DI::config()->set('config', 'private_addons' , $private_addons, false); + $transactionConfig->set('config', 'private_addons' , $private_addons); - DI::config()->set('system', 'force_ssl' , $force_ssl, false); - DI::config()->set('system', 'hide_help' , !$show_help, false); + $transactionConfig->set('system', 'force_ssl' , $force_ssl); + $transactionConfig->set('system', 'hide_help' , !$show_help); - 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); + $transactionConfig->set('system', 'dbclean' , $dbclean); + $transactionConfig->set('system', 'dbclean-expire-days' , $dbclean_expire_days); + $transactionConfig->set('system', 'dbclean_expire_conversation', $dbclean_expire_conv); if ($dbclean_unclaimed == 0) { $dbclean_unclaimed = $dbclean_expire_days; } - DI::config()->set('system', 'dbclean-expire-unclaimed', $dbclean_unclaimed, false); + $transactionConfig->set('system', 'dbclean-expire-unclaimed', $dbclean_unclaimed); - DI::config()->set('system', 'max_comments', $max_comments, false); - DI::config()->set('system', 'max_display_comments', $max_display_comments, false); + $transactionConfig->set('system', 'max_comments', $max_comments); + $transactionConfig->set('system', 'max_display_comments', $max_display_comments); if ($temppath != '') { $temppath = BasePath::getRealPath($temppath); } - DI::config()->set('system', 'temppath', $temppath, false); + $transactionConfig->set('system', 'temppath', $temppath); - DI::config()->set('system', 'only_tag_search' , $only_tag_search, false); - DI::config()->set('system', 'compute_group_counts', $compute_group_counts, false); + $transactionConfig->set('system', 'only_tag_search' , $only_tag_search); + $transactionConfig->set('system', 'compute_group_counts', $compute_group_counts); - DI::config()->set('system', 'worker_queues' , $worker_queues, false); - DI::config()->set('system', 'worker_fastlane' , $worker_fastlane, false); + $transactionConfig->set('system', 'worker_queues' , $worker_queues); + $transactionConfig->set('system', 'worker_fastlane' , $worker_fastlane); - 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); + $transactionConfig->set('system', 'relay_directly' , $relay_directly); + $transactionConfig->set('system', 'relay_scope' , $relay_scope); + $transactionConfig->set('system', 'relay_server_tags', $relay_server_tags); + $transactionConfig->set('system', 'relay_deny_tags' , $relay_deny_tags); + $transactionConfig->set('system', 'relay_user_tags' , $relay_user_tags); - DI::config()->save(); + $transactionConfig->save(); DI::baseUrl()->redirect('admin/site' . $active_panel); } @@ -334,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')), false); - DI::config()->delete('system', 'directory_submit_url', false); + DI::config()->set('system', 'directory', dirname(DI::config()->get('system', 'directory_submit_url'))); + DI::config()->delete('system', 'directory_submit_url'); } /* Installed themes */ diff --git a/tests/src/Core/Config/Cache/CacheTest.php b/tests/src/Core/Config/Cache/CacheTest.php index 8b2c24da3e..9d72774c40 100644 --- a/tests/src/Core/Config/Cache/CacheTest.php +++ b/tests/src/Core/Config/Cache/CacheTest.php @@ -358,4 +358,52 @@ class CacheTest extends MockedTest $this->assertEquals(['system' => ['test_2' => 'with_data']], $configCache->getDataBySource(Cache::SOURCE_DATA)); $this->assertEquals($data, $configCache->getDataBySource(Cache::SOURCE_FILE)); } + + /** + * @dataProvider dataTests + */ + public function testMerge($data) + { + $configCache = new Cache(); + $configCache->load($data, Cache::SOURCE_FILE); + + $configCache->set('system', 'test_2','with_data', Cache::SOURCE_DATA); + $configCache->set('config', 'test_override','with_another_data', Cache::SOURCE_DATA); + + $newCache = new Cache(); + $newCache->set('config', 'test_override','override it again', Cache::SOURCE_DATA); + $newCache->set('system', 'test_3','new value', Cache::SOURCE_DATA); + + $mergedCache = $configCache->merge($newCache); + + self::assertEquals('with_data', $mergedCache->get('system', 'test_2')); + self::assertEquals('override it again', $mergedCache->get('config', 'test_override')); + self::assertEquals('new value', $mergedCache->get('system', 'test_3')); + } + + /** + * @dataProvider dataTests + */ + public function testDiff($data) + { + $configCache = new Cache(); + $configCache->load($data, Cache::SOURCE_FILE); + + $configCache->set('system', 'test_2','with_data', Cache::SOURCE_DATA); + $configCache->set('config', 'test_override','with_another_data', Cache::SOURCE_DATA); + + $newCache = new Cache(); + $newCache->set('config', 'test_override','override it again', Cache::SOURCE_DATA); + $newCache->set('system', 'test_3','new value', Cache::SOURCE_DATA); + + $mergedCache = $configCache->diff($newCache); + + print_r($mergedCache); + + self::assertEquals('with_data', $mergedCache->get('system', 'test_2')); + // existing entry was dropped + self::assertNull($mergedCache->get('config', 'test_override')); + // the newCache entry wasn't set, because we Diff + self::assertNull($mergedCache->get('system', 'test_3')); + } } diff --git a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php b/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php index e8443611f2..aed55f429e 100644 --- a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php +++ b/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php @@ -21,8 +21,8 @@ namespace Friendica\Test\src\Core\Config\Cache; -use Friendica\Core\Config\Cache; use Friendica\Core\Config\Factory\Config; +use Friendica\Core\Config\ValueObject\Cache; use Friendica\Test\MockedTest; use Friendica\Test\Util\VFSTrait; use Friendica\Core\Config\Util\ConfigFileManager; @@ -51,7 +51,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -77,7 +77,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); } @@ -106,7 +106,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -143,7 +143,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -179,7 +179,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -270,7 +270,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -304,7 +304,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -338,7 +338,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -354,7 +354,7 @@ class ConfigFileLoaderTest extends MockedTest $this->delConfigFile('local.config.php'); $configFileLoader = (new Config())->createConfigFileLoader($this->root->url(), ['FRIENDICA_CONFIG_DIR' => '/a/wrong/dir/']); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -380,7 +380,7 @@ class ConfigFileLoaderTest extends MockedTest ->setContent(file_get_contents($fileDir . 'B.config.php')); $configFileLoader = (new Config())->createConfigFileLoader($this->root->url(), ['FRIENDICA_CONFIG_DIR' => $this->root->getChild('config2')->url()]); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -403,18 +403,18 @@ class ConfigFileLoaderTest extends MockedTest ->setContent(file_get_contents($fileDir . 'B.config.php')); $configFileLoader = (new Config())->createConfigFileLoader($this->root->url(), ['FRIENDICA_CONFIG_DIR' => $this->root->getChild('config2')->url()]); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); // overwrite some data and save it back to the config file - $configCache->set('system', 'test', 'it', \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); - $configCache->set('config', 'test', 'it', \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); - $configCache->set('system', 'test_2', 2, \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); + $configCache->set('system', 'test', 'it', Cache::SOURCE_DATA); + $configCache->set('config', 'test', 'it', Cache::SOURCE_DATA); + $configCache->set('system', 'test_2', 2, Cache::SOURCE_DATA); $configFileLoader->saveData($configCache); // Reload the configCache with the new values - $configCache2 = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache2 = new Cache(); $configFileLoader->setupCache($configCache2); self::assertEquals($configCache, $configCache2); @@ -425,6 +425,6 @@ class ConfigFileLoaderTest extends MockedTest ], 'config' => [ 'test' => 'it' - ]], $configCache2->getDataBySource(\Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA)); + ]], $configCache2->getDataBySource(Cache::SOURCE_DATA)); } } diff --git a/tests/src/Core/Config/TransactionalConfigTest.php b/tests/src/Core/Config/TransactionalConfigTest.php new file mode 100644 index 0000000000..b42fee97c0 --- /dev/null +++ b/tests/src/Core/Config/TransactionalConfigTest.php @@ -0,0 +1,110 @@ +setUpVfsDir(); + + $this->configFileManager = new ConfigFileManager($this->root->url(), $this->root->url() . '/config/', $this->root->url() . '/static/'); + } + + public function dataTests(): array + { + return [ + 'default' => [ + 'data' => include dirname(__FILE__, 4) . '/datasets/B.node.config.php', + ] + ]; + } + + public function testInstance() + { + $config = new Config($this->configFileManager, new Cache()); + $transactionalConfig = new TransactionalConfig($config); + + self::assertInstanceOf(ISetConfigValuesTransactional::class, $transactionalConfig); + self::assertInstanceOf(TransactionalConfig::class, $transactionalConfig); + } + + public function testTransactionalConfig() + { + $config = new Config($this->configFileManager, new Cache()); + $config->set('config', 'key1', 'value1'); + $config->set('system', 'key2', 'value2'); + $config->set('system', 'keyDel', 'valueDel'); + $config->set('delete', 'keyDel', 'catDel'); + + $transactionalConfig = new TransactionalConfig($config); + self::assertEquals('value1', $transactionalConfig->get('config', 'key1')); + self::assertEquals('value2', $transactionalConfig->get('system', 'key2')); + self::assertEquals('valueDel', $transactionalConfig->get('system', 'keyDel')); + self::assertEquals('catDel', $transactionalConfig->get('delete', 'keyDel')); + // the config file knows it as well immediately + $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; + self::assertEquals('value1', $tempData['config']['key1'] ?? null); + self::assertEquals('value2', $tempData['system']['key2'] ?? null); + + // new key-value + $transactionalConfig->set('transaction', 'key3', 'value3'); + // overwrite key-value + $transactionalConfig->set('config', 'key1', 'changedValue1'); + // delete key-value + $transactionalConfig->delete('system', 'keyDel'); + // delete last key of category - so the category is gone + $transactionalConfig->delete('delete', 'keyDel'); + + // The main config still doesn't know about the change + self::assertNull($config->get('transaction', 'key3')); + self::assertEquals('value1', $config->get('config', 'key1')); + self::assertEquals('valueDel', $config->get('system', 'keyDel')); + self::assertEquals('catDel', $config->get('delete', 'keyDel')); + // but the transaction config of course knows it + self::assertEquals('value3', $transactionalConfig->get('transaction', 'key3')); + self::assertEquals('changedValue1', $transactionalConfig->get('config', 'key1')); + self::assertNull($transactionalConfig->get('system', 'keyDel')); + self::assertNull($transactionalConfig->get('delete', 'keyDel')); + // The config file still doesn't know it either + $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; + self::assertEquals('value1', $tempData['config']['key1'] ?? null); + self::assertEquals('value2', $tempData['system']['key2'] ?? null); + self::assertEquals('catDel', $tempData['delete']['keyDel'] ?? null); + self::assertNull($tempData['transaction']['key3'] ?? null); + + // save it back! + $transactionalConfig->save(); + + // Now every config and file knows the change + self::assertEquals('changedValue1', $config->get('config', 'key1')); + self::assertEquals('value3', $config->get('transaction', 'key3')); + self::assertNull($config->get('system', 'keyDel')); + self::assertNull($config->get('delete', 'keyDel')); + self::assertEquals('value3', $transactionalConfig->get('transaction', 'key3')); + self::assertEquals('changedValue1', $transactionalConfig->get('config', 'key1')); + self::assertNull($transactionalConfig->get('system', 'keyDel')); + $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; + self::assertEquals('changedValue1', $tempData['config']['key1'] ?? null); + self::assertEquals('value2', $tempData['system']['key2'] ?? null); + self::assertEquals('value3', $tempData['transaction']['key3'] ?? null); + self::assertNull($tempData['system']['keyDel'] ?? null); + self::assertNull($tempData['delete']['keyDel'] ?? null); + // the whole category should be gone + self::assertNull($tempData['delete'] ?? null); + } +} diff --git a/update.php b/update.php index ad33dde012..7ad6e432fe 100644 --- a/update.php +++ b/update.php @@ -1184,11 +1184,13 @@ function update_1508() { $config = DBA::selectToArray('config'); + $newConfig = DI::config()->transactional(); + foreach ($config as $entry) { - DI::config()->set($entry['cat'], $entry['k'], $entry['v'], false); + $newConfig->set($entry['cat'], $entry['k'], $entry['v']); } - DI::config()->save(); + $newConfig->save(); DBA::e("DELETE FROM `config`"); } From dd88d193b9e01f4518a21d9cd1d25b7575677c39 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 15:36:36 +0100 Subject: [PATCH 17/71] Escape single quotes and backslashes --- src/Core/Config/Util/ConfigFileTransformer.php | 2 +- tests/datasets/config/B.node.config.php | 1 + ...figFileLoaderTest.php => ConfigFileManagerTest.php} | 10 ++++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) rename tests/src/Core/Config/Cache/{ConfigFileLoaderTest.php => ConfigFileManagerTest.php} (98%) diff --git a/src/Core/Config/Util/ConfigFileTransformer.php b/src/Core/Config/Util/ConfigFileTransformer.php index 9b80991af6..282714df2a 100644 --- a/src/Core/Config/Util/ConfigFileTransformer.php +++ b/src/Core/Config/Util/ConfigFileTransformer.php @@ -75,7 +75,7 @@ class ConfigFileTransformer } elseif (is_numeric($value)) { $string .= $value . ","; } else { - $string .= sprintf('\'%s\',', $value); + $string .= sprintf('\'%s\',', addcslashes($value, '\'\\')); } $string .= PHP_EOL; diff --git a/tests/datasets/config/B.node.config.php b/tests/datasets/config/B.node.config.php index 94b2e3f12e..499e092a45 100644 --- a/tests/datasets/config/B.node.config.php +++ b/tests/datasets/config/B.node.config.php @@ -34,5 +34,6 @@ return [ 'theme' => 'frio', 'int' => 23, 'float' => 2.5, + 'with special chars' => 'I can\'t follow this "$&§%"$%§$%&\'[),', ], ]; diff --git a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php b/tests/src/Core/Config/Cache/ConfigFileManagerTest.php similarity index 98% rename from tests/src/Core/Config/Cache/ConfigFileLoaderTest.php rename to tests/src/Core/Config/Cache/ConfigFileManagerTest.php index aed55f429e..99049426bc 100644 --- a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php +++ b/tests/src/Core/Config/Cache/ConfigFileManagerTest.php @@ -28,7 +28,7 @@ use Friendica\Test\Util\VFSTrait; use Friendica\Core\Config\Util\ConfigFileManager; use org\bovigo\vfs\vfsStream; -class ConfigFileLoaderTest extends MockedTest +class ConfigFileManagerTest extends MockedTest { use VFSTrait; @@ -407,10 +407,13 @@ class ConfigFileLoaderTest extends MockedTest $configFileLoader->setupCache($configCache); + $specialChars = '!"§$%&/()(/&%$\'>set('system', 'test', 'it', Cache::SOURCE_DATA); $configCache->set('config', 'test', 'it', Cache::SOURCE_DATA); $configCache->set('system', 'test_2', 2, Cache::SOURCE_DATA); + $configCache->set('special_chars', 'special', $specialChars, Cache::SOURCE_DATA); $configFileLoader->saveData($configCache); // Reload the configCache with the new values @@ -424,7 +427,10 @@ class ConfigFileLoaderTest extends MockedTest 'test_2' => 2 ], 'config' => [ - 'test' => 'it' + 'test' => 'it', + ], + 'special_chars' => [ + 'special' => $specialChars, ]], $configCache2->getDataBySource(Cache::SOURCE_DATA)); } } From 072464119a353f4f0cbfb03b5e6253df0e2e4dde Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 15:40:57 +0100 Subject: [PATCH 18/71] Make PHP-CS happy --- tests/src/Core/Config/TransactionalConfigTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/src/Core/Config/TransactionalConfigTest.php b/tests/src/Core/Config/TransactionalConfigTest.php index b42fee97c0..e2fdc633cb 100644 --- a/tests/src/Core/Config/TransactionalConfigTest.php +++ b/tests/src/Core/Config/TransactionalConfigTest.php @@ -1,6 +1,7 @@ configFileManager, new Cache()); + $config = new Config($this->configFileManager, new Cache()); $transactionalConfig = new TransactionalConfig($config); self::assertInstanceOf(ISetConfigValuesTransactional::class, $transactionalConfig); From b439df892a3c57617365cd02b1d239fdb63cc550 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 17:24:05 +0100 Subject: [PATCH 19/71] Apply suggestions --- src/Console/Maintenance.php | 4 +- src/Console/Relocate.php | 11 ++-- .../Config/Capability/IManageConfigValues.php | 4 +- ...hp => ISetConfigValuesTransactionally.php} | 6 +-- src/Core/Config/Model/Config.php | 6 +-- ...tionalConfig.php => ConfigTransaction.php} | 12 ++--- src/Core/Config/ValueObject/Cache.php | 6 +-- src/Core/Update.php | 16 +++--- src/Database/DBStructure.php | 4 +- src/Module/Admin/Site.php | 4 +- tests/src/Core/Config/Cache/CacheTest.php | 4 ++ ...nfigTest.php => ConfigTransactionTest.php} | 50 +++++++++---------- update.php | 6 +-- view/theme/frio/style.php | 2 +- 14 files changed, 69 insertions(+), 66 deletions(-) rename src/Core/Config/Capability/{ISetConfigValuesTransactional.php => ISetConfigValuesTransactionally.php} (95%) rename src/Core/Config/Model/{TransactionalConfig.php => ConfigTransaction.php} (90%) rename tests/src/Core/Config/{TransactionalConfigTest.php => ConfigTransactionTest.php} (66%) diff --git a/src/Console/Maintenance.php b/src/Console/Maintenance.php index 6a11eb2bb5..076b89db82 100644 --- a/src/Console/Maintenance.php +++ b/src/Console/Maintenance.php @@ -100,7 +100,7 @@ HELP; $enabled = intval($this->getArgument(0)); - $transactionConfig = $this->config->transactional(); + $transactionConfig = $this->config->beginTransaction(); $transactionConfig->set('system', 'maintenance', $enabled); @@ -112,7 +112,7 @@ HELP; $transactionConfig->delete('system', 'maintenance_reason'); } - $transactionConfig->save(); + $transactionConfig->commit(); if ($enabled) { $mode_str = "maintenance mode"; diff --git a/src/Console/Relocate.php b/src/Console/Relocate.php index 7a2ef1d071..c63434cbb7 100644 --- a/src/Console/Relocate.php +++ b/src/Console/Relocate.php @@ -101,9 +101,10 @@ HELP; $old_host = str_replace('http://', '@', Strings::normaliseLink($old_url)); $this->out('Entering maintenance mode'); - $this->config->set('system', 'maintenance', true, false); - $this->config->set('system', 'maintenance_reason', 'Relocating node to ' . $new_url, false); - + $this->config->beginTransaction() + ->set('system', 'maintenance', true) + ->set('system', 'maintenance_reason', 'Relocating node to ' . $new_url) + ->commit(); try { if (!$this->database->transaction()) { throw new \Exception('Unable to start a transaction, please retry later.'); @@ -189,10 +190,10 @@ HELP; return 1; } finally { $this->out('Leaving maintenance mode'); - $this->config->transactional() + $this->config->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); } // send relocate diff --git a/src/Core/Config/Capability/IManageConfigValues.php b/src/Core/Config/Capability/IManageConfigValues.php index 42ebea0004..715887ddf3 100644 --- a/src/Core/Config/Capability/IManageConfigValues.php +++ b/src/Core/Config/Capability/IManageConfigValues.php @@ -94,9 +94,9 @@ interface IManageConfigValues * * It relies on the current instance, so after save(), the values of this config class will get altered at once too. * - * @return ISetConfigValuesTransactional + * @return ISetConfigValuesTransactionally */ - public function transactional(): ISetConfigValuesTransactional; + public function beginTransaction(): ISetConfigValuesTransactionally; /** * Deletes the given key from the system configuration. diff --git a/src/Core/Config/Capability/ISetConfigValuesTransactional.php b/src/Core/Config/Capability/ISetConfigValuesTransactionally.php similarity index 95% rename from src/Core/Config/Capability/ISetConfigValuesTransactional.php rename to src/Core/Config/Capability/ISetConfigValuesTransactionally.php index 9c58427a04..ae193f2ce2 100644 --- a/src/Core/Config/Capability/ISetConfigValuesTransactional.php +++ b/src/Core/Config/Capability/ISetConfigValuesTransactionally.php @@ -27,7 +27,7 @@ use Friendica\Core\Config\Exception\ConfigPersistenceException; * Interface for transactional saving of config values * It buffers every set/delete until "save()" is called */ -interface ISetConfigValuesTransactional +interface ISetConfigValuesTransactionally { /** * Get a particular user's config variable given the category name @@ -76,9 +76,9 @@ interface ISetConfigValuesTransactional public function delete(string $cat, string $key): self; /** - * Saves the node specific config values + * Commits the changes of the current transaction * * @throws ConfigPersistenceException In case the persistence layer throws errors */ - public function save(): void; + public function commit(): void; } diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php index 24f5fd3b59..29ea6b12d3 100644 --- a/src/Core/Config/Model/Config.php +++ b/src/Core/Config/Model/Config.php @@ -22,7 +22,7 @@ namespace Friendica\Core\Config\Model; use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Config\Capability\ISetConfigValuesTransactional; +use Friendica\Core\Config\Capability\ISetConfigValuesTransactionally; use Friendica\Core\Config\Exception\ConfigFileException; use Friendica\Core\Config\Exception\ConfigPersistenceException; use Friendica\Core\Config\Util\ConfigFileManager; @@ -63,9 +63,9 @@ class Config implements IManageConfigValues } /** {@inheritDoc} */ - public function transactional(): ISetConfigValuesTransactional + public function beginTransaction(): ISetConfigValuesTransactionally { - return new TransactionalConfig($this); + return new ConfigTransaction($this); } /** diff --git a/src/Core/Config/Model/TransactionalConfig.php b/src/Core/Config/Model/ConfigTransaction.php similarity index 90% rename from src/Core/Config/Model/TransactionalConfig.php rename to src/Core/Config/Model/ConfigTransaction.php index e9aa71160f..7ec5784ad2 100644 --- a/src/Core/Config/Model/TransactionalConfig.php +++ b/src/Core/Config/Model/ConfigTransaction.php @@ -22,14 +22,14 @@ namespace Friendica\Core\Config\Model; use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Config\Capability\ISetConfigValuesTransactional; +use Friendica\Core\Config\Capability\ISetConfigValuesTransactionally; use Friendica\Core\Config\Exception\ConfigPersistenceException; use Friendica\Core\Config\ValueObject\Cache; /** - * config class, which sets values into a temporary buffer until "save()" is called + * Transaction class for configurations, which sets values into a temporary buffer until "save()" is called */ -class TransactionalConfig implements ISetConfigValuesTransactional +class ConfigTransaction implements ISetConfigValuesTransactionally { /** @var IManageConfigValues */ protected $config; @@ -54,7 +54,7 @@ class TransactionalConfig implements ISetConfigValuesTransactional } /** {@inheritDoc} */ - public function set(string $cat, string $key, $value): ISetConfigValuesTransactional + public function set(string $cat, string $key, $value): ISetConfigValuesTransactionally { $this->cache->set($cat, $key, $value, Cache::SOURCE_DATA); @@ -63,7 +63,7 @@ class TransactionalConfig implements ISetConfigValuesTransactional /** {@inheritDoc} */ - public function delete(string $cat, string $key): ISetConfigValuesTransactional + public function delete(string $cat, string $key): ISetConfigValuesTransactionally { $this->cache->delete($cat, $key); $this->delCache->set($cat, $key, 'deleted'); @@ -72,7 +72,7 @@ class TransactionalConfig implements ISetConfigValuesTransactional } /** {@inheritDoc} */ - public function save(): void + public function commit(): void { try { $newCache = $this->config->getCache()->merge($this->cache); diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index 305c00d330..b5af3280c0 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -332,10 +332,8 @@ class Cache $keys = array_keys($cache->config[$category]); foreach ($keys as $key) { - if (!is_null($newConfig[$category][$key] ?? null)) { - unset($newConfig[$category][$key]); - unset($newSource[$category][$key]); - } + unset($newConfig[$category][$key]); + unset($newSource[$category][$key]); } } } diff --git a/src/Core/Update.php b/src/Core/Update.php index a026457833..e5ee587dc1 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -160,10 +160,10 @@ class Update Logger::warning('Pre update failed', ['version' => $version]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->transactional() + DI::config()->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); return $r; } else { Logger::notice('Pre update executed.', ['version' => $version]); @@ -183,10 +183,10 @@ 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()->transactional() + DI::config()->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); return $retval; } else { Logger::notice('Database structure update finished.', ['from' => $stored, 'to' => $current]); @@ -202,10 +202,10 @@ class Update Logger::warning('Post update failed', ['version' => $version]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->transactional() + DI::config()->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); return $r; } else { DI::config()->set('system', 'build', $version); @@ -216,10 +216,10 @@ class Update DI::config()->set('system', 'build', $current); DI::config()->set('system', 'update', Update::SUCCESS); DI::lock()->release('dbupdate'); - DI::config()->transactional() + DI::config()->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); Logger::notice('Update success.', ['from' => $stored, 'to' => $current]); if ($sendMail) { diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index ed2a5e30e7..7b284bf6d5 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -182,10 +182,10 @@ class DBStructure $status = self::update($verbose, true); if ($enable_maintenance_mode) { - DI::config()->transactional() + DI::config()->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); } return $status; diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index 50a7ee8686..39dc9f26da 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -144,7 +144,7 @@ class Site extends BaseAdmin $relay_user_tags = !empty($_POST['relay_user_tags']); $active_panel = (!empty($_POST['active_panel']) ? "#" . trim($_POST['active_panel']) : ''); - $transactionConfig = DI::config()->transactional(); + $transactionConfig = DI::config()->beginTransaction(); // Has the directory url changed? If yes, then resubmit the existing profiles there if ($global_directory != DI::config()->get('system', 'directory') && ($global_directory != '')) { @@ -320,7 +320,7 @@ class Site extends BaseAdmin $transactionConfig->set('system', 'relay_deny_tags' , $relay_deny_tags); $transactionConfig->set('system', 'relay_user_tags' , $relay_user_tags); - $transactionConfig->save(); + $transactionConfig->commit(); DI::baseUrl()->redirect('admin/site' . $active_panel); } diff --git a/tests/src/Core/Config/Cache/CacheTest.php b/tests/src/Core/Config/Cache/CacheTest.php index 9d72774c40..2db6196b7b 100644 --- a/tests/src/Core/Config/Cache/CacheTest.php +++ b/tests/src/Core/Config/Cache/CacheTest.php @@ -369,16 +369,20 @@ class CacheTest extends MockedTest $configCache->set('system', 'test_2','with_data', Cache::SOURCE_DATA); $configCache->set('config', 'test_override','with_another_data', Cache::SOURCE_DATA); + $configCache->set('old_category', 'test_45','given category', Cache::SOURCE_DATA); $newCache = new Cache(); $newCache->set('config', 'test_override','override it again', Cache::SOURCE_DATA); $newCache->set('system', 'test_3','new value', Cache::SOURCE_DATA); + $newCache->set('new_category', 'test_23','added category', Cache::SOURCE_DATA); $mergedCache = $configCache->merge($newCache); self::assertEquals('with_data', $mergedCache->get('system', 'test_2')); self::assertEquals('override it again', $mergedCache->get('config', 'test_override')); self::assertEquals('new value', $mergedCache->get('system', 'test_3')); + self::assertEquals('given category', $mergedCache->get('old_category', 'test_45')); + self::assertEquals('added category', $mergedCache->get('new_category', 'test_23')); } /** diff --git a/tests/src/Core/Config/TransactionalConfigTest.php b/tests/src/Core/Config/ConfigTransactionTest.php similarity index 66% rename from tests/src/Core/Config/TransactionalConfigTest.php rename to tests/src/Core/Config/ConfigTransactionTest.php index e2fdc633cb..2eec9b68f3 100644 --- a/tests/src/Core/Config/TransactionalConfigTest.php +++ b/tests/src/Core/Config/ConfigTransactionTest.php @@ -2,15 +2,15 @@ namespace Friendica\Test\src\Core\Config; -use Friendica\Core\Config\Capability\ISetConfigValuesTransactional; +use Friendica\Core\Config\Capability\ISetConfigValuesTransactionally; use Friendica\Core\Config\Model\Config; -use Friendica\Core\Config\Model\TransactionalConfig; +use Friendica\Core\Config\Model\ConfigTransaction; use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Test\MockedTest; use Friendica\Test\Util\VFSTrait; -class TransactionalConfigTest extends MockedTest +class ConfigTransactionTest extends MockedTest { use VFSTrait; @@ -37,14 +37,14 @@ class TransactionalConfigTest extends MockedTest public function testInstance() { - $config = new Config($this->configFileManager, new Cache()); - $transactionalConfig = new TransactionalConfig($config); + $config = new Config($this->configFileManager, new Cache()); + $configTransaction = new ConfigTransaction($config); - self::assertInstanceOf(ISetConfigValuesTransactional::class, $transactionalConfig); - self::assertInstanceOf(TransactionalConfig::class, $transactionalConfig); + self::assertInstanceOf(ISetConfigValuesTransactionally::class, $configTransaction); + self::assertInstanceOf(ConfigTransaction::class, $configTransaction); } - public function testTransactionalConfig() + public function testConfigTransaction() { $config = new Config($this->configFileManager, new Cache()); $config->set('config', 'key1', 'value1'); @@ -52,24 +52,24 @@ class TransactionalConfigTest extends MockedTest $config->set('system', 'keyDel', 'valueDel'); $config->set('delete', 'keyDel', 'catDel'); - $transactionalConfig = new TransactionalConfig($config); - self::assertEquals('value1', $transactionalConfig->get('config', 'key1')); - self::assertEquals('value2', $transactionalConfig->get('system', 'key2')); - self::assertEquals('valueDel', $transactionalConfig->get('system', 'keyDel')); - self::assertEquals('catDel', $transactionalConfig->get('delete', 'keyDel')); + $configTransaction = new ConfigTransaction($config); + self::assertEquals('value1', $configTransaction->get('config', 'key1')); + self::assertEquals('value2', $configTransaction->get('system', 'key2')); + self::assertEquals('valueDel', $configTransaction->get('system', 'keyDel')); + self::assertEquals('catDel', $configTransaction->get('delete', 'keyDel')); // the config file knows it as well immediately $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; self::assertEquals('value1', $tempData['config']['key1'] ?? null); self::assertEquals('value2', $tempData['system']['key2'] ?? null); // new key-value - $transactionalConfig->set('transaction', 'key3', 'value3'); + $configTransaction->set('transaction', 'key3', 'value3'); // overwrite key-value - $transactionalConfig->set('config', 'key1', 'changedValue1'); + $configTransaction->set('config', 'key1', 'changedValue1'); // delete key-value - $transactionalConfig->delete('system', 'keyDel'); + $configTransaction->delete('system', 'keyDel'); // delete last key of category - so the category is gone - $transactionalConfig->delete('delete', 'keyDel'); + $configTransaction->delete('delete', 'keyDel'); // The main config still doesn't know about the change self::assertNull($config->get('transaction', 'key3')); @@ -77,10 +77,10 @@ class TransactionalConfigTest extends MockedTest self::assertEquals('valueDel', $config->get('system', 'keyDel')); self::assertEquals('catDel', $config->get('delete', 'keyDel')); // but the transaction config of course knows it - self::assertEquals('value3', $transactionalConfig->get('transaction', 'key3')); - self::assertEquals('changedValue1', $transactionalConfig->get('config', 'key1')); - self::assertNull($transactionalConfig->get('system', 'keyDel')); - self::assertNull($transactionalConfig->get('delete', 'keyDel')); + self::assertEquals('value3', $configTransaction->get('transaction', 'key3')); + self::assertEquals('changedValue1', $configTransaction->get('config', 'key1')); + self::assertNull($configTransaction->get('system', 'keyDel')); + self::assertNull($configTransaction->get('delete', 'keyDel')); // The config file still doesn't know it either $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; self::assertEquals('value1', $tempData['config']['key1'] ?? null); @@ -89,16 +89,16 @@ class TransactionalConfigTest extends MockedTest self::assertNull($tempData['transaction']['key3'] ?? null); // save it back! - $transactionalConfig->save(); + $configTransaction->commit(); // Now every config and file knows the change self::assertEquals('changedValue1', $config->get('config', 'key1')); self::assertEquals('value3', $config->get('transaction', 'key3')); self::assertNull($config->get('system', 'keyDel')); self::assertNull($config->get('delete', 'keyDel')); - self::assertEquals('value3', $transactionalConfig->get('transaction', 'key3')); - self::assertEquals('changedValue1', $transactionalConfig->get('config', 'key1')); - self::assertNull($transactionalConfig->get('system', 'keyDel')); + self::assertEquals('value3', $configTransaction->get('transaction', 'key3')); + self::assertEquals('changedValue1', $configTransaction->get('config', 'key1')); + self::assertNull($configTransaction->get('system', 'keyDel')); $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; self::assertEquals('changedValue1', $tempData['config']['key1'] ?? null); self::assertEquals('value2', $tempData['system']['key2'] ?? null); diff --git a/update.php b/update.php index 7ad6e432fe..1dbf78e06f 100644 --- a/update.php +++ b/update.php @@ -1184,13 +1184,13 @@ function update_1508() { $config = DBA::selectToArray('config'); - $newConfig = DI::config()->transactional(); + $newConfig = DI::config()->beginTransaction(); foreach ($config as $entry) { $newConfig->set($entry['cat'], $entry['k'], $entry['v']); } - $newConfig->save(); + $newConfig->commit(); - DBA::e("DELETE FROM `config`"); + DBA::e("TRUNCATE TABLE `config`"); } diff --git a/view/theme/frio/style.php b/view/theme/frio/style.php index 8feefdc930..479793b596 100644 --- a/view/theme/frio/style.php +++ b/view/theme/frio/style.php @@ -49,7 +49,7 @@ $login_bg_color = ''; $modified = time(); if (DI::mode()->has(\Friendica\App\Mode::MAINTENANCEDISABLED)) { - DI::config()->reload('frio'); + DI::config()->reload(); // Default to hard-coded values for empty settings $scheme = DI::config()->get('frio', 'scheme', DI::config()->get('frio', 'schema')); From a46cd2fb36f57547586d6eda77128d1bdb44cf0b Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 17:26:48 +0100 Subject: [PATCH 20/71] Remove get() from config transaction interface --- .../ISetConfigValuesTransactionally.php | 16 ---------------- src/Core/Config/Model/ConfigTransaction.php | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/Core/Config/Capability/ISetConfigValuesTransactionally.php b/src/Core/Config/Capability/ISetConfigValuesTransactionally.php index ae193f2ce2..501e24f738 100644 --- a/src/Core/Config/Capability/ISetConfigValuesTransactionally.php +++ b/src/Core/Config/Capability/ISetConfigValuesTransactionally.php @@ -29,22 +29,6 @@ use Friendica\Core\Config\Exception\ConfigPersistenceException; */ interface ISetConfigValuesTransactionally { - /** - * Get a particular user's config variable given the category name - * ($cat) and a $key. - * - * Get a particular config value from the given category ($cat) - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to query - * - * @return mixed Stored value or null if it does not exist - * - * @throws ConfigPersistenceException In case the persistence layer throws errors - * - */ - public function get(string $cat, string $key); - /** * Sets a configuration value for system config * diff --git a/src/Core/Config/Model/ConfigTransaction.php b/src/Core/Config/Model/ConfigTransaction.php index 7ec5784ad2..d8c7d7d43e 100644 --- a/src/Core/Config/Model/ConfigTransaction.php +++ b/src/Core/Config/Model/ConfigTransaction.php @@ -45,8 +45,20 @@ class ConfigTransaction implements ISetConfigValuesTransactionally $this->delCache = new Cache(); } - /** {@inheritDoc} */ - public function get(string $cat, string $key) + /** + * Get a particular user's config variable given the category name + * ($cat) and a $key from the current transaction. + * + * Isn't part of the interface because of it's rare use case + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to query + * + * @return mixed Stored value or null if it does not exist + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + * + */ public function get(string $cat, string $key) { return !$this->delCache->get($cat, $key) ? ($this->cache->get($cat, $key) ?? $this->config->get($cat, $key)) : From beff759c82a5afc5d26289daf3ba0d84feefb2aa Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 17:39:32 +0100 Subject: [PATCH 21/71] Update src/Core/Config/Model/ConfigTransaction.php Co-authored-by: Hypolite Petovan --- src/Core/Config/Model/ConfigTransaction.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Core/Config/Model/ConfigTransaction.php b/src/Core/Config/Model/ConfigTransaction.php index d8c7d7d43e..296a469c06 100644 --- a/src/Core/Config/Model/ConfigTransaction.php +++ b/src/Core/Config/Model/ConfigTransaction.php @@ -58,7 +58,8 @@ class ConfigTransaction implements ISetConfigValuesTransactionally * * @throws ConfigPersistenceException In case the persistence layer throws errors * - */ public function get(string $cat, string $key) + */ + public function get(string $cat, string $key) { return !$this->delCache->get($cat, $key) ? ($this->cache->get($cat, $key) ?? $this->config->get($cat, $key)) : From 3110831131c0ab0c781d4916930a73ac2f0ce3f4 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 21:05:58 +0100 Subject: [PATCH 22/71] Fix empty fetchResult at ExternalStorage (thx to Marco R.) --- src/Model/Post/Link.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Post/Link.php b/src/Model/Post/Link.php index aa830a1d28..343ad815c3 100644 --- a/src/Model/Post/Link.php +++ b/src/Model/Post/Link.php @@ -126,7 +126,7 @@ class Link $timeout = DI::config()->get('system', 'xrd_timeout'); $curlResult = HTTPSignature::fetchRaw($url, 0, [HttpClientOptions::TIMEOUT => $timeout, HttpClientOptions::ACCEPT_CONTENT => $accept]); - if (!$curlResult->isSuccess()) { + if (empty($curlResult) || !$curlResult->isSuccess()) { return []; } $fields = ['mimetype' => $curlResult->getHeader('Content-Type')[0]]; From 176af67e88e128e5ad1b975e3e5b07f4d247b720 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 21:06:16 +0100 Subject: [PATCH 23/71] Fix empty fetchResult curResult at Link::fetchMimeType (thx to Marco R.) --- src/Core/Storage/Type/ExternalResource.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Core/Storage/Type/ExternalResource.php b/src/Core/Storage/Type/ExternalResource.php index c988ea90c4..055db0deaa 100644 --- a/src/Core/Storage/Type/ExternalResource.php +++ b/src/Core/Storage/Type/ExternalResource.php @@ -63,7 +63,11 @@ class ExternalResource implements ICanReadFromStorage Logger::debug('Got picture', ['Content-Type' => $fetchResult->getHeader('Content-Type'), 'uid' => $data->uid, 'url' => $data->url]); return $fetchResult->getBody(); } else { - throw new ReferenceStorageException(sprintf('External resource failed to get %s', $reference), $fetchResult->getReturnCode(), new Exception($fetchResult->getBody())); + if (empty($fetchResult)) { + throw new ReferenceStorageException(sprintf('External resource failed to get %s', $reference)); + } else { + throw new ReferenceStorageException(sprintf('External resource failed to get %s', $reference), $fetchResult->getReturnCode(), new Exception($fetchResult->getBody())); + } } } From 4c2fc3ea384d5aeb2705bc634448442deb9e0ac6 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 23:55:51 +0100 Subject: [PATCH 24/71] Reduce config->set() load for worker executions --- src/App/BaseURL.php | 21 +++++++++++--------- tests/src/Util/BaseURLTest.php | 35 ++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/App/BaseURL.php b/src/App/BaseURL.php index 20fd54916d..89dded3002 100644 --- a/src/App/BaseURL.php +++ b/src/App/BaseURL.php @@ -175,6 +175,7 @@ class BaseURL $currHostname = $this->hostname; $currSSLPolicy = $this->sslPolicy; $currURLPath = $this->urlPath; + $currUrl = $this->url; if (!empty($hostname) && $hostname !== $this->hostname) { if ($this->config->set('config', 'hostname', $hostname)) { @@ -207,16 +208,18 @@ class BaseURL } $this->determineBaseUrl(); - if (!$this->config->set('system', 'url', $this->url)) { - $this->hostname = $currHostname; - $this->sslPolicy = $currSSLPolicy; - $this->urlPath = $currURLPath; - $this->determineBaseUrl(); + if ($this->url !== $currUrl) { + if (!$this->config->set('system', 'url', $this->url)) { + $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); - return false; + $this->config->set('config', 'hostname', $this->hostname); + $this->config->set('system', 'ssl_policy', $this->sslPolicy); + $this->config->set('system', 'urlpath', $this->urlPath); + return false; + } } return true; diff --git a/tests/src/Util/BaseURLTest.php b/tests/src/Util/BaseURLTest.php index 0be83be0a7..e108385f05 100644 --- a/tests/src/Util/BaseURLTest.php +++ b/tests/src/Util/BaseURLTest.php @@ -231,6 +231,21 @@ class BaseURLTest extends MockedTest public function dataSave() { return [ + 'no_change' => [ + 'input' => [ + 'hostname' => 'friendica.local', + 'urlPath' => 'path', + 'sslPolicy' => BaseURL::SSL_POLICY_FULL, + 'url' => 'https://friendica.local/path', + 'force_ssl' => true, + ], + 'save' => [ + 'hostname' => 'friendica.local', + 'urlPath' => 'path', + 'sslPolicy' => BaseURL::SSL_POLICY_FULL, + ], + 'url' => 'https://friendica.local/path', + ], 'default' => [ 'input' => [ 'hostname' => 'friendica.old', @@ -324,19 +339,21 @@ class BaseURLTest extends MockedTest $baseUrl = new BaseURL($configMock, []); - if (isset($save['hostname'])) { + if (isset($save['hostname']) && ($save['hostname'] !== $input['hostname'])) { $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); } - if (isset($save['urlPath'])) { + if (isset($save['urlPath']) && ($save['urlPath'] !== $input['urlPath'])) { $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); } - if (isset($save['sslPolicy'])) { + if (isset($save['sslPolicy']) && ($save['sslPolicy'] !== $input['sslPolicy'])) { $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); } - $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); + if ($input['url'] !== $url) { + $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); + } $baseUrl->save($save['hostname'], $save['sslPolicy'], $save['urlPath']); @@ -362,19 +379,21 @@ class BaseURLTest extends MockedTest $baseUrl = new BaseURL($configMock, []); - if (isset($save['hostname'])) { + if (isset($save['hostname']) && ($save['hostname'] !== $input['hostname'])) { $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); } - if (isset($save['urlPath'])) { + if (isset($save['urlPath']) && ($save['urlPath'] !== $input['urlPath'])) { $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); } - if (isset($save['sslPolicy'])) { + if (isset($save['sslPolicy']) && ($save['sslPolicy'] !== $input['sslPolicy'])) { $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); } - $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); + if ($input['url'] !== $url) { + $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); + } $baseUrl->saveByURL($url); From 317c525cbe55e5c087d656de6a6dade39824c7d1 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 23:58:33 +0100 Subject: [PATCH 25/71] Fix keyValue() call at daemon.php --- bin/daemon.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/daemon.php b/bin/daemon.php index f9ed693f38..577e884ebf 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -126,7 +126,7 @@ if ($mode == 'status') { unlink($pidfile); - DI::config()->set('system', 'worker_daemon_mode', false); + DI::keyValue()->set('worker_daemon_mode', false); die("Daemon process $pid isn't running.\n"); } From 2292263780000a50e1e7583bc170cca619d86f42 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 23:58:55 +0100 Subject: [PATCH 26/71] Add more special chars at tests --- tests/src/Core/Config/Cache/ConfigFileManagerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/Core/Config/Cache/ConfigFileManagerTest.php b/tests/src/Core/Config/Cache/ConfigFileManagerTest.php index 99049426bc..55dff2f4a9 100644 --- a/tests/src/Core/Config/Cache/ConfigFileManagerTest.php +++ b/tests/src/Core/Config/Cache/ConfigFileManagerTest.php @@ -407,7 +407,7 @@ class ConfigFileManagerTest extends MockedTest $configFileLoader->setupCache($configCache); - $specialChars = '!"§$%&/()(/&%$\'>set('system', 'test', 'it', Cache::SOURCE_DATA); From 17105cf7d126dc9765975dd54d2daf261a1c18a9 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 23:06:17 +0100 Subject: [PATCH 27/71] Fix config read/write locking --- src/Core/Config/Util/ConfigFileManager.php | 25 +++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index 378d7fbd4e..a3084b8323 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -173,7 +173,21 @@ class ConfigFileManager $filename = $this->configDir . '/' . self::CONFIG_DATA_FILE; if (file_exists($filename)) { - $dataArray = include $filename; + + $content = '' . $content); if (!is_array($dataArray)) { throw new ConfigFileException(sprintf('Error loading config file %s', $filename)); @@ -200,8 +214,13 @@ class ConfigFileManager 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)); + $configStream = fopen($this->configDir . '/' . self::CONFIG_DATA_FILE, 'w+'); + + if (flock($configStream, LOCK_EX)) { + ftruncate($configStream, 0); + fwrite($configStream, $encodedData); + fflush($configStream); + flock($configStream, LOCK_UN); } } From c057954896ee9984513473acb4041751ecad6339 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 4 Jan 2023 08:14:00 +0100 Subject: [PATCH 28/71] Make BaseURL check/save transactional and make the whole process easier --- src/App/BaseURL.php | 53 +++---- .../ISetConfigValuesTransactionally.php | 4 - tests/src/Util/BaseURLTest.php | 148 +++++------------- 3 files changed, 59 insertions(+), 146 deletions(-) diff --git a/src/App/BaseURL.php b/src/App/BaseURL.php index 89dded3002..e557f712f0 100644 --- a/src/App/BaseURL.php +++ b/src/App/BaseURL.php @@ -172,54 +172,37 @@ class BaseURL */ public function save($hostname = null, $sslPolicy = null, $urlPath = null): bool { - $currHostname = $this->hostname; - $currSSLPolicy = $this->sslPolicy; - $currURLPath = $this->urlPath; - $currUrl = $this->url; + $currUrl = $this->url; + + $configTransaction = $this->config->beginTransaction(); + $savable = false; if (!empty($hostname) && $hostname !== $this->hostname) { - if ($this->config->set('config', 'hostname', $hostname)) { - $this->hostname = $hostname; - } else { - return false; - } + $configTransaction->set('config', 'hostname', $hostname); + $this->hostname = $hostname; + $savable = true; } if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) { - if ($this->config->set('system', 'ssl_policy', $sslPolicy)) { - $this->sslPolicy = $sslPolicy; - } else { - $this->hostname = $currHostname; - $this->config->set('config', 'hostname', $this->hostname); - return false; - } + $configTransaction->set('system', 'ssl_policy', $sslPolicy); + $this->sslPolicy = $sslPolicy; + $savable = true; } if (isset($urlPath) && $urlPath !== $this->urlPath) { - if ($this->config->set('system', 'urlpath', $urlPath)) { - $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); - return false; - } + $configTransaction->set('system', 'urlpath', $urlPath); + $this->urlPath = $urlPath; + $savable = true; } $this->determineBaseUrl(); if ($this->url !== $currUrl) { - if (!$this->config->set('system', 'url', $this->url)) { - $this->hostname = $currHostname; - $this->sslPolicy = $currSSLPolicy; - $this->urlPath = $currURLPath; - $this->determineBaseUrl(); + $configTransaction->set('system', 'url', $this->url); + $savable = true; + } - $this->config->set('config', 'hostname', $this->hostname); - $this->config->set('system', 'ssl_policy', $this->sslPolicy); - $this->config->set('system', 'urlpath', $this->urlPath); - return false; - } + if ($savable) { + $configTransaction->commit(); } return true; diff --git a/src/Core/Config/Capability/ISetConfigValuesTransactionally.php b/src/Core/Config/Capability/ISetConfigValuesTransactionally.php index 501e24f738..a9fe36b022 100644 --- a/src/Core/Config/Capability/ISetConfigValuesTransactionally.php +++ b/src/Core/Config/Capability/ISetConfigValuesTransactionally.php @@ -41,8 +41,6 @@ interface ISetConfigValuesTransactionally * @param mixed $value The value to store * * @return static the current instance - * - * @throws ConfigPersistenceException In case the persistence layer throws errors */ public function set(string $cat, string $key, $value): self; @@ -54,8 +52,6 @@ interface ISetConfigValuesTransactionally * * @return static the current instance * - * @throws ConfigPersistenceException In case the persistence layer throws errors - * */ public function delete(string $cat, string $key): self; diff --git a/tests/src/Util/BaseURLTest.php b/tests/src/Util/BaseURLTest.php index e108385f05..b9c23a1d8b 100644 --- a/tests/src/Util/BaseURLTest.php +++ b/tests/src/Util/BaseURLTest.php @@ -23,10 +23,25 @@ namespace Friendica\Test\src\Util; use Friendica\App\BaseURL; use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Config\Capability\ISetConfigValuesTransactionally; +use Friendica\Core\Config\Model\Config; +use Friendica\Core\Config\Model\ConfigTransaction; +use Friendica\Core\Config\Util\ConfigFileManager; +use Friendica\Core\Config\ValueObject\Cache; use Friendica\Test\MockedTest; +use Friendica\Test\Util\VFSTrait; class BaseURLTest extends MockedTest { + use VFSTrait; + + protected function setUp(): void + { + parent::setUp(); + + $this->setUpVfsDir(); + } + public function dataDefault() { return [ @@ -330,30 +345,20 @@ class BaseURLTest extends MockedTest */ public function testSave($input, $save, $url) { - $configMock = \Mockery::mock(IManageConfigValues::class); - $configMock->shouldReceive('get')->with('config', 'hostname')->andReturn($input['hostname']); - $configMock->shouldReceive('get')->with('system', 'urlpath')->andReturn($input['urlPath']); - $configMock->shouldReceive('get')->with('system', 'ssl_policy')->andReturn($input['sslPolicy']); - $configMock->shouldReceive('get')->with('system', 'url')->andReturn($input['url']); - $configMock->shouldReceive('get')->with('system', 'force_ssl')->andReturn($input['force_ssl']); + $configFileManager = new ConfigFileManager($this->root->url(), $this->root->url() . '/config/', $this->root->url() . '/static/'); + $config = new Config($configFileManager, new Cache([ + 'config' => [ + 'hostname' => $input['hostname'] ?? null, + ], + 'system' => [ + 'urlpath' => $input['urlPath'] ?? null, + 'ssl_policy' => $input['sslPolicy'] ?? null, + 'url' => $input['url'] ?? null, + 'force_ssl' => $input['force_ssl'] ?? null, + ], + ])); - $baseUrl = new BaseURL($configMock, []); - - if (isset($save['hostname']) && ($save['hostname'] !== $input['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); - } - - if (isset($save['urlPath']) && ($save['urlPath'] !== $input['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); - } - - if (isset($save['sslPolicy']) && ($save['sslPolicy'] !== $input['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); - } - - if ($input['url'] !== $url) { - $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); - } + $baseUrl = new BaseURL($config, []); $baseUrl->save($save['hostname'], $save['sslPolicy'], $save['urlPath']); @@ -370,30 +375,20 @@ class BaseURLTest extends MockedTest */ public function testSaveByUrl($input, $save, $url) { - $configMock = \Mockery::mock(IManageConfigValues::class); - $configMock->shouldReceive('get')->with('config', 'hostname')->andReturn($input['hostname']); - $configMock->shouldReceive('get')->with('system', 'urlpath')->andReturn($input['urlPath']); - $configMock->shouldReceive('get')->with('system', 'ssl_policy')->andReturn($input['sslPolicy']); - $configMock->shouldReceive('get')->with('system', 'url')->andReturn($input['url']); - $configMock->shouldReceive('get')->with('system', 'force_ssl')->andReturn($input['force_ssl']); + $configFileManager = new ConfigFileManager($this->root->url(), $this->root->url() . '/config/', $this->root->url() . '/static/'); + $config = new Config($configFileManager, new Cache([ + 'config' => [ + 'hostname' => $input['hostname'] ?? null, + ], + 'system' => [ + 'urlpath' => $input['urlPath'] ?? null, + 'ssl_policy' => $input['sslPolicy'] ?? null, + 'url' => $input['url'] ?? null, + 'force_ssl' => $input['force_ssl'] ?? null, + ], + ])); - $baseUrl = new BaseURL($configMock, []); - - if (isset($save['hostname']) && ($save['hostname'] !== $input['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); - } - - if (isset($save['urlPath']) && ($save['urlPath'] !== $input['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); - } - - if (isset($save['sslPolicy']) && ($save['sslPolicy'] !== $input['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); - } - - if ($input['url'] !== $url) { - $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); - } + $baseUrl = new BaseURL($config, []); $baseUrl->saveByURL($url); @@ -517,65 +512,4 @@ class BaseURLTest extends MockedTest self::assertEquals($redirect, $baseUrl->checkRedirectHttps()); } - - public function dataWrongSave() - { - return [ - 'wrongHostname' => [ - 'fail' => 'hostname', - ], - 'wrongSSLPolicy' => [ - 'fail' => 'sslPolicy', - ], - 'wrongURLPath' => [ - 'fail' => 'urlPath', - ], - 'wrongURL' => [ - 'fail' => 'url', - ], - ]; - } - - /** - * Test the save() method with wrong parameters - * @dataProvider dataWrongSave - */ - public function testWrongSave($fail) - { - $configMock = \Mockery::mock(IManageConfigValues::class); - $configMock->shouldReceive('get')->with('config', 'hostname')->andReturn('friendica.local'); - $configMock->shouldReceive('get')->with('system', 'urlpath')->andReturn('new/test'); - $configMock->shouldReceive('get')->with('system', 'ssl_policy')->andReturn(BaseURL::DEFAULT_SSL_SCHEME); - $configMock->shouldReceive('get')->with('system', 'url')->andReturn('http://friendica.local/new/test'); - - switch ($fail) { - case 'hostname': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(false)->once(); - break; - case 'sslPolicy': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(false)->once(); - break; - case 'urlPath': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(false)->once(); - break; - case 'url': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'url', \Mockery::any())->andReturn(false)->once(); - break; - } - - $baseUrl = new BaseURL($configMock, []); - self::assertFalse($baseUrl->save('test', 10, 'nope')); - - // nothing should have changed because we never successfully saved anything - self::assertEquals('friendica.local', $baseUrl->getHostname()); - self::assertEquals('new/test', $baseUrl->getUrlPath()); - self::assertEquals(BaseURL::DEFAULT_SSL_SCHEME, $baseUrl->getSSLPolicy()); - self::assertEquals('http://friendica.local/new/test', $baseUrl->get()); - } } From aabe39220dfbe2d914edc746799a8c74b443f986 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 4 Jan 2023 08:16:40 +0100 Subject: [PATCH 29/71] Make flock writing easier --- src/Core/Config/Util/ConfigFileManager.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index a3084b8323..cc264ea26c 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -214,10 +214,9 @@ class ConfigFileManager throw new ConfigFileException('config source cannot get encoded'); } - $configStream = fopen($this->configDir . '/' . self::CONFIG_DATA_FILE, 'w+'); + $configStream = fopen($this->configDir . '/' . self::CONFIG_DATA_FILE, 'w'); if (flock($configStream, LOCK_EX)) { - ftruncate($configStream, 0); fwrite($configStream, $encodedData); fflush($configStream); flock($configStream, LOCK_UN); From bf28f4f75179386efac8cbdd49e70fe069626f30 Mon Sep 17 00:00:00 2001 From: Hannes Heute <5753419+haheute@users.noreply.github.com> Date: Wed, 4 Jan 2023 09:18:24 +0100 Subject: [PATCH 30/71] small typo --- view/theme/frio/templates/photo_view.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/theme/frio/templates/photo_view.tpl b/view/theme/frio/templates/photo_view.tpl index 76fa33a3a3..fe2aa38391 100644 --- a/view/theme/frio/templates/photo_view.tpl +++ b/view/theme/frio/templates/photo_view.tpl @@ -1,4 +1,4 @@ -{{* Template for singele photo view *}} +{{* Template for single photo view *}} {{* "live-photos" is needed for js autoupdate *}}
From 46461be818d5de9cc6902df9505e4a0b1c6ced2a Mon Sep 17 00:00:00 2001 From: Hannes Heute <5753419+haheute@users.noreply.github.com> Date: Wed, 4 Jan 2023 14:25:33 +0100 Subject: [PATCH 31/71] improve previous / next function in photo gallery --- mod/photos.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mod/photos.php b/mod/photos.php index a1aebfac66..2f9c5ca310 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -937,11 +937,17 @@ function photos_content(App $a) $nxt = null; foreach ($prvnxt as $z => $entry) { if ($entry['resource-id'] == $ph[0]['resource-id']) { - $prv = $z - 1; - $nxt = $z + 1; + $prv = ($order_field === 'created') ? ($z - 1) : ($z + 1); + $nxt = ($order_field === 'created') ? ($z + 1) : ($z - 1); if ($prv < 0) { $prv = count($prvnxt) - 1; } + if ($nxt < 0) { + $nxt = count($prvnxt) - 1; + } + if ($prv >= count($prvnxt)) { + $prv = 0; + } if ($nxt >= count($prvnxt)) { $nxt = 0; } From 24b5710e1704c02e57d0d2da81dd47f8e232bd4e Mon Sep 17 00:00:00 2001 From: Hannes Heute <5753419+haheute@users.noreply.github.com> Date: Wed, 4 Jan 2023 16:58:45 +0100 Subject: [PATCH 32/71] remove parentheses --- mod/photos.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/photos.php b/mod/photos.php index 2f9c5ca310..50780724ff 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -937,8 +937,8 @@ function photos_content(App $a) $nxt = null; foreach ($prvnxt as $z => $entry) { if ($entry['resource-id'] == $ph[0]['resource-id']) { - $prv = ($order_field === 'created') ? ($z - 1) : ($z + 1); - $nxt = ($order_field === 'created') ? ($z + 1) : ($z - 1); + $prv = $order_field === 'created' ? $z - 1 : $z + 1; + $nxt = $order_field === 'created' ? $z + 1 : $z - 1; if ($prv < 0) { $prv = count($prvnxt) - 1; } From dce86be58efad2db2e4a473bbf8dd4d1f281d5b7 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 4 Jan 2023 19:55:22 +0100 Subject: [PATCH 33/71] Just commit config transactions if something changed --- src/App/BaseURL.php | 9 +------ src/Core/Config/Model/ConfigTransaction.php | 9 +++++++ .../src/Core/Config/ConfigTransactionTest.php | 24 +++++++++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/App/BaseURL.php b/src/App/BaseURL.php index e557f712f0..ab3d03a5be 100644 --- a/src/App/BaseURL.php +++ b/src/App/BaseURL.php @@ -175,35 +175,28 @@ class BaseURL $currUrl = $this->url; $configTransaction = $this->config->beginTransaction(); - $savable = false; if (!empty($hostname) && $hostname !== $this->hostname) { $configTransaction->set('config', 'hostname', $hostname); $this->hostname = $hostname; - $savable = true; } if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) { $configTransaction->set('system', 'ssl_policy', $sslPolicy); $this->sslPolicy = $sslPolicy; - $savable = true; } if (isset($urlPath) && $urlPath !== $this->urlPath) { $configTransaction->set('system', 'urlpath', $urlPath); $this->urlPath = $urlPath; - $savable = true; } $this->determineBaseUrl(); if ($this->url !== $currUrl) { $configTransaction->set('system', 'url', $this->url); - $savable = true; } - if ($savable) { - $configTransaction->commit(); - } + $configTransaction->commit(); return true; } diff --git a/src/Core/Config/Model/ConfigTransaction.php b/src/Core/Config/Model/ConfigTransaction.php index 296a469c06..26420b0788 100644 --- a/src/Core/Config/Model/ConfigTransaction.php +++ b/src/Core/Config/Model/ConfigTransaction.php @@ -37,6 +37,8 @@ class ConfigTransaction implements ISetConfigValuesTransactionally protected $cache; /** @var Cache */ protected $delCache; + /** @var bool field to check if something is to save */ + protected $changedConfig = false; public function __construct(IManageConfigValues $config) { @@ -70,6 +72,7 @@ class ConfigTransaction implements ISetConfigValuesTransactionally public function set(string $cat, string $key, $value): ISetConfigValuesTransactionally { $this->cache->set($cat, $key, $value, Cache::SOURCE_DATA); + $this->changedConfig = true; return $this; } @@ -80,6 +83,7 @@ class ConfigTransaction implements ISetConfigValuesTransactionally { $this->cache->delete($cat, $key); $this->delCache->set($cat, $key, 'deleted'); + $this->changedConfig = true; return $this; } @@ -87,6 +91,11 @@ class ConfigTransaction implements ISetConfigValuesTransactionally /** {@inheritDoc} */ public function commit(): void { + // If nothing changed, just do nothing :) + if (!$this->changedConfig) { + return; + } + try { $newCache = $this->config->getCache()->merge($this->cache); $newCache = $newCache->diff($this->delCache); diff --git a/tests/src/Core/Config/ConfigTransactionTest.php b/tests/src/Core/Config/ConfigTransactionTest.php index 2eec9b68f3..454c760d4a 100644 --- a/tests/src/Core/Config/ConfigTransactionTest.php +++ b/tests/src/Core/Config/ConfigTransactionTest.php @@ -9,6 +9,7 @@ use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Test\MockedTest; use Friendica\Test\Util\VFSTrait; +use Mockery\Exception\InvalidCountException; class ConfigTransactionTest extends MockedTest { @@ -108,4 +109,27 @@ class ConfigTransactionTest extends MockedTest // the whole category should be gone self::assertNull($tempData['delete'] ?? null); } + + /** + * This test asserts that in empty transactions, no saveData is called, thus no config file writing was performed + */ + public function testNothingToDo() + { + $this->configFileManager = \Mockery::spy(ConfigFileManager::class); + + $config = new Config($this->configFileManager, new Cache()); + $configTransaction = new ConfigTransaction($config); + + // commit empty transaction + $configTransaction->commit(); + + try { + $this->configFileManager->shouldNotHaveReceived('saveData'); + } catch (InvalidCountException $exception) { + self::fail($exception); + } + + // If not failed, the test ends successfully :) + self::assertTrue(true); + } } From a574146f042d52e2acbec3bd4ce98ad1eeaf6057 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 4 Jan 2023 11:38:08 -0500 Subject: [PATCH 34/71] Add UriInterface-enabled cleanUri method in Model\GServer - Tests! --- src/Model/GServer.php | 30 +++++++++++-- tests/src/Model/GServerTest.php | 76 +++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 tests/src/Model/GServerTest.php diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 1878befa9f..9e32c7b0cb 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -45,6 +45,7 @@ use Friendica\Util\Strings; use Friendica\Util\XML; use Friendica\Network\HTTPException; use GuzzleHttp\Psr7\Uri; +use Psr\Http\Message\UriInterface; /** * This class handles GServer related functions @@ -442,18 +443,41 @@ class GServer * * @return string cleaned URL * @throws Exception + * @deprecated since 2023.03 Use cleanUri instead */ public static function cleanURL(string $dirtyUrl): string { try { - $url = str_replace('/index.php', '', trim($dirtyUrl, '/')); - return (string)(new Uri($url))->withUserInfo('')->withQuery('')->withFragment(''); + return (string)self::cleanUri(new Uri($dirtyUrl)); } catch (\Throwable $e) { - Logger::warning('Invalid URL', ['dirtyUrl' => $dirtyUrl, 'url' => $url]); + Logger::warning('Invalid URL', ['dirtyUrl' => $dirtyUrl]); return ''; } } + /** + * Remove unwanted content from the given URI + * + * @param UriInterface $dirtyUri + * + * @return UriInterface cleaned URI + * @throws Exception + */ + public static function cleanUri(UriInterface $dirtyUri): string + { + return $dirtyUri + ->withUserInfo('') + ->withQuery('') + ->withFragment('') + ->withPath( + preg_replace( + '#(?:^|/)index\.php#', + '', + rtrim($dirtyUri->getPath(), '/') + ) + ); + } + /** * Detect server data (type, protocol, version number, ...) * The detected data is then updated or inserted in the gserver table. diff --git a/tests/src/Model/GServerTest.php b/tests/src/Model/GServerTest.php new file mode 100644 index 0000000000..a56f4ed6ff --- /dev/null +++ b/tests/src/Model/GServerTest.php @@ -0,0 +1,76 @@ +. + * + */ + +namespace Friendica\Test\src\Model; + +use Friendica\Model\GServer; +use GuzzleHttp\Psr7\Uri; +use Psr\Http\Message\UriInterface; + +class GServerTest extends \PHPUnit\Framework\TestCase +{ + public function dataCleanUri(): array + { + return [ + 'full-monty' => [ + 'expected' => new Uri('https://example.com/path'), + 'dirtyUri' => new Uri('https://user:password@example.com/path?query=string#fragment'), + ], + 'index.php' => [ + 'expected' => new Uri('https://example.com'), + 'dirtyUri' => new Uri('https://example.com/index.php'), + ], + 'index.php-2' => [ + 'expected' => new Uri('https://example.com/path/to/resource'), + 'dirtyUri' => new Uri('https://example.com/index.php/path/to/resource'), + ], + 'index.php-path' => [ + 'expected' => new Uri('https://example.com/path/to'), + 'dirtyUri' => new Uri('https://example.com/path/to/index.php'), + ], + 'index.php-path-2' => [ + 'expected' => new Uri('https://example.com/path/to/path/to/resource'), + 'dirtyUri' => new Uri('https://example.com/path/to/index.php/path/to/resource'), + ], + 'index.php-slash' => [ + 'expected' => new Uri('https://example.com'), + 'dirtyUri' => new Uri('https://example.com/index.php/'), + ], + 'index.php-slash-2' => [ + 'expected' => new Uri('https://example.com/path/to/resource'), + 'dirtyUri' => new Uri('https://example.com/index.php/path/to/resource/'), + ], + ]; + } + + /** + * @dataProvider dataCleanUri + * + * @param UriInterface $expected + * @param UriInterface $dirtyUri + * @return void + * @throws \Exception + */ + public function testCleanUri(UriInterface $expected, UriInterface $dirtyUri) + { + $this->assertEquals($expected, GServer::cleanUri($dirtyUri)); + } +} From a907d6c87b05c475953e25205e453a77fbf45274 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 4 Jan 2023 11:38:46 -0500 Subject: [PATCH 35/71] Add UriInterface-enabled isUriBlocked method in Util\Network --- src/Util/Network.php | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Util/Network.php b/src/Util/Network.php index b7954d8e4d..f9f32d4222 100644 --- a/src/Util/Network.php +++ b/src/Util/Network.php @@ -29,6 +29,7 @@ use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientOptions; use Friendica\Network\HTTPException\NotModifiedException; use GuzzleHttp\Psr7\Uri; +use Psr\Http\Message\UriInterface; class Network { @@ -177,11 +178,28 @@ class Network * @param string $url The url to check the domain from * * @return boolean + * + * @deprecated since 2023.03 Use isUriBlocked instead */ public static function isUrlBlocked(string $url): bool { - $host = @parse_url($url, PHP_URL_HOST); - if (!$host) { + try { + return self::isUriBlocked(new Uri($url)); + } catch (\Throwable $e) { + Logger::warning('Invalid URL', ['url' => $url]); + return false; + } + } + + /** + * Checks if the provided URI domain is on the domain blocklist. + * + * @param UriInterface $uri + * @return boolean + */ + public static function isUriBlocked(UriInterface $uri): bool + { + if (!$uri->getHost()) { return false; } @@ -191,7 +209,7 @@ class Network } foreach ($domain_blocklist as $domain_block) { - if (fnmatch(strtolower($domain_block['domain']), strtolower($host))) { + if (fnmatch(strtolower($domain_block['domain']), strtolower($uri->getHost()))) { return true; } } From 1f3c07c06f53877735f5dc8d686120ff8be5c56b Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 4 Jan 2023 11:39:22 -0500 Subject: [PATCH 36/71] Drop UpdateGServer worker task if domain is blocked --- src/Model/GServer.php | 17 ++++++++++------- src/Worker/UpdateGServer.php | 32 +++++++++++++++++++++++++++++++- src/Worker/UpdateGServers.php | 5 +++-- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 9e32c7b0cb..a3a3d1abbc 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -44,6 +44,7 @@ use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Util\XML; use Friendica\Network\HTTPException; +use Friendica\Worker\UpdateGServer; use GuzzleHttp\Psr7\Uri; use Psr\Http\Message\UriInterface; @@ -100,11 +101,11 @@ class GServer */ public static function add(string $url, bool $only_nodeinfo = false) { - if (self::getID($url, false)) { + if (self::getID($url)) { return; } - Worker::add(Worker::PRIORITY_LOW, 'UpdateGServer', $url, $only_nodeinfo); + UpdateGServer::add(Worker::PRIORITY_LOW, $url, $only_nodeinfo); } /** @@ -192,8 +193,9 @@ class GServer return false; } else { if (strtotime($gserver['next_contact']) < time()) { - Worker::add(Worker::PRIORITY_LOW, 'UpdateGServer', $gserver['url'], false); + UpdateGServer::add(Worker::PRIORITY_LOW, $gserver['url']); } + return self::isDefunct($gserver); } } @@ -211,8 +213,9 @@ class GServer return true; } else { if (strtotime($gserver['next_contact']) < time()) { - Worker::add(Worker::PRIORITY_LOW, 'UpdateGServer', $gserver['url'], false); + UpdateGServer::add(Worker::PRIORITY_LOW, $gserver['url']); } + return !$gserver['failed'] && in_array($gserver['network'], Protocol::FEDERATED); } } @@ -253,7 +256,7 @@ class GServer } if (!empty($server) && (empty($gserver) || strtotime($gserver['next_contact']) < time())) { - Worker::add(Worker::PRIORITY_LOW, 'UpdateGServer', $server, false); + UpdateGServer::add(Worker::PRIORITY_LOW, $server); } return $reachable; @@ -376,7 +379,7 @@ class GServer Logger::info('Reset failed status for server', ['url' => $gserver['url']]); if (strtotime($gserver['next_contact']) < time()) { - Worker::add(Worker::PRIORITY_LOW, 'UpdateGServer', $gserver['url'], false); + UpdateGServer::add(Worker::PRIORITY_LOW, $gserver['url']); } } } @@ -394,7 +397,7 @@ class GServer Logger::info('Set failed status for server', ['url' => $gserver['url']]); if (strtotime($gserver['next_contact']) < time()) { - Worker::add(Worker::PRIORITY_LOW, 'UpdateGServer', $gserver['url'], false); + UpdateGServer::add(Worker::PRIORITY_LOW, $gserver['url']); } } } diff --git a/src/Worker/UpdateGServer.php b/src/Worker/UpdateGServer.php index 9b111fceda..f6f33113f3 100644 --- a/src/Worker/UpdateGServer.php +++ b/src/Worker/UpdateGServer.php @@ -22,9 +22,14 @@ namespace Friendica\Worker; use Friendica\Core\Logger; +use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\Model\GServer; +use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Util\Network; use Friendica\Util\Strings; +use GuzzleHttp\Psr7\Uri; +use Psr\Http\Message\UriInterface; class UpdateGServer { @@ -34,8 +39,9 @@ class UpdateGServer * @param string $server_url Server URL * @param boolean $only_nodeinfo Only use nodeinfo for server detection * @return void + * @throws \Exception */ - public static function execute(string $server_url, bool $only_nodeinfo = false) + public static function execute(string $server_url, bool $only_nodeinfo) { if (empty($server_url)) { return; @@ -47,6 +53,11 @@ class UpdateGServer return; } + // Silently dropping the worker task if the server domain is blocked + if (Network::isUrlBlocked($filtered)) { + return; + } + if (($filtered != $server_url) && DBA::exists('gserver', ['nurl' => Strings::normaliseLink($server_url)])) { GServer::setFailureByUrl($server_url); return; @@ -61,4 +72,23 @@ class UpdateGServer $ret = GServer::check($filtered, '', true, $only_nodeinfo); Logger::info('Updated gserver', ['url' => $filtered, 'result' => $ret]); } + + /** + * @param array|int $run_parameters Priority constant or array of options described in Worker::add + * @param string $serverUrl + * @param bool $onlyNodeInfo Only use NodeInfo for server detection + * @return int + * @throws InternalServerErrorException + */ + public static function add($run_parameters, string $serverUrl, bool $onlyNodeInfo = false): int + { + // Dropping the worker task if the server domain is blocked + if (Network::isUrlBlocked($serverUrl)) { + return 0; + } + + // We have to convert the Uri back to string because worker parameters are saved in JSON format which + // doesn't allow for structured objects. + return Worker::add($run_parameters, 'UpdateGServer', $serverUrl, $onlyNodeInfo); + } } diff --git a/src/Worker/UpdateGServers.php b/src/Worker/UpdateGServers.php index ef76ca9bf5..12f3ff10e1 100644 --- a/src/Worker/UpdateGServers.php +++ b/src/Worker/UpdateGServers.php @@ -27,6 +27,7 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Util\DateTimeFormat; use Friendica\Util\Strings; +use GuzzleHttp\Psr7\Uri; class UpdateGServers { @@ -63,12 +64,12 @@ class UpdateGServers // There are duplicated "url" but not "nurl". So we check both addresses instead of just overwriting them, // since that would mean loosing data. if (!empty($gserver['url'])) { - if (Worker::add(Worker::PRIORITY_LOW, 'UpdateGServer', $gserver['url'])) { + if (UpdateGServer::add(Worker::PRIORITY_LOW, $gserver['url'])) { $count++; } } if (!empty($gserver['nurl']) && ($gserver['nurl'] != Strings::normaliseLink($gserver['url']))) { - if (Worker::add(Worker::PRIORITY_LOW, 'UpdateGServer', $gserver['nurl'])) { + if (UpdateGServer::add(Worker::PRIORITY_LOW, $gserver['nurl'])) { $count++; } } From 647ab1d04ac1dfba46888fed156dd416c7aa5b54 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 4 Jan 2023 11:42:54 -0500 Subject: [PATCH 37/71] Drop UpdateContact worker task if contact is blocked --- src/Worker/UpdateContact.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Worker/UpdateContact.php b/src/Worker/UpdateContact.php index 633058428d..c2e4432820 100644 --- a/src/Worker/UpdateContact.php +++ b/src/Worker/UpdateContact.php @@ -25,6 +25,7 @@ use Friendica\Core\Logger; use Friendica\Core\Worker; use Friendica\Model\Contact; use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Util\Network; class UpdateContact { @@ -38,6 +39,11 @@ class UpdateContact */ public static function execute(int $contact_id) { + // Silently dropping the task if the contact is blocked + if (Contact::isBlocked($contact_id)) { + return; + } + $success = Contact::updateFromProbe($contact_id); Logger::info('Updated from probe', ['id' => $contact_id, 'success' => $success]); @@ -55,6 +61,11 @@ class UpdateContact throw new \InvalidArgumentException('Invalid value provided for contact_id'); } + // Dropping the task if the contact is blocked + if (Contact::isBlocked($contact_id)) { + return 0; + } + return Worker::add($run_parameters, 'UpdateContact', $contact_id); } } From cd11088cc408de3bcf1081b2ecb9850b39563b28 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 19:54:05 +0100 Subject: [PATCH 38/71] Move 'addon' table into config --- database.sql | 2 +- src/Database/DBStructure.php | 2 +- static/dbstructure.config.php | 2 +- update.php | 20 +++++++++++++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/database.sql b/database.sql index c413f2c24a..9ffa3544ac 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2023.03-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1508 +-- DB_UPDATE_VERSION 1509 -- ------------------------------------------ diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index 7b284bf6d5..c0bd005d1e 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', 'config']; + 'auth_codes', 'tokens', 'clients', 'profile_check', 'host', 'conversation', 'fcontact', 'config', 'addon']; $tables = DBA::selectToArray('INFORMATION_SCHEMA.TABLES', ['TABLE_NAME'], ['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']); diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 018bbf2c31..848da04040 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', 1508); + define('DB_UPDATE_VERSION', 1509); } return [ diff --git a/update.php b/update.php index 1dbf78e06f..584b8d788c 100644 --- a/update.php +++ b/update.php @@ -1192,5 +1192,23 @@ function update_1508() $newConfig->commit(); - DBA::e("TRUNCATE TABLE `config`"); + return DBA::e("TRUNCATE TABLE `config`") ? Update::SUCCESS : Update::FAILED; +} + +function update_1509() +{ + $addons = DBA::selectToArray('addon'); + + $newConfig = DI::config()->beginTransaction(); + + foreach ($addons as $addon) { + $newConfig->set('addons', $addon['name'], [ + 'last_update' => $addon['timestamp'], + 'admin' => (bool)$addon['plugin_admin'], + ]); + } + + $newConfig->commit(); + + return DBA::e("TRUNCATE TABLE `addon`") ? Update::SUCCESS : Update::FAILED; } From 13b234d279a7de89c718c446a85238bff7d873ca Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 20:24:48 +0100 Subject: [PATCH 39/71] Use addons config entries instead of the addon table --- src/Core/Addon.php | 52 ++++--- .../Config/Capability/IManageConfigValues.php | 6 +- src/Core/Config/Model/Config.php | 2 +- src/Core/L10n.php | 11 +- static/settings.config.php | 4 - tests/src/Core/Config/Cache/CacheTest.php | 129 ++++++++++++++++++ tests/src/Core/Config/ConfigTest.php | 129 ++++++++++++++++++ 7 files changed, 294 insertions(+), 39 deletions(-) diff --git a/src/Core/Addon.php b/src/Core/Addon.php index cea814f320..be3e70a540 100644 --- a/src/Core/Addon.php +++ b/src/Core/Addon.php @@ -21,7 +21,6 @@ namespace Friendica\Core; -use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Util\Strings; @@ -85,15 +84,18 @@ class Addon public static function getAdminList() { $addons_admin = []; - $addonsAdminStmt = DBA::select('addon', ['name'], ['plugin_admin' => 1], ['order' => ['name']]); - while ($addon = DBA::fetch($addonsAdminStmt)) { - $addons_admin[$addon['name']] = [ - 'url' => 'admin/addons/' . $addon['name'], - 'name' => $addon['name'], + $addons = DI::config()->get('addons'); + foreach ($addons as $name => $data) { + if (empty($data['admin'])) { + continue; + } + + $addons_admin[$name] = [ + 'url' => 'admin/addons/' . $name, + 'name' => $name, 'class' => 'addon' ]; } - DBA::close($addonsAdminStmt); return $addons_admin; } @@ -113,8 +115,7 @@ class Addon */ public static function loadAddons() { - $installed_addons = DBA::selectToArray('addon', ['name'], ['installed' => true]); - self::$addons = array_column($installed_addons, 'name'); + self::$addons = array_keys(DI::config()->get('addons') ?? []); } /** @@ -129,7 +130,7 @@ class Addon $addon = Strings::sanitizeFilePathItem($addon); Logger::debug("Addon {addon}: {action}", ['action' => 'uninstall', 'addon' => $addon]); - DBA::delete('addon', ['name' => $addon]); + DI::config()->delete('addons', $addon); @include_once('addon/' . $addon . '/' . $addon . '.php'); if (function_exists($addon . '_uninstall')) { @@ -168,12 +169,9 @@ class Addon $func(DI::app()); } - DBA::insert('addon', [ - 'name' => $addon, - 'installed' => true, - 'timestamp' => $t, - 'plugin_admin' => function_exists($addon . '_addon_admin'), - 'hidden' => file_exists('addon/' . $addon . '/.hidden') + DI::config()->set('addons', $addon,[ + 'last_update' => $t, + 'admin' => function_exists($addon . '_addon_admin'), ]); if (!self::isEnabled($addon)) { @@ -190,20 +188,20 @@ class Addon */ public static function reload() { - $addons = DBA::selectToArray('addon', [], ['installed' => true]); + $addons = DI::config()->get('addons'); - foreach ($addons as $addon) { - $addonname = Strings::sanitizeFilePathItem(trim($addon['name'])); + foreach ($addons as $name => $data) { + $addonname = Strings::sanitizeFilePathItem(trim($name)); $addon_file_path = 'addon/' . $addonname . '/' . $addonname . '.php'; - if (file_exists($addon_file_path) && $addon['timestamp'] == filemtime($addon_file_path)) { + if (file_exists($addon_file_path) && $data['last_update'] == filemtime($addon_file_path)) { // Addon unmodified, skipping continue; } - Logger::debug("Addon {addon}: {action}", ['action' => 'reload', 'addon' => $addon['name']]); + Logger::debug("Addon {addon}: {action}", ['action' => 'reload', 'addon' => $name]); - self::uninstall($addon['name']); - self::install($addon['name']); + self::uninstall($name); + self::install($name); } } @@ -313,11 +311,9 @@ class Addon public static function getVisibleList(): array { $visible_addons = []; - $stmt = DBA::select('addon', ['name'], ['hidden' => false, 'installed' => true]); - if (DBA::isResult($stmt)) { - foreach (DBA::toArray($stmt) as $addon) { - $visible_addons[] = $addon['name']; - } + $addons = DI::config()->get('addons'); + foreach ($addons as $name => $data) { + $visible_addons[] = $name; } return $visible_addons; diff --git a/src/Core/Config/Capability/IManageConfigValues.php b/src/Core/Config/Capability/IManageConfigValues.php index 715887ddf3..09e2cd9144 100644 --- a/src/Core/Config/Capability/IManageConfigValues.php +++ b/src/Core/Config/Capability/IManageConfigValues.php @@ -47,8 +47,8 @@ interface IManageConfigValues * * Get a particular config value from the given category ($cat) * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to query + * @param string $cat The category of the configuration value + * @param ?string $key The configuration key to query (if null, the whole array at the category will get returned) * @param mixed $default_value Deprecated, use `Config->get($cat, $key, null, $refresh) ?? $default_value` instead * * @return mixed Stored value or null if it does not exist @@ -56,7 +56,7 @@ interface IManageConfigValues * @throws ConfigPersistenceException In case the persistence layer throws errors * */ - public function get(string $cat, string $key, $default_value = null); + public function get(string $cat, string $key = null, $default_value = null); /** * Load all configuration values from a given cache and saves it back in the configuration node store diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php index 29ea6b12d3..593ab04070 100644 --- a/src/Core/Config/Model/Config.php +++ b/src/Core/Config/Model/Config.php @@ -102,7 +102,7 @@ class Config implements IManageConfigValues } /** {@inheritDoc} */ - public function get(string $cat, string $key, $default_value = null) + public function get(string $cat, string $key = null, $default_value = null) { return $this->configCache->get($cat, $key) ?? $default_value; } diff --git a/src/Core/L10n.php b/src/Core/L10n.php index 0a91677b5d..3f74a40849 100644 --- a/src/Core/L10n.php +++ b/src/Core/L10n.php @@ -85,10 +85,15 @@ class L10n * @var Database */ private $dba; + /** + * @var IManageConfigValues + */ + private $config; public function __construct(IManageConfigValues $config, Database $dba, IHandleSessions $session, array $server, array $get) { $this->dba = $dba; + $this->config = $config; $this->loadTranslationTable(L10n::detectLanguage($server, $get, $config->get('system', 'language', self::DEFAULT))); $this->setSessionVariable($session); @@ -157,9 +162,9 @@ class L10n $a->strings = []; // load enabled addons strings - $addons = $this->dba->select('addon', ['name'], ['installed' => true]); - while ($p = $this->dba->fetch($addons)) { - $name = Strings::sanitizeFilePathItem($p['name']); + $addons = array_keys($this->config->get('addons') ?? []); + foreach ($addons as $addon) { + $name = Strings::sanitizeFilePathItem($addon); if (file_exists(__DIR__ . "/../../addon/$name/lang/$lang/strings.php")) { include __DIR__ . "/../../addon/$name/lang/$lang/strings.php"; } diff --git a/static/settings.config.php b/static/settings.config.php index c7d4b6a9c2..04994bc73e 100644 --- a/static/settings.config.php +++ b/static/settings.config.php @@ -52,10 +52,6 @@ return [ // Enter 0 for no time limit. 'account_abandon_days' => 0, - // addon (Comma-separated list) - // Manual list of addons which are enabled on this system. - 'addon' => '', - // add_missing_posts (boolean) // Checks for missing entries in "post", "post-thread" or "post-thread-user" and creates them 'add_missing_posts' => false, diff --git a/tests/src/Core/Config/Cache/CacheTest.php b/tests/src/Core/Config/Cache/CacheTest.php index 2db6196b7b..024c9c7e3e 100644 --- a/tests/src/Core/Config/Cache/CacheTest.php +++ b/tests/src/Core/Config/Cache/CacheTest.php @@ -410,4 +410,133 @@ class CacheTest extends MockedTest // the newCache entry wasn't set, because we Diff self::assertNull($mergedCache->get('system', 'test_3')); } + + public function dataTestCat() + { + return [ + 'test_with_hashmap' => [ + 'data' => [ + 'test_with_hashmap' => [ + 'notifyall' => [ + 'last_update' => 1671051565, + 'admin' => true, + ], + 'blockbot' => [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + 'config' => [ + 'register_policy' => 2, + 'register_text' => '', + 'sitename' => 'Friendica Social Network23', + 'hostname' => 'friendica.local', + 'private_addons' => false, + ], + 'system' => [ + 'dbclean_expire_conversation' => 90, + ], + ], + 'cat' => 'test_with_hashmap', + 'assertion' => [ + 'notifyall' => [ + 'last_update' => 1671051565, + 'admin' => true, + ], + 'blockbot' => [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + ], + 'test_with_keys' => [ + 'data' => [ + 'test_with_keys' => [ + [ + 'last_update' => 1671051565, + 'admin' => true, + ], + [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + 'config' => [ + 'register_policy' => 2, + 'register_text' => '', + 'sitename' => 'Friendica Social Network23', + 'hostname' => 'friendica.local', + 'private_addons' => false, + ], + 'system' => [ + 'dbclean_expire_conversation' => 90, + ], + ], + 'cat' => 'test_with_keys', + 'assertion' => [ + [ + 'last_update' => 1671051565, + 'admin' => true, + ], + [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + ], + 'test_with_inner_array' => [ + 'data' => [ + 'test_with_inner_array' => [ + 'notifyall' => [ + 'last_update' => 1671051565, + 'admin' => [ + 'yes' => true, + 'no' => 1.5, + ], + ], + 'blogbot' => [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + 'config' => [ + 'register_policy' => 2, + 'register_text' => '', + 'sitename' => 'Friendica Social Network23', + 'hostname' => 'friendica.local', + 'private_addons' => false, + ], + 'system' => [ + 'dbclean_expire_conversation' => 90, + ], + ], + 'cat' => 'test_with_inner_array', + 'assertion' => [ + 'notifyall' => [ + 'last_update' => 1671051565, + 'admin' => [ + 'yes' => true, + 'no' => 1.5, + ], + ], + 'blogbot' => [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + ], + ]; + } + + /** + * Tests that the Cache can return a whole category at once + * + * @dataProvider dataTestCat + */ + public function testGetCategory(array $data, string $category, array $assertion) + { + $cache = new Cache($data); + + self::assertEquals($assertion, $cache->get($category)); + } } diff --git a/tests/src/Core/Config/ConfigTest.php b/tests/src/Core/Config/ConfigTest.php index c85cf3f708..876a0b05b3 100644 --- a/tests/src/Core/Config/ConfigTest.php +++ b/tests/src/Core/Config/ConfigTest.php @@ -403,4 +403,133 @@ class ConfigTest extends MockedTest self::assertFalse($this->testedConfig->set('config', 'test', '123')); self::assertEquals('prio', $this->testedConfig->get('config', 'test', '', true)); } + + + public function dataTestCat() + { + return [ + 'test_with_hashmap' => [ + 'data' => [ + 'test_with_hashmap' => [ + 'notifyall' => [ + 'last_update' => 1671051565, + 'admin' => true, + ], + 'blockbot' => [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + 'config' => [ + 'register_policy' => 2, + 'register_text' => '', + 'sitename' => 'Friendica Social Network23', + 'hostname' => 'friendica.local', + 'private_addons' => false, + ], + 'system' => [ + 'dbclean_expire_conversation' => 90, + ], + ], + 'cat' => 'test_with_hashmap', + 'assertion' => [ + 'notifyall' => [ + 'last_update' => 1671051565, + 'admin' => true, + ], + 'blockbot' => [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + ], + 'test_with_keys' => [ + 'data' => [ + 'test_with_keys' => [ + [ + 'last_update' => 1671051565, + 'admin' => true, + ], + [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + 'config' => [ + 'register_policy' => 2, + 'register_text' => '', + 'sitename' => 'Friendica Social Network23', + 'hostname' => 'friendica.local', + 'private_addons' => false, + ], + 'system' => [ + 'dbclean_expire_conversation' => 90, + ], + ], + 'cat' => 'test_with_keys', + 'assertion' => [ + [ + 'last_update' => 1671051565, + 'admin' => true, + ], + [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + ], + 'test_with_inner_array' => [ + 'data' => [ + 'test_with_inner_array' => [ + 'notifyall' => [ + 'last_update' => 1671051565, + 'admin' => [ + 'yes' => true, + 'no' => 1.5, + ], + ], + 'blogbot' => [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + 'config' => [ + 'register_policy' => 2, + 'register_text' => '', + 'sitename' => 'Friendica Social Network23', + 'hostname' => 'friendica.local', + 'private_addons' => false, + ], + 'system' => [ + 'dbclean_expire_conversation' => 90, + ], + ], + 'cat' => 'test_with_inner_array', + 'assertion' => [ + 'notifyall' => [ + 'last_update' => 1671051565, + 'admin' => [ + 'yes' => true, + 'no' => 1.5, + ], + ], + 'blogbot' => [ + 'last_update' => 1658952852, + 'admin' => true, + ], + ], + ], + ]; + } + + /** + * @dataProvider dataTestCat + */ + public function testGetCategory(array $data, string $category, array $assertion) + { + $this->configCache = new Cache($data); + $config = new Config($this->configFileManager, $this->configCache); + + self::assertEquals($assertion, $config->get($category)); + } } From 01403d15c4ac402ba8b0b5eb084573331275c092 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 4 Jan 2023 01:05:02 +0100 Subject: [PATCH 40/71] sort addon array --- src/Core/Addon.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/Addon.php b/src/Core/Addon.php index be3e70a540..419fb4bd9b 100644 --- a/src/Core/Addon.php +++ b/src/Core/Addon.php @@ -85,6 +85,7 @@ class Addon { $addons_admin = []; $addons = DI::config()->get('addons'); + ksort($addons); foreach ($addons as $name => $data) { if (empty($data['admin'])) { continue; From 4b17d6f3bfecadb35e3148589386a55e94b77356 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 4 Jan 2023 23:12:41 +0100 Subject: [PATCH 41/71] Update src/Core/Addon.php Co-authored-by: Hypolite Petovan --- src/Core/Addon.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Addon.php b/src/Core/Addon.php index 419fb4bd9b..ef2289ca59 100644 --- a/src/Core/Addon.php +++ b/src/Core/Addon.php @@ -170,7 +170,7 @@ class Addon $func(DI::app()); } - DI::config()->set('addons', $addon,[ + DI::config()->set('addons', $addon, [ 'last_update' => $t, 'admin' => function_exists($addon . '_addon_admin'), ]); From b38141211cdfe42ff1713cf45e254a2a05154438 Mon Sep 17 00:00:00 2001 From: Philipp Date: Thu, 5 Jan 2023 02:40:38 +0100 Subject: [PATCH 42/71] Don't wipe `config` or `addon` data --- update.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/update.php b/update.php index 584b8d788c..cf94b5679d 100644 --- a/update.php +++ b/update.php @@ -1192,7 +1192,7 @@ function update_1508() $newConfig->commit(); - return DBA::e("TRUNCATE TABLE `config`") ? Update::SUCCESS : Update::FAILED; + return Update::SUCCESS; } function update_1509() @@ -1210,5 +1210,5 @@ function update_1509() $newConfig->commit(); - return DBA::e("TRUNCATE TABLE `addon`") ? Update::SUCCESS : Update::FAILED; + return Update::SUCCESS; } From dfcfae6bcca54a27b1bf5099d96d54f2bba997d3 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Thu, 5 Jan 2023 10:23:25 -0500 Subject: [PATCH 43/71] Replace $_GET references with $request in Update classes --- src/Module/Update/Community.php | 2 +- src/Module/Update/Network.php | 12 ++++++------ src/Module/Update/Profile.php | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Module/Update/Community.php b/src/Module/Update/Community.php index 05d2e94b06..b1e77c42ba 100644 --- a/src/Module/Update/Community.php +++ b/src/Module/Update/Community.php @@ -38,7 +38,7 @@ class Community extends CommunityModule $this->parseRequest(); $o = ''; - if (!empty($_GET['force']) || !DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'no_auto_update')) { + if (!empty($request['force']) || !DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'no_auto_update')) { $o = DI::conversation()->create(self::getItems(), 'community', true, false, 'commented', DI::userSession()->getLocalUserId()); } diff --git a/src/Module/Update/Network.php b/src/Module/Update/Network.php index 323d22cd5b..f8367e2d91 100644 --- a/src/Module/Update/Network.php +++ b/src/Module/Update/Network.php @@ -31,19 +31,19 @@ class Network extends NetworkModule { protected function rawContent(array $request = []) { - if (!isset($_GET['p']) || !isset($_GET['item'])) { + if (!isset($request['p']) || !isset($request['item'])) { System::exit(); } - $this->parseRequest($_GET); + $this->parseRequest($request); - $profile_uid = intval($_GET['p']); + $profile_uid = intval($request['p']); $o = ''; - if (!DI::pConfig()->get($profile_uid, 'system', 'no_auto_update') || ($_GET['force'] == 1)) { - if (!empty($_GET['item'])) { - $item = Post::selectFirst(['parent'], ['id' => $_GET['item']]); + if (!DI::pConfig()->get($profile_uid, 'system', 'no_auto_update') || ($request['force'] == 1)) { + if (!empty($request['item'])) { + $item = Post::selectFirst(['parent'], ['id' => $request['item']]); $parent = $item['parent'] ?? 0; } else { $parent = 0; diff --git a/src/Module/Update/Profile.php b/src/Module/Update/Profile.php index 9eb814bd01..c2b1951349 100644 --- a/src/Module/Update/Profile.php +++ b/src/Module/Update/Profile.php @@ -39,7 +39,7 @@ class Profile extends BaseModule $a = DI::app(); // Ensure we've got a profile owner if updating. - $a->setProfileOwner((int)($_GET['p'] ?? 0)); + $a->setProfileOwner((int)($request['p'] ?? 0)); if (DI::config()->get('system', 'block_public') && !DI::userSession()->getLocalUserId() && !DI::userSession()->getRemoteContactID($a->getProfileOwner())) { throw new ForbiddenException(); @@ -58,7 +58,7 @@ class Profile extends BaseModule $o = ''; - if (empty($_GET['force']) && DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'no_auto_update')) { + if (empty($request['force']) && DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'no_auto_update')) { System::htmlUpdateExit($o); } @@ -73,9 +73,9 @@ class Profile extends BaseModule AND `visible` AND (NOT `deleted` OR `gravity` = ?) AND `wall` " . $sql_extra, $a->getProfileOwner(), Item::GRAVITY_ACTIVITY]; - if ($_GET['force'] && !empty($_GET['item'])) { + if ($request['force'] && !empty($request['item'])) { // When the parent is provided, we only fetch this - $condition = DBA::mergeConditions($condition, ['parent' => $_GET['item']]); + $condition = DBA::mergeConditions($condition, ['parent' => $request['item']]); } elseif ($is_owner || !$last_updated) { // If the page user is the owner of the page we should query for unseen // items. Otherwise use a timestamp of the last succesful update request. From 3b9bf4d70d658568eb81616855a101d8ed40f95e Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Thu, 5 Jan 2023 10:27:03 -0500 Subject: [PATCH 44/71] Remove system.no_auto_update personal config key and assume default value of true - By popular request, the behavior with false was too perturbating --- mod/update_contact.php | 2 +- src/Module/Settings/Display.php | 4 -- src/Module/Update/Community.php | 2 +- src/Module/Update/Network.php | 72 ++++++++++--------- src/Module/Update/Profile.php | 2 +- view/templates/settings/display.tpl | 1 - .../theme/frio/templates/settings/display.tpl | 1 - 7 files changed, 40 insertions(+), 44 deletions(-) diff --git a/mod/update_contact.php b/mod/update_contact.php index 12a0aed4d6..1a85018e6d 100644 --- a/mod/update_contact.php +++ b/mod/update_contact.php @@ -30,7 +30,7 @@ use Friendica\Model\Contact; function update_contact_content(App $a) { - if (!empty(DI::args()->get(1)) && (!empty($_GET['force']) || !DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'no_auto_update'))) { + if (!empty(DI::args()->get(1)) && !empty($_GET['force'])) { $contact = Contact::getById(DI::args()->get(1), ['id', 'deleted']); if (DBA::isResult($contact) && empty($contact['deleted'])) { DI::page()['aside'] = ''; diff --git a/src/Module/Settings/Display.php b/src/Module/Settings/Display.php index 08b219d0da..cb9ec09ee3 100644 --- a/src/Module/Settings/Display.php +++ b/src/Module/Settings/Display.php @@ -79,7 +79,6 @@ class Display extends BaseSettings $first_day_of_week = !empty($request['first_day_of_week']) ? intval($request['first_day_of_week']) : 0; $calendar_default_view = !empty($request['calendar_default_view']) ? trim($request['calendar_default_view']) : 'month'; $infinite_scroll = !empty($request['infinite_scroll']) ? intval($request['infinite_scroll']) : 0; - $no_auto_update = !empty($request['no_auto_update']) ? intval($request['no_auto_update']) : 0; $enable_smart_threading = !empty($request['enable_smart_threading']) ? intval($request['enable_smart_threading']) : 0; $enable_dislike = !empty($request['enable_dislike']) ? intval($request['enable_dislike']) : 0; $display_resharer = !empty($request['display_resharer']) ? intval($request['display_resharer']) : 0; @@ -113,7 +112,6 @@ class Display extends BaseSettings $this->pConfig->set($uid, 'system', 'itemspage_network' , $itemspage_network); $this->pConfig->set($uid, 'system', 'itemspage_mobile_network', $itemspage_mobile_network); $this->pConfig->set($uid, 'system', 'update_interval' , $browser_update); - $this->pConfig->set($uid, 'system', 'no_auto_update' , $no_auto_update); $this->pConfig->set($uid, 'system', 'no_smilies' , !$enable_smile); $this->pConfig->set($uid, 'system', 'infinite_scroll' , $infinite_scroll); $this->pConfig->set($uid, 'system', 'no_smart_threading' , !$enable_smart_threading); @@ -202,7 +200,6 @@ class Display extends BaseSettings $browser_update = (($browser_update == 0) ? 40 : $browser_update / 1000); // default if not set: 40 seconds } - $no_auto_update = $this->pConfig->get($uid, 'system', 'no_auto_update', 0); $enable_smile = !$this->pConfig->get($uid, 'system', 'no_smilies', 0); $infinite_scroll = $this->pConfig->get($uid, 'system', 'infinite_scroll', 0); $enable_smart_threading = !$this->pConfig->get($uid, 'system', 'no_smart_threading', 0); @@ -265,7 +262,6 @@ class Display extends BaseSettings '$itemspage_network' => ['itemspage_network' , $this->t('Number of items to display per page:'), $itemspage_network, $this->t('Maximum of 100 items')], '$itemspage_mobile_network' => ['itemspage_mobile_network', $this->t('Number of items to display per page when viewed from mobile device:'), $itemspage_mobile_network, $this->t('Maximum of 100 items')], '$ajaxint' => ['browser_update' , $this->t('Update browser every xx seconds'), $browser_update, $this->t('Minimum of 10 seconds. Enter -1 to disable it.')], - '$no_auto_update' => ['no_auto_update' , $this->t('Automatic updates only at the top of the post stream pages'), $no_auto_update, $this->t('Auto update may add new posts at the top of the post stream pages, which can affect the scroll position and perturb normal reading if it happens anywhere else the top of the page.')], '$enable_smile' => ['enable_smile' , $this->t('Display emoticons'), $enable_smile, $this->t('When enabled, emoticons are replaced with matching symbols.')], '$infinite_scroll' => ['infinite_scroll' , $this->t('Infinite scroll'), $infinite_scroll, $this->t('Automatic fetch new items when reaching the page end.')], '$enable_smart_threading' => ['enable_smart_threading' , $this->t('Enable Smart Threading'), $enable_smart_threading, $this->t('Enable the automatic suppression of extraneous thread indentation.')], diff --git a/src/Module/Update/Community.php b/src/Module/Update/Community.php index b1e77c42ba..5b8e479fbf 100644 --- a/src/Module/Update/Community.php +++ b/src/Module/Update/Community.php @@ -38,7 +38,7 @@ class Community extends CommunityModule $this->parseRequest(); $o = ''; - if (!empty($request['force']) || !DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'no_auto_update')) { + if (!empty($request['force'])) { $o = DI::conversation()->create(self::getItems(), 'community', true, false, 'commented', DI::userSession()->getLocalUserId()); } diff --git a/src/Module/Update/Network.php b/src/Module/Update/Network.php index f8367e2d91..2119631565 100644 --- a/src/Module/Update/Network.php +++ b/src/Module/Update/Network.php @@ -41,43 +41,45 @@ class Network extends NetworkModule $o = ''; - if (!DI::pConfig()->get($profile_uid, 'system', 'no_auto_update') || ($request['force'] == 1)) { - if (!empty($request['item'])) { - $item = Post::selectFirst(['parent'], ['id' => $request['item']]); - $parent = $item['parent'] ?? 0; - } else { - $parent = 0; - } - - $conditionFields = []; - if (!empty($parent)) { - // Load only a single thread - $conditionFields['parent'] = $parent; - } elseif (self::$order === 'received') { - // Only load new toplevel posts - $conditionFields['unseen'] = true; - $conditionFields['gravity'] = Item::GRAVITY_PARENT; - } else { - // Load all unseen items - $conditionFields['unseen'] = true; - } - - $params = ['limit' => 100]; - $table = 'network-item-view'; - - $items = self::getItems($table, $params, $conditionFields); - - if (self::$order === 'received') { - $ordering = '`received`'; - } elseif (self::$order === 'created') { - $ordering = '`created`'; - } else { - $ordering = '`commented`'; - } - - $o = DI::conversation()->create($items, 'network', $profile_uid, false, $ordering, DI::userSession()->getLocalUserId()); + if (empty($request['force'])) { + System::htmlUpdateExit($o); } + if (!empty($request['item'])) { + $item = Post::selectFirst(['parent'], ['id' => $request['item']]); + $parent = $item['parent'] ?? 0; + } else { + $parent = 0; + } + + $conditionFields = []; + if (!empty($parent)) { + // Load only a single thread + $conditionFields['parent'] = $parent; + } elseif (self::$order === 'received') { + // Only load new toplevel posts + $conditionFields['unseen'] = true; + $conditionFields['gravity'] = Item::GRAVITY_PARENT; + } else { + // Load all unseen items + $conditionFields['unseen'] = true; + } + + $params = ['limit' => 100]; + $table = 'network-item-view'; + + $items = self::getItems($table, $params, $conditionFields); + + if (self::$order === 'received') { + $ordering = '`received`'; + } elseif (self::$order === 'created') { + $ordering = '`created`'; + } else { + $ordering = '`commented`'; + } + + $o = DI::conversation()->create($items, 'network', $profile_uid, false, $ordering, DI::userSession()->getLocalUserId()); + System::htmlUpdateExit($o); } } diff --git a/src/Module/Update/Profile.php b/src/Module/Update/Profile.php index c2b1951349..80d3b51891 100644 --- a/src/Module/Update/Profile.php +++ b/src/Module/Update/Profile.php @@ -58,7 +58,7 @@ class Profile extends BaseModule $o = ''; - if (empty($request['force']) && DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'no_auto_update')) { + if (empty($request['force'])) { System::htmlUpdateExit($o); } diff --git a/view/templates/settings/display.tpl b/view/templates/settings/display.tpl index e7488b1242..4aab6721b6 100644 --- a/view/templates/settings/display.tpl +++ b/view/templates/settings/display.tpl @@ -13,7 +13,6 @@ {{include file="field_input.tpl" field=$itemspage_mobile_network}} {{include file="field_input.tpl" field=$ajaxint}} - {{include file="field_checkbox.tpl" field=$no_auto_update}} {{include file="field_checkbox.tpl" field=$enable_smile}} {{include file="field_checkbox.tpl" field=$infinite_scroll}} {{include file="field_checkbox.tpl" field=$enable_smart_threading}} diff --git a/view/theme/frio/templates/settings/display.tpl b/view/theme/frio/templates/settings/display.tpl index 19a22eb070..09f23d32af 100644 --- a/view/theme/frio/templates/settings/display.tpl +++ b/view/theme/frio/templates/settings/display.tpl @@ -60,7 +60,6 @@ {{include file="field_input.tpl" field=$itemspage_network}} {{include file="field_input.tpl" field=$itemspage_mobile_network}} {{include file="field_input.tpl" field=$ajaxint}} - {{include file="field_checkbox.tpl" field=$no_auto_update}} {{include file="field_checkbox.tpl" field=$enable_smile}} {{include file="field_checkbox.tpl" field=$infinite_scroll}} {{include file="field_checkbox.tpl" field=$enable_smart_threading}} From 204a478f65915bdba2f403cb72191491b2c7a8d1 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Thu, 5 Jan 2023 10:44:03 -0500 Subject: [PATCH 45/71] Update main translation file after removing a string --- view/lang/C/messages.po | 939 ++++++++++++++++++++-------------------- 1 file changed, 464 insertions(+), 475 deletions(-) diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index 1872ad586d..d10d339fc6 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -1,5 +1,5 @@ # FRIENDICA Distributed Social Network -# Copyright (C) 2010-2022, the Friendica project +# Copyright (C) 2010-2023, the Friendica project # This file is distributed under the same license as the Friendica package. # Mike Macgirvin, 2010 # @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2023.03-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-12-29 20:29+0000\n" +"POT-Creation-Date: 2023-01-05 10:33-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,7 +23,7 @@ msgid "Unable to locate original post." msgstr "" #: mod/item.php:179 mod/item.php:184 mod/item.php:855 mod/message.php:69 -#: mod/message.php:114 mod/notes.php:44 mod/photos.php:157 mod/photos.php:674 +#: mod/message.php:114 mod/notes.php:44 mod/photos.php:158 mod/photos.php:675 #: src/Model/Event.php:522 src/Module/Attach.php:55 src/Module/BaseApi.php:95 #: src/Module/BaseNotifications.php:98 src/Module/BaseSettings.php:52 #: src/Module/Calendar/Event/API.php:88 src/Module/Calendar/Event/Form.php:84 @@ -47,7 +47,7 @@ msgstr "" #: src/Module/Register.php:245 src/Module/Search/Directory.php:37 #: src/Module/Settings/Account.php:50 src/Module/Settings/Account.php:407 #: src/Module/Settings/Delegation.php:41 src/Module/Settings/Delegation.php:69 -#: src/Module/Settings/Display.php:69 src/Module/Settings/Display.php:153 +#: src/Module/Settings/Display.php:69 src/Module/Settings/Display.php:151 #: src/Module/Settings/Profile/Photo/Crop.php:165 #: src/Module/Settings/Profile/Photo/Index.php:111 #: src/Module/Settings/RemoveMe.php:117 src/Module/Settings/UserExport.php:80 @@ -291,16 +291,16 @@ msgstr "" msgid "Insert web link" msgstr "" -#: mod/message.php:203 mod/message.php:360 mod/photos.php:1290 +#: mod/message.php:203 mod/message.php:360 mod/photos.php:1297 #: src/Content/Conversation.php:371 src/Content/Conversation.php:717 #: src/Module/Item/Compose.php:204 src/Module/Post/Edit.php:142 #: src/Module/Profile/UnkMail.php:155 src/Object/Post.php:537 msgid "Please wait" msgstr "" -#: mod/message.php:204 mod/message.php:359 mod/photos.php:707 -#: mod/photos.php:824 mod/photos.php:1096 mod/photos.php:1137 -#: mod/photos.php:1193 mod/photos.php:1267 +#: mod/message.php:204 mod/message.php:359 mod/photos.php:708 +#: mod/photos.php:825 mod/photos.php:1103 mod/photos.php:1144 +#: mod/photos.php:1200 mod/photos.php:1274 #: src/Module/Calendar/Event/Form.php:250 src/Module/Contact/Advanced.php:132 #: src/Module/Contact/Profile.php:327 #: src/Module/Debug/ActivityPubConversion.php:140 @@ -383,7 +383,7 @@ msgstr "" msgid "Save" msgstr "" -#: mod/photos.php:66 mod/photos.php:137 mod/photos.php:582 +#: mod/photos.php:67 mod/photos.php:138 mod/photos.php:583 #: src/Model/Event.php:514 src/Model/Profile.php:234 #: src/Module/Calendar/Export.php:67 src/Module/Calendar/Show.php:74 #: src/Module/DFRN/Poll.php:43 src/Module/Feed.php:65 src/Module/HCard.php:51 @@ -395,100 +395,100 @@ msgstr "" msgid "User not found." msgstr "" -#: mod/photos.php:105 src/Module/BaseProfile.php:68 +#: mod/photos.php:106 src/Module/BaseProfile.php:68 #: src/Module/Profile/Photos.php:399 msgid "Photo Albums" msgstr "" -#: mod/photos.php:106 src/Module/Profile/Photos.php:400 +#: mod/photos.php:107 src/Module/Profile/Photos.php:400 #: src/Module/Profile/Photos.php:420 msgid "Recent Photos" msgstr "" -#: mod/photos.php:108 mod/photos.php:872 src/Module/Profile/Photos.php:402 +#: mod/photos.php:109 mod/photos.php:873 src/Module/Profile/Photos.php:402 #: src/Module/Profile/Photos.php:422 msgid "Upload New Photos" msgstr "" -#: mod/photos.php:126 src/Module/BaseSettings.php:74 +#: mod/photos.php:127 src/Module/BaseSettings.php:74 #: src/Module/Profile/Photos.php:383 msgid "everybody" msgstr "" -#: mod/photos.php:164 +#: mod/photos.php:165 msgid "Contact information unavailable" msgstr "" -#: mod/photos.php:193 +#: mod/photos.php:194 msgid "Album not found." msgstr "" -#: mod/photos.php:247 +#: mod/photos.php:248 msgid "Album successfully deleted" msgstr "" -#: mod/photos.php:249 +#: mod/photos.php:250 msgid "Album was empty." msgstr "" -#: mod/photos.php:281 +#: mod/photos.php:282 msgid "Failed to delete the photo." msgstr "" -#: mod/photos.php:549 +#: mod/photos.php:550 msgid "a photo" msgstr "" -#: mod/photos.php:549 +#: mod/photos.php:550 #, php-format msgid "%1$s was tagged in %2$s by %3$s" msgstr "" -#: mod/photos.php:586 src/Module/Conversation/Community.php:187 +#: mod/photos.php:587 src/Module/Conversation/Community.php:187 #: src/Module/Directory.php:48 src/Module/Profile/Photos.php:315 #: src/Module/Search/Index.php:64 msgid "Public access denied." msgstr "" -#: mod/photos.php:591 +#: mod/photos.php:592 msgid "No photos selected" msgstr "" -#: mod/photos.php:723 +#: mod/photos.php:724 #, php-format msgid "The maximum accepted image size is %s" msgstr "" -#: mod/photos.php:730 +#: mod/photos.php:731 msgid "Upload Photos" msgstr "" -#: mod/photos.php:734 mod/photos.php:820 +#: mod/photos.php:735 mod/photos.php:821 msgid "New album name: " msgstr "" -#: mod/photos.php:735 +#: mod/photos.php:736 msgid "or select existing album:" msgstr "" -#: mod/photos.php:736 +#: mod/photos.php:737 msgid "Do not show a status post for this upload" msgstr "" -#: mod/photos.php:738 mod/photos.php:1092 src/Content/Conversation.php:373 +#: mod/photos.php:739 mod/photos.php:1099 src/Content/Conversation.php:373 #: src/Module/Calendar/Event/Form.php:253 src/Module/Post/Edit.php:179 msgid "Permissions" msgstr "" -#: mod/photos.php:801 +#: mod/photos.php:802 msgid "Do you really want to delete this photo album and all its photos?" msgstr "" -#: mod/photos.php:802 mod/photos.php:825 +#: mod/photos.php:803 mod/photos.php:826 msgid "Delete Album" msgstr "" -#: mod/photos.php:803 mod/photos.php:904 src/Content/Conversation.php:389 +#: mod/photos.php:804 mod/photos.php:905 src/Content/Conversation.php:389 #: src/Module/Contact/Follow.php:172 src/Module/Contact/Revoke.php:109 #: src/Module/Contact/Unfollow.php:126 #: src/Module/Media/Attachment/Browser.php:77 @@ -498,130 +498,130 @@ msgstr "" msgid "Cancel" msgstr "" -#: mod/photos.php:829 +#: mod/photos.php:830 msgid "Edit Album" msgstr "" -#: mod/photos.php:830 +#: mod/photos.php:831 msgid "Drop Album" msgstr "" -#: mod/photos.php:834 +#: mod/photos.php:835 msgid "Show Newest First" msgstr "" -#: mod/photos.php:836 +#: mod/photos.php:837 msgid "Show Oldest First" msgstr "" -#: mod/photos.php:857 src/Module/Profile/Photos.php:370 +#: mod/photos.php:858 src/Module/Profile/Photos.php:370 msgid "View Photo" msgstr "" -#: mod/photos.php:890 +#: mod/photos.php:891 msgid "Permission denied. Access to this item may be restricted." msgstr "" -#: mod/photos.php:892 +#: mod/photos.php:893 msgid "Photo not available" msgstr "" -#: mod/photos.php:902 +#: mod/photos.php:903 msgid "Do you really want to delete this photo?" msgstr "" -#: mod/photos.php:903 mod/photos.php:1097 +#: mod/photos.php:904 mod/photos.php:1104 msgid "Delete Photo" msgstr "" -#: mod/photos.php:995 +#: mod/photos.php:1002 msgid "View photo" msgstr "" -#: mod/photos.php:997 +#: mod/photos.php:1004 msgid "Edit photo" msgstr "" -#: mod/photos.php:998 +#: mod/photos.php:1005 msgid "Delete photo" msgstr "" -#: mod/photos.php:999 +#: mod/photos.php:1006 msgid "Use as profile photo" msgstr "" -#: mod/photos.php:1006 +#: mod/photos.php:1013 msgid "Private Photo" msgstr "" -#: mod/photos.php:1012 +#: mod/photos.php:1019 msgid "View Full Size" msgstr "" -#: mod/photos.php:1065 +#: mod/photos.php:1072 msgid "Tags: " msgstr "" -#: mod/photos.php:1068 +#: mod/photos.php:1075 msgid "[Select tags to remove]" msgstr "" -#: mod/photos.php:1083 +#: mod/photos.php:1090 msgid "New album name" msgstr "" -#: mod/photos.php:1084 +#: mod/photos.php:1091 msgid "Caption" msgstr "" -#: mod/photos.php:1085 +#: mod/photos.php:1092 msgid "Add a Tag" msgstr "" -#: mod/photos.php:1085 +#: mod/photos.php:1092 msgid "Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping" msgstr "" -#: mod/photos.php:1086 +#: mod/photos.php:1093 msgid "Do not rotate" msgstr "" -#: mod/photos.php:1087 +#: mod/photos.php:1094 msgid "Rotate CW (right)" msgstr "" -#: mod/photos.php:1088 +#: mod/photos.php:1095 msgid "Rotate CCW (left)" msgstr "" -#: mod/photos.php:1134 mod/photos.php:1190 mod/photos.php:1264 -#: src/Module/Contact.php:550 src/Module/Item/Compose.php:188 +#: mod/photos.php:1141 mod/photos.php:1197 mod/photos.php:1271 +#: src/Module/Contact.php:557 src/Module/Item/Compose.php:188 #: src/Object/Post.php:983 msgid "This is you" msgstr "" -#: mod/photos.php:1136 mod/photos.php:1192 mod/photos.php:1266 +#: mod/photos.php:1143 mod/photos.php:1199 mod/photos.php:1273 #: src/Object/Post.php:531 src/Object/Post.php:985 msgid "Comment" msgstr "" -#: mod/photos.php:1138 mod/photos.php:1194 mod/photos.php:1268 +#: mod/photos.php:1145 mod/photos.php:1201 mod/photos.php:1275 #: src/Content/Conversation.php:386 src/Module/Calendar/Event/Form.php:248 #: src/Module/Item/Compose.php:199 src/Module/Post/Edit.php:162 #: src/Object/Post.php:997 msgid "Preview" msgstr "" -#: mod/photos.php:1139 src/Content/Conversation.php:341 +#: mod/photos.php:1146 src/Content/Conversation.php:341 #: src/Module/Post/Edit.php:127 src/Object/Post.php:987 msgid "Loading..." msgstr "" -#: mod/photos.php:1225 src/Content/Conversation.php:633 src/Object/Post.php:255 +#: mod/photos.php:1232 src/Content/Conversation.php:633 src/Object/Post.php:255 msgid "Select" msgstr "" -#: mod/photos.php:1226 src/Content/Conversation.php:634 +#: mod/photos.php:1233 src/Content/Conversation.php:634 #: src/Module/Moderation/Users/Active.php:136 #: src/Module/Moderation/Users/Blocked.php:136 #: src/Module/Moderation/Users/Index.php:151 @@ -629,31 +629,31 @@ msgstr "" msgid "Delete" msgstr "" -#: mod/photos.php:1287 src/Object/Post.php:378 +#: mod/photos.php:1294 src/Object/Post.php:378 msgid "Like" msgstr "" -#: mod/photos.php:1288 src/Object/Post.php:378 +#: mod/photos.php:1295 src/Object/Post.php:378 msgid "I like this (toggle)" msgstr "" -#: mod/photos.php:1289 src/Object/Post.php:379 +#: mod/photos.php:1296 src/Object/Post.php:379 msgid "Dislike" msgstr "" -#: mod/photos.php:1291 src/Object/Post.php:379 +#: mod/photos.php:1298 src/Object/Post.php:379 msgid "I don't like this (toggle)" msgstr "" -#: mod/photos.php:1313 +#: mod/photos.php:1320 msgid "Map" msgstr "" -#: src/App.php:472 +#: src/App.php:471 msgid "No system theme config value set." msgstr "" -#: src/App.php:594 +#: src/App.php:593 msgid "Apologies but the website is unavailable at the moment." msgstr "" @@ -695,16 +695,16 @@ msgid "All contacts" msgstr "" #: src/BaseModule.php:432 src/Content/Widget.php:235 src/Core/ACL.php:194 -#: src/Module/Contact.php:371 src/Module/PermissionTooltip.php:122 +#: src/Module/Contact.php:378 src/Module/PermissionTooltip.php:122 #: src/Module/PermissionTooltip.php:144 msgid "Followers" msgstr "" -#: src/BaseModule.php:437 src/Content/Widget.php:236 src/Module/Contact.php:372 +#: src/BaseModule.php:437 src/Content/Widget.php:236 src/Module/Contact.php:379 msgid "Following" msgstr "" -#: src/BaseModule.php:442 src/Content/Widget.php:237 src/Module/Contact.php:373 +#: src/BaseModule.php:442 src/Content/Widget.php:237 src/Module/Contact.php:380 msgid "Mutual friends" msgstr "" @@ -1539,35 +1539,35 @@ msgstr "" msgid "Follow Thread" msgstr "" -#: src/Content/Item.php:387 src/Model/Contact.php:1200 +#: src/Content/Item.php:387 src/Model/Contact.php:1205 msgid "View Status" msgstr "" -#: src/Content/Item.php:388 src/Content/Item.php:406 src/Model/Contact.php:1144 -#: src/Model/Contact.php:1192 src/Model/Contact.php:1201 +#: src/Content/Item.php:388 src/Content/Item.php:406 src/Model/Contact.php:1149 +#: src/Model/Contact.php:1197 src/Model/Contact.php:1206 #: src/Module/Directory.php:157 src/Module/Settings/Profile/Index.php:234 msgid "View Profile" msgstr "" -#: src/Content/Item.php:389 src/Model/Contact.php:1202 +#: src/Content/Item.php:389 src/Model/Contact.php:1207 msgid "View Photos" msgstr "" -#: src/Content/Item.php:390 src/Model/Contact.php:1193 -#: src/Model/Contact.php:1203 +#: src/Content/Item.php:390 src/Model/Contact.php:1198 +#: src/Model/Contact.php:1208 msgid "Network Posts" msgstr "" -#: src/Content/Item.php:391 src/Model/Contact.php:1194 -#: src/Model/Contact.php:1204 +#: src/Content/Item.php:391 src/Model/Contact.php:1199 +#: src/Model/Contact.php:1209 msgid "View Contact" msgstr "" -#: src/Content/Item.php:392 src/Model/Contact.php:1205 +#: src/Content/Item.php:392 src/Model/Contact.php:1210 msgid "Send PM" msgstr "" -#: src/Content/Item.php:393 src/Module/Contact.php:402 +#: src/Content/Item.php:393 src/Module/Contact.php:409 #: src/Module/Contact/Profile.php:348 src/Module/Contact/Profile.php:467 #: src/Module/Moderation/Blocklist/Contact.php:116 #: src/Module/Moderation/Users/Active.php:137 @@ -1575,7 +1575,7 @@ msgstr "" msgid "Block" msgstr "" -#: src/Content/Item.php:394 src/Module/Contact.php:403 +#: src/Content/Item.php:394 src/Module/Contact.php:410 #: src/Module/Contact/Profile.php:349 src/Module/Contact/Profile.php:475 #: src/Module/Notifications/Introductions.php:134 #: src/Module/Notifications/Introductions.php:206 @@ -1588,7 +1588,7 @@ msgid "Languages" msgstr "" #: src/Content/Item.php:403 src/Content/Widget.php:80 -#: src/Model/Contact.php:1195 src/Model/Contact.php:1206 +#: src/Model/Contact.php:1200 src/Model/Contact.php:1211 #: src/Module/Contact/Follow.php:166 view/theme/vier/theme.php:196 msgid "Connect/Follow" msgstr "" @@ -1627,7 +1627,7 @@ msgid "Sign in" msgstr "" #: src/Content/Nav.php:193 src/Module/BaseProfile.php:57 -#: src/Module/Contact.php:437 src/Module/Contact/Profile.php:380 +#: src/Module/Contact.php:444 src/Module/Contact/Profile.php:380 #: src/Module/Settings/TwoFactor/Index.php:119 view/theme/frio/theme.php:236 msgid "Status" msgstr "" @@ -1638,7 +1638,7 @@ msgid "Your posts and conversations" msgstr "" #: src/Content/Nav.php:194 src/Module/BaseProfile.php:49 -#: src/Module/BaseSettings.php:100 src/Module/Contact.php:461 +#: src/Module/BaseSettings.php:100 src/Module/Contact.php:468 #: src/Module/Contact/Profile.php:382 src/Module/Profile/Profile.php:268 #: src/Module/Welcome.php:57 view/theme/frio/theme.php:237 msgid "Profile" @@ -1658,7 +1658,7 @@ msgid "Your photos" msgstr "" #: src/Content/Nav.php:196 src/Module/BaseProfile.php:73 -#: src/Module/BaseProfile.php:76 src/Module/Contact.php:453 +#: src/Module/BaseProfile.php:76 src/Module/Contact.php:460 #: view/theme/frio/theme.php:242 msgid "Media" msgstr "" @@ -1670,7 +1670,7 @@ msgstr "" #: src/Content/Nav.php:197 src/Content/Nav.php:257 #: src/Module/BaseProfile.php:85 src/Module/BaseProfile.php:88 #: src/Module/BaseProfile.php:96 src/Module/BaseProfile.php:99 -#: src/Module/Settings/Display.php:255 view/theme/frio/theme.php:243 +#: src/Module/Settings/Display.php:252 view/theme/frio/theme.php:243 #: view/theme/frio/theme.php:247 msgid "Calendar" msgstr "" @@ -1744,8 +1744,8 @@ msgstr "" #: src/Content/Nav.php:238 src/Content/Nav.php:293 #: src/Content/Text/HTML.php:900 src/Module/BaseProfile.php:127 -#: src/Module/BaseProfile.php:130 src/Module/Contact.php:374 -#: src/Module/Contact.php:468 view/theme/frio/theme.php:250 +#: src/Module/BaseProfile.php:130 src/Module/Contact.php:381 +#: src/Module/Contact.php:475 view/theme/frio/theme.php:250 msgid "Contacts" msgstr "" @@ -1920,8 +1920,8 @@ msgid "" "%2$s %3$s" msgstr "" -#: src/Content/Text/BBCode.php:1257 src/Model/Item.php:3586 -#: src/Model/Item.php:3592 src/Model/Item.php:3593 +#: src/Content/Text/BBCode.php:1257 src/Model/Item.php:3588 +#: src/Model/Item.php:3594 src/Model/Item.php:3595 msgid "Link to source" msgstr "" @@ -1993,7 +1993,7 @@ msgstr "" msgid "Examples: Robert Morgenstein, Fishing" msgstr "" -#: src/Content/Widget.php:82 src/Module/Contact.php:395 +#: src/Content/Widget.php:82 src/Module/Contact.php:402 #: src/Module/Directory.php:96 view/theme/vier/theme.php:198 msgid "Find" msgstr "" @@ -2025,7 +2025,7 @@ msgid "Local Directory" msgstr "" #: src/Content/Widget.php:211 src/Model/Group.php:587 -#: src/Module/Contact.php:358 src/Module/Welcome.php:76 +#: src/Module/Contact.php:365 src/Module/Welcome.php:76 msgid "Groups" msgstr "" @@ -2037,7 +2037,7 @@ msgstr "" msgid "Relationships" msgstr "" -#: src/Content/Widget.php:244 src/Module/Contact.php:310 +#: src/Content/Widget.php:244 src/Module/Contact.php:317 #: src/Module/Group.php:291 msgid "All Contacts" msgstr "" @@ -2081,7 +2081,7 @@ msgstr "" msgid "Organisations" msgstr "" -#: src/Content/Widget.php:523 src/Model/Contact.php:1648 +#: src/Content/Widget.php:523 src/Model/Contact.php:1657 msgid "News" msgstr "" @@ -2162,8 +2162,8 @@ msgstr "" msgid "Network:" msgstr "" -#: src/Content/Widget/VCard.php:111 src/Model/Contact.php:1196 -#: src/Model/Contact.php:1207 src/Model/Profile.php:465 +#: src/Content/Widget/VCard.php:111 src/Model/Contact.php:1201 +#: src/Model/Contact.php:1212 src/Model/Profile.php:465 #: src/Module/Contact/Profile.php:419 msgid "Unfollow" msgstr "" @@ -2547,158 +2547,158 @@ msgstr "" msgid "Could not connect to database." msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:432 -#: src/Module/Settings/Display.php:225 +#: src/Core/L10n.php:408 src/Model/Event.php:432 +#: src/Module/Settings/Display.php:222 msgid "Monday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:433 -#: src/Module/Settings/Display.php:226 +#: src/Core/L10n.php:408 src/Model/Event.php:433 +#: src/Module/Settings/Display.php:223 msgid "Tuesday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:434 -#: src/Module/Settings/Display.php:227 +#: src/Core/L10n.php:408 src/Model/Event.php:434 +#: src/Module/Settings/Display.php:224 msgid "Wednesday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:435 -#: src/Module/Settings/Display.php:228 +#: src/Core/L10n.php:408 src/Model/Event.php:435 +#: src/Module/Settings/Display.php:225 msgid "Thursday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:436 -#: src/Module/Settings/Display.php:229 +#: src/Core/L10n.php:408 src/Model/Event.php:436 +#: src/Module/Settings/Display.php:226 msgid "Friday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:437 -#: src/Module/Settings/Display.php:230 +#: src/Core/L10n.php:408 src/Model/Event.php:437 +#: src/Module/Settings/Display.php:227 msgid "Saturday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:431 -#: src/Module/Settings/Display.php:224 +#: src/Core/L10n.php:408 src/Model/Event.php:431 +#: src/Module/Settings/Display.php:221 msgid "Sunday" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:452 +#: src/Core/L10n.php:412 src/Model/Event.php:452 msgid "January" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:453 +#: src/Core/L10n.php:412 src/Model/Event.php:453 msgid "February" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:454 +#: src/Core/L10n.php:412 src/Model/Event.php:454 msgid "March" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:455 +#: src/Core/L10n.php:412 src/Model/Event.php:455 msgid "April" msgstr "" -#: src/Core/L10n.php:407 src/Core/L10n.php:426 src/Model/Event.php:443 +#: src/Core/L10n.php:412 src/Core/L10n.php:431 src/Model/Event.php:443 msgid "May" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:456 +#: src/Core/L10n.php:412 src/Model/Event.php:456 msgid "June" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:457 +#: src/Core/L10n.php:412 src/Model/Event.php:457 msgid "July" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:458 +#: src/Core/L10n.php:412 src/Model/Event.php:458 msgid "August" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:459 +#: src/Core/L10n.php:412 src/Model/Event.php:459 msgid "September" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:460 +#: src/Core/L10n.php:412 src/Model/Event.php:460 msgid "October" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:461 +#: src/Core/L10n.php:412 src/Model/Event.php:461 msgid "November" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:462 +#: src/Core/L10n.php:412 src/Model/Event.php:462 msgid "December" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:424 +#: src/Core/L10n.php:427 src/Model/Event.php:424 msgid "Mon" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:425 +#: src/Core/L10n.php:427 src/Model/Event.php:425 msgid "Tue" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:426 +#: src/Core/L10n.php:427 src/Model/Event.php:426 msgid "Wed" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:427 +#: src/Core/L10n.php:427 src/Model/Event.php:427 msgid "Thu" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:428 +#: src/Core/L10n.php:427 src/Model/Event.php:428 msgid "Fri" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:429 +#: src/Core/L10n.php:427 src/Model/Event.php:429 msgid "Sat" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:423 +#: src/Core/L10n.php:427 src/Model/Event.php:423 msgid "Sun" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:439 +#: src/Core/L10n.php:431 src/Model/Event.php:439 msgid "Jan" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:440 +#: src/Core/L10n.php:431 src/Model/Event.php:440 msgid "Feb" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:441 +#: src/Core/L10n.php:431 src/Model/Event.php:441 msgid "Mar" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:442 +#: src/Core/L10n.php:431 src/Model/Event.php:442 msgid "Apr" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:444 +#: src/Core/L10n.php:431 src/Model/Event.php:444 msgid "Jun" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:445 +#: src/Core/L10n.php:431 src/Model/Event.php:445 msgid "Jul" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:446 +#: src/Core/L10n.php:431 src/Model/Event.php:446 msgid "Aug" msgstr "" -#: src/Core/L10n.php:426 +#: src/Core/L10n.php:431 msgid "Sep" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:448 +#: src/Core/L10n.php:431 src/Model/Event.php:448 msgid "Oct" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:449 +#: src/Core/L10n.php:431 src/Model/Event.php:449 msgid "Nov" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:450 +#: src/Core/L10n.php:431 src/Model/Event.php:450 msgid "Dec" msgstr "" @@ -2750,17 +2750,17 @@ msgstr "" msgid "%s: executing pre update %d" msgstr "" -#: src/Core/Update.php:194 +#: src/Core/Update.php:198 #, php-format msgid "%s: executing post update %d" msgstr "" -#: src/Core/Update.php:264 +#: src/Core/Update.php:272 #, php-format msgid "Update %s failed. See error logs." msgstr "" -#: src/Core/Update.php:304 +#: src/Core/Update.php:312 #, php-format msgid "" "\n" @@ -2772,16 +2772,16 @@ msgid "" "might be invalid." msgstr "" -#: src/Core/Update.php:310 +#: src/Core/Update.php:318 #, php-format msgid "The error message is\\n[pre]%s[/pre]" msgstr "" -#: src/Core/Update.php:314 src/Core/Update.php:342 +#: src/Core/Update.php:322 src/Core/Update.php:350 msgid "[Friendica Notify] Database update" msgstr "" -#: src/Core/Update.php:336 +#: src/Core/Update.php:344 #, php-format msgid "" "\n" @@ -2826,16 +2826,16 @@ msgstr "" msgid "Errors encountered performing database changes: " msgstr "" -#: src/Database/DBStructure.php:219 +#: src/Database/DBStructure.php:221 msgid "Another database update is currently running." msgstr "" -#: src/Database/DBStructure.php:223 +#: src/Database/DBStructure.php:225 #, php-format msgid "%s: Database update" msgstr "" -#: src/Database/DBStructure.php:480 +#: src/Database/DBStructure.php:482 #, php-format msgid "%s: updating %s table." msgstr "" @@ -2866,82 +2866,82 @@ msgstr "" msgid "Legacy module file not found: %s" msgstr "" -#: src/Model/Contact.php:1213 src/Module/Moderation/Users/Pending.php:102 +#: src/Model/Contact.php:1218 src/Module/Moderation/Users/Pending.php:102 #: src/Module/Notifications/Introductions.php:132 #: src/Module/Notifications/Introductions.php:204 msgid "Approve" msgstr "" -#: src/Model/Contact.php:1644 +#: src/Model/Contact.php:1653 msgid "Organisation" msgstr "" -#: src/Model/Contact.php:1652 +#: src/Model/Contact.php:1661 msgid "Forum" msgstr "" -#: src/Model/Contact.php:2919 +#: src/Model/Contact.php:2928 msgid "Disallowed profile URL." msgstr "" -#: src/Model/Contact.php:2924 src/Module/Friendica.php:82 +#: src/Model/Contact.php:2933 src/Module/Friendica.php:83 msgid "Blocked domain" msgstr "" -#: src/Model/Contact.php:2929 +#: src/Model/Contact.php:2938 msgid "Connect URL missing." msgstr "" -#: src/Model/Contact.php:2938 +#: src/Model/Contact.php:2947 msgid "" "The contact could not be added. Please check the relevant network " "credentials in your Settings -> Social Networks page." msgstr "" -#: src/Model/Contact.php:2956 +#: src/Model/Contact.php:2965 #, php-format msgid "Expected network %s does not match actual network %s" msgstr "" -#: src/Model/Contact.php:2973 +#: src/Model/Contact.php:2982 msgid "The profile address specified does not provide adequate information." msgstr "" -#: src/Model/Contact.php:2975 +#: src/Model/Contact.php:2984 msgid "No compatible communication protocols or feeds were discovered." msgstr "" -#: src/Model/Contact.php:2978 +#: src/Model/Contact.php:2987 msgid "An author or name was not found." msgstr "" -#: src/Model/Contact.php:2981 +#: src/Model/Contact.php:2990 msgid "No browser URL could be matched to this address." msgstr "" -#: src/Model/Contact.php:2984 +#: src/Model/Contact.php:2993 msgid "" "Unable to match @-style Identity Address with a known protocol or email " "contact." msgstr "" -#: src/Model/Contact.php:2985 +#: src/Model/Contact.php:2994 msgid "Use mailto: in front of address to force email check." msgstr "" -#: src/Model/Contact.php:2991 +#: src/Model/Contact.php:3000 msgid "" "The profile address specified belongs to a network which has been disabled " "on this site." msgstr "" -#: src/Model/Contact.php:2996 +#: src/Model/Contact.php:3005 msgid "" "Limited profile. This person will be unable to receive direct/personal " "notifications from you." msgstr "" -#: src/Model/Contact.php:3061 +#: src/Model/Contact.php:3070 msgid "Unable to retrieve contact information." msgstr "" @@ -2973,17 +2973,17 @@ msgid "today" msgstr "" #: src/Model/Event.php:465 src/Module/Calendar/Show.php:129 -#: src/Module/Settings/Display.php:235 src/Util/Temporal.php:353 +#: src/Module/Settings/Display.php:232 src/Util/Temporal.php:353 msgid "month" msgstr "" #: src/Model/Event.php:466 src/Module/Calendar/Show.php:130 -#: src/Module/Settings/Display.php:236 src/Util/Temporal.php:354 +#: src/Module/Settings/Display.php:233 src/Util/Temporal.php:354 msgid "week" msgstr "" #: src/Model/Event.php:467 src/Module/Calendar/Show.php:131 -#: src/Module/Settings/Display.php:237 src/Util/Temporal.php:355 +#: src/Module/Settings/Display.php:234 src/Util/Temporal.php:355 msgid "day" msgstr "" @@ -3112,44 +3112,44 @@ msgstr "" msgid "Content warning: %s" msgstr "" -#: src/Model/Item.php:3498 +#: src/Model/Item.php:3500 msgid "bytes" msgstr "" -#: src/Model/Item.php:3529 +#: src/Model/Item.php:3531 #, php-format msgid "%2$s (%3$d%%, %1$d vote)" msgid_plural "%2$s (%3$d%%, %1$d votes)" msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3531 +#: src/Model/Item.php:3533 #, php-format msgid "%2$s (%1$d vote)" msgid_plural "%2$s (%1$d votes)" msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3536 +#: src/Model/Item.php:3538 #, php-format msgid "%d voter. Poll end: %s" msgid_plural "%d voters. Poll end: %s" msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3538 +#: src/Model/Item.php:3540 #, php-format msgid "%d voter." msgid_plural "%d voters." msgstr[0] "" msgstr[1] "" -#: src/Model/Item.php:3540 +#: src/Model/Item.php:3542 #, php-format msgid "Poll end: %s" msgstr "" -#: src/Model/Item.php:3574 src/Model/Item.php:3575 +#: src/Model/Item.php:3576 src/Model/Item.php:3577 msgid "View on separate page" msgstr "" @@ -3592,7 +3592,7 @@ msgstr "" #: src/Module/Admin/Addons/Details.php:111 src/Module/Admin/Addons/Index.php:67 #: src/Module/Admin/Federation.php:207 src/Module/Admin/Logs/Settings.php:79 #: src/Module/Admin/Logs/View.php:84 src/Module/Admin/Queue.php:72 -#: src/Module/Admin/Site.php:433 src/Module/Admin/Storage.php:138 +#: src/Module/Admin/Site.php:435 src/Module/Admin/Storage.php:138 #: src/Module/Admin/Summary.php:216 src/Module/Admin/Themes/Details.php:90 #: src/Module/Admin/Themes/Index.php:111 src/Module/Admin/Tos.php:77 #: src/Module/Moderation/Users/Create.php:61 @@ -3630,12 +3630,12 @@ msgid "Addon %s failed to install." msgstr "" #: src/Module/Admin/Addons/Index.php:69 src/Module/Admin/Features.php:87 -#: src/Module/Admin/Logs/Settings.php:81 src/Module/Admin/Site.php:436 +#: src/Module/Admin/Logs/Settings.php:81 src/Module/Admin/Site.php:438 #: src/Module/Admin/Themes/Index.php:113 src/Module/Admin/Tos.php:86 #: src/Module/Settings/Account.php:560 src/Module/Settings/Addons.php:81 #: src/Module/Settings/Connectors.php:159 #: src/Module/Settings/Connectors.php:244 -#: src/Module/Settings/Delegation.php:169 src/Module/Settings/Display.php:250 +#: src/Module/Settings/Delegation.php:169 src/Module/Settings/Display.php:247 #: src/Module/Settings/Features.php:76 msgid "Save Settings" msgstr "" @@ -3987,284 +3987,284 @@ msgstr "" msgid "%s is no valid input for maximum image size" msgstr "" -#: src/Module/Admin/Site.php:342 src/Module/Settings/Display.php:171 +#: src/Module/Admin/Site.php:344 src/Module/Settings/Display.php:169 msgid "No special theme for mobile devices" msgstr "" -#: src/Module/Admin/Site.php:359 src/Module/Settings/Display.php:181 +#: src/Module/Admin/Site.php:361 src/Module/Settings/Display.php:179 #, php-format msgid "%s - (Experimental)" msgstr "" -#: src/Module/Admin/Site.php:371 +#: src/Module/Admin/Site.php:373 msgid "No community page" msgstr "" -#: src/Module/Admin/Site.php:372 +#: src/Module/Admin/Site.php:374 msgid "No community page for visitors" msgstr "" -#: src/Module/Admin/Site.php:373 +#: src/Module/Admin/Site.php:375 msgid "Public postings from users of this site" msgstr "" -#: src/Module/Admin/Site.php:374 +#: src/Module/Admin/Site.php:376 msgid "Public postings from the federated network" msgstr "" -#: src/Module/Admin/Site.php:375 +#: src/Module/Admin/Site.php:377 msgid "Public postings from local users and the federated network" msgstr "" -#: src/Module/Admin/Site.php:381 +#: src/Module/Admin/Site.php:383 msgid "Multi user instance" msgstr "" -#: src/Module/Admin/Site.php:404 +#: src/Module/Admin/Site.php:406 msgid "Closed" msgstr "" -#: src/Module/Admin/Site.php:405 +#: src/Module/Admin/Site.php:407 msgid "Requires approval" msgstr "" -#: src/Module/Admin/Site.php:406 +#: src/Module/Admin/Site.php:408 msgid "Open" msgstr "" -#: src/Module/Admin/Site.php:410 src/Module/Install.php:222 +#: src/Module/Admin/Site.php:412 src/Module/Install.php:222 msgid "No SSL policy, links will track page SSL state" msgstr "" -#: src/Module/Admin/Site.php:411 src/Module/Install.php:223 +#: src/Module/Admin/Site.php:413 src/Module/Install.php:223 msgid "Force all links to use SSL" msgstr "" -#: src/Module/Admin/Site.php:412 src/Module/Install.php:224 +#: src/Module/Admin/Site.php:414 src/Module/Install.php:224 msgid "Self-signed certificate, use SSL for local links only (discouraged)" msgstr "" -#: src/Module/Admin/Site.php:416 +#: src/Module/Admin/Site.php:418 msgid "Don't check" msgstr "" -#: src/Module/Admin/Site.php:417 +#: src/Module/Admin/Site.php:419 msgid "check the stable version" msgstr "" -#: src/Module/Admin/Site.php:418 +#: src/Module/Admin/Site.php:420 msgid "check the development version" msgstr "" -#: src/Module/Admin/Site.php:422 +#: src/Module/Admin/Site.php:424 msgid "none" msgstr "" -#: src/Module/Admin/Site.php:423 +#: src/Module/Admin/Site.php:425 msgid "Local contacts" msgstr "" -#: src/Module/Admin/Site.php:424 +#: src/Module/Admin/Site.php:426 msgid "Interactors" msgstr "" -#: src/Module/Admin/Site.php:434 src/Module/BaseAdmin.php:90 +#: src/Module/Admin/Site.php:436 src/Module/BaseAdmin.php:90 msgid "Site" msgstr "" -#: src/Module/Admin/Site.php:435 +#: src/Module/Admin/Site.php:437 msgid "General Information" msgstr "" -#: src/Module/Admin/Site.php:437 +#: src/Module/Admin/Site.php:439 msgid "Republish users to directory" msgstr "" -#: src/Module/Admin/Site.php:438 src/Module/Register.php:152 +#: src/Module/Admin/Site.php:440 src/Module/Register.php:152 msgid "Registration" msgstr "" -#: src/Module/Admin/Site.php:439 +#: src/Module/Admin/Site.php:441 msgid "File upload" msgstr "" -#: src/Module/Admin/Site.php:440 +#: src/Module/Admin/Site.php:442 msgid "Policies" msgstr "" -#: src/Module/Admin/Site.php:441 src/Module/Calendar/Event/Form.php:252 -#: src/Module/Contact.php:478 src/Module/Profile/Profile.php:276 +#: src/Module/Admin/Site.php:443 src/Module/Calendar/Event/Form.php:252 +#: src/Module/Contact.php:485 src/Module/Profile/Profile.php:276 msgid "Advanced" msgstr "" -#: src/Module/Admin/Site.php:442 +#: src/Module/Admin/Site.php:444 msgid "Auto Discovered Contact Directory" msgstr "" -#: src/Module/Admin/Site.php:443 +#: src/Module/Admin/Site.php:445 msgid "Performance" msgstr "" -#: src/Module/Admin/Site.php:444 +#: src/Module/Admin/Site.php:446 msgid "Worker" msgstr "" -#: src/Module/Admin/Site.php:445 +#: src/Module/Admin/Site.php:447 msgid "Message Relay" msgstr "" -#: src/Module/Admin/Site.php:446 +#: src/Module/Admin/Site.php:448 msgid "" "Use the command \"console relay\" in the command line to add or remove " "relays." msgstr "" -#: src/Module/Admin/Site.php:447 +#: src/Module/Admin/Site.php:449 msgid "The system is not subscribed to any relays at the moment." msgstr "" -#: src/Module/Admin/Site.php:448 +#: src/Module/Admin/Site.php:450 msgid "The system is currently subscribed to the following relays:" msgstr "" -#: src/Module/Admin/Site.php:450 +#: src/Module/Admin/Site.php:452 msgid "Relocate Node" msgstr "" -#: src/Module/Admin/Site.php:451 +#: src/Module/Admin/Site.php:453 msgid "" "Relocating your node enables you to change the DNS domain of this node and " "keep all the existing users and posts. This process takes a while and can " "only be started from the relocate console command like this:" msgstr "" -#: src/Module/Admin/Site.php:452 +#: src/Module/Admin/Site.php:454 msgid "(Friendica directory)# bin/console relocate https://newdomain.com" msgstr "" -#: src/Module/Admin/Site.php:456 +#: src/Module/Admin/Site.php:458 msgid "Site name" msgstr "" -#: src/Module/Admin/Site.php:457 +#: src/Module/Admin/Site.php:459 msgid "Sender Email" msgstr "" -#: src/Module/Admin/Site.php:457 +#: src/Module/Admin/Site.php:459 msgid "" "The email address your server shall use to send notification emails from." msgstr "" -#: src/Module/Admin/Site.php:458 +#: src/Module/Admin/Site.php:460 msgid "Name of the system actor" msgstr "" -#: src/Module/Admin/Site.php:458 +#: src/Module/Admin/Site.php:460 msgid "" "Name of the internal system account that is used to perform ActivityPub " "requests. This must be an unused username. If set, this can't be changed " "again." msgstr "" -#: src/Module/Admin/Site.php:459 +#: src/Module/Admin/Site.php:461 msgid "Banner/Logo" msgstr "" -#: src/Module/Admin/Site.php:460 +#: src/Module/Admin/Site.php:462 msgid "Email Banner/Logo" msgstr "" -#: src/Module/Admin/Site.php:461 +#: src/Module/Admin/Site.php:463 msgid "Shortcut icon" msgstr "" -#: src/Module/Admin/Site.php:461 +#: src/Module/Admin/Site.php:463 msgid "Link to an icon that will be used for browsers." msgstr "" -#: src/Module/Admin/Site.php:462 +#: src/Module/Admin/Site.php:464 msgid "Touch icon" msgstr "" -#: src/Module/Admin/Site.php:462 +#: src/Module/Admin/Site.php:464 msgid "Link to an icon that will be used for tablets and mobiles." msgstr "" -#: src/Module/Admin/Site.php:463 +#: src/Module/Admin/Site.php:465 msgid "Additional Info" msgstr "" -#: src/Module/Admin/Site.php:463 +#: src/Module/Admin/Site.php:465 #, php-format msgid "" "For public servers: you can add additional information here that will be " "listed at %s/servers." msgstr "" -#: src/Module/Admin/Site.php:464 +#: src/Module/Admin/Site.php:466 msgid "System language" msgstr "" -#: src/Module/Admin/Site.php:465 +#: src/Module/Admin/Site.php:467 msgid "System theme" msgstr "" -#: src/Module/Admin/Site.php:465 +#: src/Module/Admin/Site.php:467 #, php-format msgid "" "Default system theme - may be over-ridden by user profiles - Change default theme settings" msgstr "" -#: src/Module/Admin/Site.php:466 +#: src/Module/Admin/Site.php:468 msgid "Mobile system theme" msgstr "" -#: src/Module/Admin/Site.php:466 +#: src/Module/Admin/Site.php:468 msgid "Theme for mobile devices" msgstr "" -#: src/Module/Admin/Site.php:467 src/Module/Install.php:232 +#: src/Module/Admin/Site.php:469 src/Module/Install.php:232 msgid "SSL link policy" msgstr "" -#: src/Module/Admin/Site.php:467 src/Module/Install.php:234 +#: src/Module/Admin/Site.php:469 src/Module/Install.php:234 msgid "Determines whether generated links should be forced to use SSL" msgstr "" -#: src/Module/Admin/Site.php:468 +#: src/Module/Admin/Site.php:470 msgid "Force SSL" msgstr "" -#: src/Module/Admin/Site.php:468 +#: src/Module/Admin/Site.php:470 msgid "" "Force all Non-SSL requests to SSL - Attention: on some systems it could lead " "to endless loops." msgstr "" -#: src/Module/Admin/Site.php:469 +#: src/Module/Admin/Site.php:471 msgid "Show help entry from navigation menu" msgstr "" -#: src/Module/Admin/Site.php:469 +#: src/Module/Admin/Site.php:471 msgid "" "Displays the menu entry for the Help pages from the navigation menu. It is " "always accessible by calling /help directly." msgstr "" -#: src/Module/Admin/Site.php:470 +#: src/Module/Admin/Site.php:472 msgid "Single user instance" msgstr "" -#: src/Module/Admin/Site.php:470 +#: src/Module/Admin/Site.php:472 msgid "Make this instance multi-user or single-user for the named user" msgstr "" -#: src/Module/Admin/Site.php:472 +#: src/Module/Admin/Site.php:474 msgid "Maximum image size" msgstr "" -#: src/Module/Admin/Site.php:472 +#: src/Module/Admin/Site.php:474 #, php-format msgid "" "Maximum size in bytes of uploaded images. Default is 0, which means no " @@ -4276,192 +4276,192 @@ msgid "" "to %s (%s byte)" msgstr "" -#: src/Module/Admin/Site.php:476 +#: src/Module/Admin/Site.php:478 msgid "Maximum image length" msgstr "" -#: src/Module/Admin/Site.php:476 +#: src/Module/Admin/Site.php:478 msgid "" "Maximum length in pixels of the longest side of uploaded images. Default is " "-1, which means no limits." msgstr "" -#: src/Module/Admin/Site.php:477 +#: src/Module/Admin/Site.php:479 msgid "JPEG image quality" msgstr "" -#: src/Module/Admin/Site.php:477 +#: src/Module/Admin/Site.php:479 msgid "" "Uploaded JPEGS will be saved at this quality setting [0-100]. Default is " "100, which is full quality." msgstr "" -#: src/Module/Admin/Site.php:479 +#: src/Module/Admin/Site.php:481 msgid "Register policy" msgstr "" -#: src/Module/Admin/Site.php:480 +#: src/Module/Admin/Site.php:482 msgid "Maximum Daily Registrations" msgstr "" -#: src/Module/Admin/Site.php:480 +#: src/Module/Admin/Site.php:482 msgid "" "If registration is permitted above, this sets the maximum number of new user " "registrations to accept per day. If register is set to closed, this setting " "has no effect." msgstr "" -#: src/Module/Admin/Site.php:481 +#: src/Module/Admin/Site.php:483 msgid "Register text" msgstr "" -#: src/Module/Admin/Site.php:481 +#: src/Module/Admin/Site.php:483 msgid "" "Will be displayed prominently on the registration page. You can use BBCode " "here." msgstr "" -#: src/Module/Admin/Site.php:482 +#: src/Module/Admin/Site.php:484 msgid "Forbidden Nicknames" msgstr "" -#: src/Module/Admin/Site.php:482 +#: src/Module/Admin/Site.php:484 msgid "" "Comma separated list of nicknames that are forbidden from registration. " "Preset is a list of role names according RFC 2142." msgstr "" -#: src/Module/Admin/Site.php:483 +#: src/Module/Admin/Site.php:485 msgid "Accounts abandoned after x days" msgstr "" -#: src/Module/Admin/Site.php:483 +#: src/Module/Admin/Site.php:485 msgid "" "Will not waste system resources polling external sites for abandonded " "accounts. Enter 0 for no time limit." msgstr "" -#: src/Module/Admin/Site.php:484 +#: src/Module/Admin/Site.php:486 msgid "Allowed friend domains" msgstr "" -#: src/Module/Admin/Site.php:484 +#: src/Module/Admin/Site.php:486 msgid "" "Comma separated list of domains which are allowed to establish friendships " "with this site. Wildcards are accepted. Empty to allow any domains" msgstr "" -#: src/Module/Admin/Site.php:485 +#: src/Module/Admin/Site.php:487 msgid "Allowed email domains" msgstr "" -#: src/Module/Admin/Site.php:485 +#: src/Module/Admin/Site.php:487 msgid "" "Comma separated list of domains which are allowed in email addresses for " "registrations to this site. Wildcards are accepted. Empty to allow any " "domains" msgstr "" -#: src/Module/Admin/Site.php:486 +#: src/Module/Admin/Site.php:488 msgid "No OEmbed rich content" msgstr "" -#: src/Module/Admin/Site.php:486 +#: src/Module/Admin/Site.php:488 msgid "" "Don't show the rich content (e.g. embedded PDF), except from the domains " "listed below." msgstr "" -#: src/Module/Admin/Site.php:487 +#: src/Module/Admin/Site.php:489 msgid "Trusted third-party domains" msgstr "" -#: src/Module/Admin/Site.php:487 +#: src/Module/Admin/Site.php:489 msgid "" "Comma separated list of domains from which content is allowed to be embedded " "in posts like with OEmbed. All sub-domains of the listed domains are allowed " "as well." msgstr "" -#: src/Module/Admin/Site.php:488 +#: src/Module/Admin/Site.php:490 msgid "Block public" msgstr "" -#: src/Module/Admin/Site.php:488 +#: src/Module/Admin/Site.php:490 msgid "" "Check to block public access to all otherwise public personal pages on this " "site unless you are currently logged in." msgstr "" -#: src/Module/Admin/Site.php:489 +#: src/Module/Admin/Site.php:491 msgid "Force publish" msgstr "" -#: src/Module/Admin/Site.php:489 +#: src/Module/Admin/Site.php:491 msgid "" "Check to force all profiles on this site to be listed in the site directory." msgstr "" -#: src/Module/Admin/Site.php:489 +#: src/Module/Admin/Site.php:491 msgid "Enabling this may violate privacy laws like the GDPR" msgstr "" -#: src/Module/Admin/Site.php:490 +#: src/Module/Admin/Site.php:492 msgid "Global directory URL" msgstr "" -#: src/Module/Admin/Site.php:490 +#: src/Module/Admin/Site.php:492 msgid "" "URL to the global directory. If this is not set, the global directory is " "completely unavailable to the application." msgstr "" -#: src/Module/Admin/Site.php:491 +#: src/Module/Admin/Site.php:493 msgid "Private posts by default for new users" msgstr "" -#: src/Module/Admin/Site.php:491 +#: src/Module/Admin/Site.php:493 msgid "" "Set default post permissions for all new members to the default privacy " "group rather than public." msgstr "" -#: src/Module/Admin/Site.php:492 +#: src/Module/Admin/Site.php:494 msgid "Don't include post content in email notifications" msgstr "" -#: src/Module/Admin/Site.php:492 +#: src/Module/Admin/Site.php:494 msgid "" "Don't include the content of a post/comment/private message/etc. in the " "email notifications that are sent out from this site, as a privacy measure." msgstr "" -#: src/Module/Admin/Site.php:493 +#: src/Module/Admin/Site.php:495 msgid "Disallow public access to addons listed in the apps menu." msgstr "" -#: src/Module/Admin/Site.php:493 +#: src/Module/Admin/Site.php:495 msgid "" "Checking this box will restrict addons listed in the apps menu to members " "only." msgstr "" -#: src/Module/Admin/Site.php:494 +#: src/Module/Admin/Site.php:496 msgid "Don't embed private images in posts" msgstr "" -#: src/Module/Admin/Site.php:494 +#: src/Module/Admin/Site.php:496 msgid "" "Don't replace locally-hosted private photos in posts with an embedded copy " "of the image. This means that contacts who receive posts containing private " "photos will have to authenticate and load each image, which may take a while." msgstr "" -#: src/Module/Admin/Site.php:495 +#: src/Module/Admin/Site.php:497 msgid "Explicit Content" msgstr "" -#: src/Module/Admin/Site.php:495 +#: src/Module/Admin/Site.php:497 msgid "" "Set this to announce that your node is used mostly for explicit content that " "might not be suited for minors. This information will be published in the " @@ -4470,267 +4470,267 @@ msgid "" "will be shown at the user registration page." msgstr "" -#: src/Module/Admin/Site.php:496 +#: src/Module/Admin/Site.php:498 msgid "Proxify external content" msgstr "" -#: src/Module/Admin/Site.php:496 +#: src/Module/Admin/Site.php:498 msgid "" "Route external content via the proxy functionality. This is used for example " "for some OEmbed accesses and in some other rare cases." msgstr "" -#: src/Module/Admin/Site.php:497 +#: src/Module/Admin/Site.php:499 msgid "Cache contact avatars" msgstr "" -#: src/Module/Admin/Site.php:497 +#: src/Module/Admin/Site.php:499 msgid "" "Locally store the avatar pictures of the contacts. This uses a lot of " "storage space but it increases the performance." msgstr "" -#: src/Module/Admin/Site.php:498 +#: src/Module/Admin/Site.php:500 msgid "Allow Users to set remote_self" msgstr "" -#: src/Module/Admin/Site.php:498 +#: src/Module/Admin/Site.php:500 msgid "" "With checking this, every user is allowed to mark every contact as a " "remote_self in the repair contact dialog. Setting this flag on a contact " "causes mirroring every posting of that contact in the users stream." msgstr "" -#: src/Module/Admin/Site.php:499 +#: src/Module/Admin/Site.php:501 msgid "Enable multiple registrations" msgstr "" -#: src/Module/Admin/Site.php:499 +#: src/Module/Admin/Site.php:501 msgid "Enable users to register additional accounts for use as pages." msgstr "" -#: src/Module/Admin/Site.php:500 +#: src/Module/Admin/Site.php:502 msgid "Enable OpenID" msgstr "" -#: src/Module/Admin/Site.php:500 +#: src/Module/Admin/Site.php:502 msgid "Enable OpenID support for registration and logins." msgstr "" -#: src/Module/Admin/Site.php:501 +#: src/Module/Admin/Site.php:503 msgid "Enable Fullname check" msgstr "" -#: src/Module/Admin/Site.php:501 +#: src/Module/Admin/Site.php:503 msgid "" "Enable check to only allow users to register with a space between the first " "name and the last name in their full name." msgstr "" -#: src/Module/Admin/Site.php:502 +#: src/Module/Admin/Site.php:504 msgid "Email administrators on new registration" msgstr "" -#: src/Module/Admin/Site.php:502 +#: src/Module/Admin/Site.php:504 msgid "" "If enabled and the system is set to an open registration, an email for each " "new registration is sent to the administrators." msgstr "" -#: src/Module/Admin/Site.php:503 +#: src/Module/Admin/Site.php:505 msgid "Community pages for visitors" msgstr "" -#: src/Module/Admin/Site.php:503 +#: src/Module/Admin/Site.php:505 msgid "" "Which community pages should be available for visitors. Local users always " "see both pages." msgstr "" -#: src/Module/Admin/Site.php:504 +#: src/Module/Admin/Site.php:506 msgid "Posts per user on community page" msgstr "" -#: src/Module/Admin/Site.php:504 +#: src/Module/Admin/Site.php:506 msgid "" "The maximum number of posts per user on the community page. (Not valid for " "\"Global Community\")" msgstr "" -#: src/Module/Admin/Site.php:506 +#: src/Module/Admin/Site.php:508 msgid "Enable Mail support" msgstr "" -#: src/Module/Admin/Site.php:506 +#: src/Module/Admin/Site.php:508 msgid "" "Enable built-in mail support to poll IMAP folders and to reply via mail." msgstr "" -#: src/Module/Admin/Site.php:507 +#: src/Module/Admin/Site.php:509 msgid "" "Mail support can't be enabled because the PHP IMAP module is not installed." msgstr "" -#: src/Module/Admin/Site.php:508 +#: src/Module/Admin/Site.php:510 msgid "Enable OStatus support" msgstr "" -#: src/Module/Admin/Site.php:508 +#: src/Module/Admin/Site.php:510 msgid "" "Enable built-in OStatus (StatusNet, GNU Social etc.) compatibility. All " "communications in OStatus are public." msgstr "" -#: src/Module/Admin/Site.php:510 +#: src/Module/Admin/Site.php:512 msgid "" "Diaspora support can't be enabled because Friendica was installed into a sub " "directory." msgstr "" -#: src/Module/Admin/Site.php:511 +#: src/Module/Admin/Site.php:513 msgid "Enable Diaspora support" msgstr "" -#: src/Module/Admin/Site.php:511 +#: src/Module/Admin/Site.php:513 msgid "" "Enable built-in Diaspora network compatibility for communicating with " "diaspora servers." msgstr "" -#: src/Module/Admin/Site.php:512 +#: src/Module/Admin/Site.php:514 msgid "Verify SSL" msgstr "" -#: src/Module/Admin/Site.php:512 +#: src/Module/Admin/Site.php:514 msgid "" "If you wish, you can turn on strict certificate checking. This will mean you " "cannot connect (at all) to self-signed SSL sites." msgstr "" -#: src/Module/Admin/Site.php:513 +#: src/Module/Admin/Site.php:515 msgid "Proxy user" msgstr "" -#: src/Module/Admin/Site.php:513 +#: src/Module/Admin/Site.php:515 msgid "User name for the proxy server." msgstr "" -#: src/Module/Admin/Site.php:514 +#: src/Module/Admin/Site.php:516 msgid "Proxy URL" msgstr "" -#: src/Module/Admin/Site.php:514 +#: src/Module/Admin/Site.php:516 msgid "" "If you want to use a proxy server that Friendica should use to connect to " "the network, put the URL of the proxy here." msgstr "" -#: src/Module/Admin/Site.php:515 +#: src/Module/Admin/Site.php:517 msgid "Network timeout" msgstr "" -#: src/Module/Admin/Site.php:515 +#: src/Module/Admin/Site.php:517 msgid "Value is in seconds. Set to 0 for unlimited (not recommended)." msgstr "" -#: src/Module/Admin/Site.php:516 +#: src/Module/Admin/Site.php:518 msgid "Maximum Load Average" msgstr "" -#: src/Module/Admin/Site.php:516 +#: src/Module/Admin/Site.php:518 #, php-format msgid "" "Maximum system load before delivery and poll processes are deferred - " "default %d." msgstr "" -#: src/Module/Admin/Site.php:517 +#: src/Module/Admin/Site.php:519 msgid "Minimal Memory" msgstr "" -#: src/Module/Admin/Site.php:517 +#: src/Module/Admin/Site.php:519 msgid "" "Minimal free memory in MB for the worker. Needs access to /proc/meminfo - " "default 0 (deactivated)." msgstr "" -#: src/Module/Admin/Site.php:518 +#: src/Module/Admin/Site.php:520 msgid "Periodically optimize tables" msgstr "" -#: src/Module/Admin/Site.php:518 +#: src/Module/Admin/Site.php:520 msgid "Periodically optimize tables like the cache and the workerqueue" msgstr "" -#: src/Module/Admin/Site.php:520 +#: src/Module/Admin/Site.php:522 msgid "Discover followers/followings from contacts" msgstr "" -#: src/Module/Admin/Site.php:520 +#: src/Module/Admin/Site.php:522 msgid "" "If enabled, contacts are checked for their followers and following contacts." msgstr "" -#: src/Module/Admin/Site.php:521 +#: src/Module/Admin/Site.php:523 msgid "None - deactivated" msgstr "" -#: src/Module/Admin/Site.php:522 +#: src/Module/Admin/Site.php:524 msgid "" "Local contacts - contacts of our local contacts are discovered for their " "followers/followings." msgstr "" -#: src/Module/Admin/Site.php:523 +#: src/Module/Admin/Site.php:525 msgid "" "Interactors - contacts of our local contacts and contacts who interacted on " "locally visible postings are discovered for their followers/followings." msgstr "" -#: src/Module/Admin/Site.php:525 +#: src/Module/Admin/Site.php:527 msgid "Synchronize the contacts with the directory server" msgstr "" -#: src/Module/Admin/Site.php:525 +#: src/Module/Admin/Site.php:527 msgid "" "if enabled, the system will check periodically for new contacts on the " "defined directory server." msgstr "" -#: src/Module/Admin/Site.php:527 +#: src/Module/Admin/Site.php:529 msgid "Days between requery" msgstr "" -#: src/Module/Admin/Site.php:527 +#: src/Module/Admin/Site.php:529 msgid "Number of days after which a server is requeried for his contacts." msgstr "" -#: src/Module/Admin/Site.php:528 +#: src/Module/Admin/Site.php:530 msgid "Discover contacts from other servers" msgstr "" -#: src/Module/Admin/Site.php:528 +#: src/Module/Admin/Site.php:530 msgid "" "Periodically query other servers for contacts. The system queries Friendica, " "Mastodon and Hubzilla servers." msgstr "" -#: src/Module/Admin/Site.php:529 +#: src/Module/Admin/Site.php:531 msgid "Search the local directory" msgstr "" -#: src/Module/Admin/Site.php:529 +#: src/Module/Admin/Site.php:531 msgid "" "Search the local directory instead of the global directory. When searching " "locally, every search will be executed on the global directory in the " "background. This improves the search results when the search is repeated." msgstr "" -#: src/Module/Admin/Site.php:531 +#: src/Module/Admin/Site.php:533 msgid "Publish server information" msgstr "" -#: src/Module/Admin/Site.php:531 +#: src/Module/Admin/Site.php:533 msgid "" "If enabled, general server and usage data will be published. The data " "contains the name and version of the server, number of users with public " @@ -4738,50 +4738,50 @@ msgid "" "href=\"http://the-federation.info/\">the-federation.info for details." msgstr "" -#: src/Module/Admin/Site.php:533 +#: src/Module/Admin/Site.php:535 msgid "Check upstream version" msgstr "" -#: src/Module/Admin/Site.php:533 +#: src/Module/Admin/Site.php:535 msgid "" "Enables checking for new Friendica versions at github. If there is a new " "version, you will be informed in the admin panel overview." msgstr "" -#: src/Module/Admin/Site.php:534 +#: src/Module/Admin/Site.php:536 msgid "Suppress Tags" msgstr "" -#: src/Module/Admin/Site.php:534 +#: src/Module/Admin/Site.php:536 msgid "Suppress showing a list of hashtags at the end of the posting." msgstr "" -#: src/Module/Admin/Site.php:535 +#: src/Module/Admin/Site.php:537 msgid "Clean database" msgstr "" -#: src/Module/Admin/Site.php:535 +#: src/Module/Admin/Site.php:537 msgid "" "Remove old remote items, orphaned database records and old content from some " "other helper tables." msgstr "" -#: src/Module/Admin/Site.php:536 +#: src/Module/Admin/Site.php:538 msgid "Lifespan of remote items" msgstr "" -#: src/Module/Admin/Site.php:536 +#: src/Module/Admin/Site.php:538 msgid "" "When the database cleanup is enabled, this defines the days after which " "remote items will be deleted. Own items, and marked or filed items are " "always kept. 0 disables this behaviour." msgstr "" -#: src/Module/Admin/Site.php:537 +#: src/Module/Admin/Site.php:539 msgid "Lifespan of unclaimed items" msgstr "" -#: src/Module/Admin/Site.php:537 +#: src/Module/Admin/Site.php:539 msgid "" "When the database cleanup is enabled, this defines the days after which " "unclaimed remote items (mostly content from the relay) will be deleted. " @@ -4789,144 +4789,144 @@ msgid "" "items if set to 0." msgstr "" -#: src/Module/Admin/Site.php:538 +#: src/Module/Admin/Site.php:540 msgid "Lifespan of raw conversation data" msgstr "" -#: src/Module/Admin/Site.php:538 +#: src/Module/Admin/Site.php:540 msgid "" "The conversation data is used for ActivityPub and OStatus, as well as for " "debug purposes. It should be safe to remove it after 14 days, default is 90 " "days." msgstr "" -#: src/Module/Admin/Site.php:539 +#: src/Module/Admin/Site.php:541 msgid "Maximum numbers of comments per post" msgstr "" -#: src/Module/Admin/Site.php:539 +#: src/Module/Admin/Site.php:541 msgid "How much comments should be shown for each post? Default value is 100." msgstr "" -#: src/Module/Admin/Site.php:540 +#: src/Module/Admin/Site.php:542 msgid "Maximum numbers of comments per post on the display page" msgstr "" -#: src/Module/Admin/Site.php:540 +#: src/Module/Admin/Site.php:542 msgid "" "How many comments should be shown on the single view for each post? Default " "value is 1000." msgstr "" -#: src/Module/Admin/Site.php:541 +#: src/Module/Admin/Site.php:543 msgid "Temp path" msgstr "" -#: src/Module/Admin/Site.php:541 +#: src/Module/Admin/Site.php:543 msgid "" "If you have a restricted system where the webserver can't access the system " "temp path, enter another path here." msgstr "" -#: src/Module/Admin/Site.php:542 +#: src/Module/Admin/Site.php:544 msgid "Only search in tags" msgstr "" -#: src/Module/Admin/Site.php:542 +#: src/Module/Admin/Site.php:544 msgid "On large systems the text search can slow down the system extremely." msgstr "" -#: src/Module/Admin/Site.php:543 +#: src/Module/Admin/Site.php:545 msgid "Generate counts per contact group when calculating network count" msgstr "" -#: src/Module/Admin/Site.php:543 +#: src/Module/Admin/Site.php:545 msgid "" "On systems with users that heavily use contact groups the query can be very " "expensive." msgstr "" -#: src/Module/Admin/Site.php:545 +#: src/Module/Admin/Site.php:547 msgid "Maximum number of parallel workers" msgstr "" -#: src/Module/Admin/Site.php:545 +#: src/Module/Admin/Site.php:547 #, php-format msgid "" "On shared hosters set this to %d. On larger systems, values of %d are great. " "Default value is %d." msgstr "" -#: src/Module/Admin/Site.php:546 +#: src/Module/Admin/Site.php:548 msgid "Enable fastlane" msgstr "" -#: src/Module/Admin/Site.php:546 +#: src/Module/Admin/Site.php:548 msgid "" "When enabed, the fastlane mechanism starts an additional worker if processes " "with higher priority are blocked by processes of lower priority." msgstr "" -#: src/Module/Admin/Site.php:548 +#: src/Module/Admin/Site.php:550 msgid "Direct relay transfer" msgstr "" -#: src/Module/Admin/Site.php:548 +#: src/Module/Admin/Site.php:550 msgid "" "Enables the direct transfer to other servers without using the relay servers" msgstr "" -#: src/Module/Admin/Site.php:549 +#: src/Module/Admin/Site.php:551 msgid "Relay scope" msgstr "" -#: src/Module/Admin/Site.php:549 +#: src/Module/Admin/Site.php:551 msgid "" "Can be \"all\" or \"tags\". \"all\" means that every public post should be " "received. \"tags\" means that only posts with selected tags should be " "received." msgstr "" -#: src/Module/Admin/Site.php:549 src/Module/Contact/Profile.php:274 +#: src/Module/Admin/Site.php:551 src/Module/Contact/Profile.php:274 #: src/Module/Settings/TwoFactor/Index.php:125 msgid "Disabled" msgstr "" -#: src/Module/Admin/Site.php:549 +#: src/Module/Admin/Site.php:551 msgid "all" msgstr "" -#: src/Module/Admin/Site.php:549 +#: src/Module/Admin/Site.php:551 msgid "tags" msgstr "" -#: src/Module/Admin/Site.php:550 +#: src/Module/Admin/Site.php:552 msgid "Server tags" msgstr "" -#: src/Module/Admin/Site.php:550 +#: src/Module/Admin/Site.php:552 msgid "Comma separated list of tags for the \"tags\" subscription." msgstr "" -#: src/Module/Admin/Site.php:551 +#: src/Module/Admin/Site.php:553 msgid "Deny Server tags" msgstr "" -#: src/Module/Admin/Site.php:551 +#: src/Module/Admin/Site.php:553 msgid "Comma separated list of tags that are rejected." msgstr "" -#: src/Module/Admin/Site.php:552 +#: src/Module/Admin/Site.php:554 msgid "Allow user tags" msgstr "" -#: src/Module/Admin/Site.php:552 +#: src/Module/Admin/Site.php:554 msgid "" "If enabled, the tags from the saved searches will used for the \"tags\" " "subscription in addition to the \"relay_server_tags\"." msgstr "" -#: src/Module/Admin/Site.php:555 +#: src/Module/Admin/Site.php:557 msgid "Start Relocation" msgstr "" @@ -5392,11 +5392,11 @@ msgstr "" msgid "Item Source" msgstr "" -#: src/Module/BaseProfile.php:52 src/Module/Contact.php:464 +#: src/Module/BaseProfile.php:52 src/Module/Contact.php:471 msgid "Profile Details" msgstr "" -#: src/Module/BaseProfile.php:60 src/Module/Contact.php:448 +#: src/Module/BaseProfile.php:60 src/Module/Contact.php:455 #: src/Module/Contact/Follow.php:191 src/Module/Contact/Unfollow.php:138 msgid "Status Messages and Posts" msgstr "" @@ -5580,82 +5580,82 @@ msgstr "" msgid "Create New Event" msgstr "" -#: src/Module/Calendar/Show.php:132 src/Module/Settings/Display.php:238 +#: src/Module/Calendar/Show.php:132 src/Module/Settings/Display.php:235 msgid "list" msgstr "" -#: src/Module/Contact.php:89 +#: src/Module/Contact.php:92 #, php-format msgid "%d contact edited." msgid_plural "%d contacts edited." msgstr[0] "" msgstr[1] "" -#: src/Module/Contact.php:313 +#: src/Module/Contact.php:320 msgid "Show all contacts" msgstr "" -#: src/Module/Contact.php:318 src/Module/Contact.php:378 +#: src/Module/Contact.php:325 src/Module/Contact.php:385 #: src/Module/Moderation/BaseUsers.php:85 msgid "Pending" msgstr "" -#: src/Module/Contact.php:321 +#: src/Module/Contact.php:328 msgid "Only show pending contacts" msgstr "" -#: src/Module/Contact.php:326 src/Module/Contact.php:379 +#: src/Module/Contact.php:333 src/Module/Contact.php:386 #: src/Module/Moderation/BaseUsers.php:93 msgid "Blocked" msgstr "" -#: src/Module/Contact.php:329 +#: src/Module/Contact.php:336 msgid "Only show blocked contacts" msgstr "" -#: src/Module/Contact.php:334 src/Module/Contact.php:381 +#: src/Module/Contact.php:341 src/Module/Contact.php:388 #: src/Object/Post.php:338 msgid "Ignored" msgstr "" -#: src/Module/Contact.php:337 +#: src/Module/Contact.php:344 msgid "Only show ignored contacts" msgstr "" -#: src/Module/Contact.php:342 src/Module/Contact.php:382 +#: src/Module/Contact.php:349 src/Module/Contact.php:389 msgid "Archived" msgstr "" -#: src/Module/Contact.php:345 +#: src/Module/Contact.php:352 msgid "Only show archived contacts" msgstr "" -#: src/Module/Contact.php:350 src/Module/Contact.php:380 +#: src/Module/Contact.php:357 src/Module/Contact.php:387 msgid "Hidden" msgstr "" -#: src/Module/Contact.php:353 +#: src/Module/Contact.php:360 msgid "Only show hidden contacts" msgstr "" -#: src/Module/Contact.php:361 +#: src/Module/Contact.php:368 msgid "Organize your contact groups" msgstr "" -#: src/Module/Contact.php:393 +#: src/Module/Contact.php:400 msgid "Search your contacts" msgstr "" -#: src/Module/Contact.php:394 src/Module/Search/Index.php:206 +#: src/Module/Contact.php:401 src/Module/Search/Index.php:206 #, php-format msgid "Results for: %s" msgstr "" -#: src/Module/Contact.php:401 +#: src/Module/Contact.php:408 msgid "Update" msgstr "" -#: src/Module/Contact.php:402 src/Module/Contact/Profile.php:348 +#: src/Module/Contact.php:409 src/Module/Contact/Profile.php:348 #: src/Module/Contact/Profile.php:467 #: src/Module/Moderation/Blocklist/Contact.php:117 #: src/Module/Moderation/Users/Blocked.php:138 @@ -5663,56 +5663,56 @@ msgstr "" msgid "Unblock" msgstr "" -#: src/Module/Contact.php:403 src/Module/Contact/Profile.php:349 +#: src/Module/Contact.php:410 src/Module/Contact/Profile.php:349 #: src/Module/Contact/Profile.php:475 msgid "Unignore" msgstr "" -#: src/Module/Contact.php:405 +#: src/Module/Contact.php:412 msgid "Batch Actions" msgstr "" -#: src/Module/Contact.php:440 +#: src/Module/Contact.php:447 msgid "Conversations started by this contact" msgstr "" -#: src/Module/Contact.php:445 +#: src/Module/Contact.php:452 msgid "Posts and Comments" msgstr "" -#: src/Module/Contact.php:456 +#: src/Module/Contact.php:463 msgid "Posts containing media objects" msgstr "" -#: src/Module/Contact.php:471 +#: src/Module/Contact.php:478 msgid "View all known contacts" msgstr "" -#: src/Module/Contact.php:481 +#: src/Module/Contact.php:488 msgid "Advanced Contact Settings" msgstr "" -#: src/Module/Contact.php:517 +#: src/Module/Contact.php:524 msgid "Mutual Friendship" msgstr "" -#: src/Module/Contact.php:521 +#: src/Module/Contact.php:528 msgid "is a fan of yours" msgstr "" -#: src/Module/Contact.php:525 +#: src/Module/Contact.php:532 msgid "you are a fan of" msgstr "" -#: src/Module/Contact.php:543 +#: src/Module/Contact.php:550 msgid "Pending outgoing contact request" msgstr "" -#: src/Module/Contact.php:545 +#: src/Module/Contact.php:552 msgid "Pending incoming contact request" msgstr "" -#: src/Module/Contact.php:558 src/Module/Contact/Profile.php:334 +#: src/Module/Contact.php:565 src/Module/Contact/Profile.php:334 #, php-format msgid "Visit %s's profile [%s]" msgstr "" @@ -6633,55 +6633,55 @@ msgstr "" msgid "Suggest a friend for %s" msgstr "" -#: src/Module/Friendica.php:63 +#: src/Module/Friendica.php:64 msgid "Installed addons/apps:" msgstr "" -#: src/Module/Friendica.php:68 +#: src/Module/Friendica.php:69 msgid "No installed addons/apps" msgstr "" -#: src/Module/Friendica.php:73 +#: src/Module/Friendica.php:74 #, php-format msgid "Read about the Terms of Service of this node." msgstr "" -#: src/Module/Friendica.php:80 +#: src/Module/Friendica.php:81 msgid "On this server the following remote servers are blocked." msgstr "" -#: src/Module/Friendica.php:83 +#: src/Module/Friendica.php:84 #: src/Module/Moderation/Blocklist/Server/Index.php:84 #: src/Module/Moderation/Blocklist/Server/Index.php:108 msgid "Reason for the block" msgstr "" -#: src/Module/Friendica.php:85 +#: src/Module/Friendica.php:86 msgid "Download this list in CSV format" msgstr "" -#: src/Module/Friendica.php:99 +#: src/Module/Friendica.php:100 #, php-format msgid "" "This is Friendica, version %s that is running at the web location %s. The " "database version is %s, the post update version is %s." msgstr "" -#: src/Module/Friendica.php:104 +#: src/Module/Friendica.php:105 msgid "" "Please visit Friendi.ca to learn more " "about the Friendica project." msgstr "" -#: src/Module/Friendica.php:105 +#: src/Module/Friendica.php:106 msgid "Bug reports and issues: please visit" msgstr "" -#: src/Module/Friendica.php:105 +#: src/Module/Friendica.php:106 msgid "the bugtracker at github" msgstr "" -#: src/Module/Friendica.php:106 +#: src/Module/Friendica.php:107 msgid "" "Suggestions, praise, etc. - please email \"info\" at \"friendi - dot - ca" msgstr "" @@ -8123,21 +8123,21 @@ msgstr "" msgid "BCC: %s
" msgstr "" -#: src/Module/Photo.php:128 +#: src/Module/Photo.php:129 msgid "The Photo is not available." msgstr "" -#: src/Module/Photo.php:141 +#: src/Module/Photo.php:142 #, php-format msgid "The Photo with id %s is not available." msgstr "" -#: src/Module/Photo.php:178 +#: src/Module/Photo.php:179 #, php-format msgid "Invalid external resource with url %s." msgstr "" -#: src/Module/Photo.php:180 +#: src/Module/Photo.php:181 #, php-format msgid "Invalid photo with id %s." msgstr "" @@ -8546,7 +8546,7 @@ msgstr "" msgid "Your registration is pending approval by the site owner." msgstr "" -#: src/Module/Search/Acl.php:55 +#: src/Module/Search/Acl.php:73 msgid "You must be logged in to use this module." msgstr "" @@ -9541,153 +9541,142 @@ msgstr "" msgid "No entries." msgstr "" -#: src/Module/Settings/Display.php:139 +#: src/Module/Settings/Display.php:137 msgid "The theme you chose isn't available." msgstr "" -#: src/Module/Settings/Display.php:179 +#: src/Module/Settings/Display.php:177 #, php-format msgid "%s - (Unsupported)" msgstr "" -#: src/Module/Settings/Display.php:215 +#: src/Module/Settings/Display.php:212 msgid "No preview" msgstr "" -#: src/Module/Settings/Display.php:216 +#: src/Module/Settings/Display.php:213 msgid "No image" msgstr "" -#: src/Module/Settings/Display.php:217 +#: src/Module/Settings/Display.php:214 msgid "Small Image" msgstr "" -#: src/Module/Settings/Display.php:218 +#: src/Module/Settings/Display.php:215 msgid "Large Image" msgstr "" -#: src/Module/Settings/Display.php:249 +#: src/Module/Settings/Display.php:246 msgid "Display Settings" msgstr "" -#: src/Module/Settings/Display.php:251 +#: src/Module/Settings/Display.php:248 msgid "General Theme Settings" msgstr "" -#: src/Module/Settings/Display.php:252 +#: src/Module/Settings/Display.php:249 msgid "Custom Theme Settings" msgstr "" -#: src/Module/Settings/Display.php:253 +#: src/Module/Settings/Display.php:250 msgid "Content Settings" msgstr "" -#: src/Module/Settings/Display.php:254 view/theme/duepuntozero/config.php:86 +#: src/Module/Settings/Display.php:251 view/theme/duepuntozero/config.php:86 #: view/theme/frio/config.php:172 view/theme/quattro/config.php:88 #: view/theme/vier/config.php:136 msgid "Theme settings" msgstr "" -#: src/Module/Settings/Display.php:261 +#: src/Module/Settings/Display.php:258 msgid "Display Theme:" msgstr "" -#: src/Module/Settings/Display.php:262 +#: src/Module/Settings/Display.php:259 msgid "Mobile Theme:" msgstr "" -#: src/Module/Settings/Display.php:265 +#: src/Module/Settings/Display.php:262 msgid "Number of items to display per page:" msgstr "" -#: src/Module/Settings/Display.php:265 src/Module/Settings/Display.php:266 +#: src/Module/Settings/Display.php:262 src/Module/Settings/Display.php:263 msgid "Maximum of 100 items" msgstr "" -#: src/Module/Settings/Display.php:266 +#: src/Module/Settings/Display.php:263 msgid "Number of items to display per page when viewed from mobile device:" msgstr "" -#: src/Module/Settings/Display.php:267 +#: src/Module/Settings/Display.php:264 msgid "Update browser every xx seconds" msgstr "" -#: src/Module/Settings/Display.php:267 +#: src/Module/Settings/Display.php:264 msgid "Minimum of 10 seconds. Enter -1 to disable it." msgstr "" -#: src/Module/Settings/Display.php:268 -msgid "Automatic updates only at the top of the post stream pages" -msgstr "" - -#: src/Module/Settings/Display.php:268 -msgid "" -"Auto update may add new posts at the top of the post stream pages, which can " -"affect the scroll position and perturb normal reading if it happens anywhere " -"else the top of the page." -msgstr "" - -#: src/Module/Settings/Display.php:269 +#: src/Module/Settings/Display.php:265 msgid "Display emoticons" msgstr "" -#: src/Module/Settings/Display.php:269 +#: src/Module/Settings/Display.php:265 msgid "When enabled, emoticons are replaced with matching symbols." msgstr "" -#: src/Module/Settings/Display.php:270 +#: src/Module/Settings/Display.php:266 msgid "Infinite scroll" msgstr "" -#: src/Module/Settings/Display.php:270 +#: src/Module/Settings/Display.php:266 msgid "Automatic fetch new items when reaching the page end." msgstr "" -#: src/Module/Settings/Display.php:271 +#: src/Module/Settings/Display.php:267 msgid "Enable Smart Threading" msgstr "" -#: src/Module/Settings/Display.php:271 +#: src/Module/Settings/Display.php:267 msgid "Enable the automatic suppression of extraneous thread indentation." msgstr "" -#: src/Module/Settings/Display.php:272 +#: src/Module/Settings/Display.php:268 msgid "Display the Dislike feature" msgstr "" -#: src/Module/Settings/Display.php:272 +#: src/Module/Settings/Display.php:268 msgid "Display the Dislike button and dislike reactions on posts and comments." msgstr "" -#: src/Module/Settings/Display.php:273 +#: src/Module/Settings/Display.php:269 msgid "Display the resharer" msgstr "" -#: src/Module/Settings/Display.php:273 +#: src/Module/Settings/Display.php:269 msgid "Display the first resharer as icon and text on a reshared item." msgstr "" -#: src/Module/Settings/Display.php:274 +#: src/Module/Settings/Display.php:270 msgid "Stay local" msgstr "" -#: src/Module/Settings/Display.php:274 +#: src/Module/Settings/Display.php:270 msgid "Don't go to a remote system when following a contact link." msgstr "" -#: src/Module/Settings/Display.php:275 +#: src/Module/Settings/Display.php:271 msgid "Link preview mode" msgstr "" -#: src/Module/Settings/Display.php:275 +#: src/Module/Settings/Display.php:271 msgid "Appearance of the link preview that is added to each post with a link." msgstr "" -#: src/Module/Settings/Display.php:277 +#: src/Module/Settings/Display.php:273 msgid "Beginning of week:" msgstr "" -#: src/Module/Settings/Display.php:278 +#: src/Module/Settings/Display.php:274 msgid "Default calendar view:" msgstr "" @@ -11247,7 +11236,7 @@ msgstr "" msgid "Show fewer" msgstr "" -#: src/Protocol/Delivery.php:535 +#: src/Protocol/Delivery.php:547 msgid "(no subject)" msgstr "" From 6685157f8a32971b1e1db8d963743a94c97d86f8 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Thu, 5 Jan 2023 11:54:01 -0500 Subject: [PATCH 46/71] Cast alert types to boolean in Api\Mastodon\Subscription --- src/Object/Api/Mastodon/Subscription.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Object/Api/Mastodon/Subscription.php b/src/Object/Api/Mastodon/Subscription.php index 993c73e61d..8b0a84b0b1 100644 --- a/src/Object/Api/Mastodon/Subscription.php +++ b/src/Object/Api/Mastodon/Subscription.php @@ -50,11 +50,11 @@ class Subscription extends BaseDataTransferObject $this->id = (string)$subscription['id']; $this->endpoint = $subscription['endpoint']; $this->alerts = [ - Notification::TYPE_FOLLOW => $subscription[Notification::TYPE_FOLLOW], - Notification::TYPE_LIKE => $subscription[Notification::TYPE_LIKE], - Notification::TYPE_RESHARE => $subscription[Notification::TYPE_RESHARE], - Notification::TYPE_MENTION => $subscription[Notification::TYPE_MENTION], - Notification::TYPE_POLL => $subscription[Notification::TYPE_POLL], + Notification::TYPE_FOLLOW => (bool)$subscription[Notification::TYPE_FOLLOW], + Notification::TYPE_LIKE => (bool)$subscription[Notification::TYPE_LIKE], + Notification::TYPE_RESHARE => (bool)$subscription[Notification::TYPE_RESHARE], + Notification::TYPE_MENTION => (bool)$subscription[Notification::TYPE_MENTION], + Notification::TYPE_POLL => (bool)$subscription[Notification::TYPE_POLL], ]; $this->server_key = $vapid; From b4096251babf9d48ae3679637f3b3684ad28267a Mon Sep 17 00:00:00 2001 From: Philipp Date: Thu, 5 Jan 2023 21:42:35 +0100 Subject: [PATCH 47/71] Check 'config' table as fallback for migrations --- src/Core/Update.php | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/Core/Update.php b/src/Core/Update.php index e5ee587dc1..d440136b55 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -61,8 +61,18 @@ class Update $build = DI::config()->get('system', 'build'); if (empty($build)) { - DI::config()->set('system', 'build', DB_UPDATE_VERSION - 1); - $build = DB_UPDATE_VERSION - 1; + // legacy option - check if there's something in the Config table + if (DBStructure::existsTable('config')) { + $dbConfig = DBA::selectFirst('config', ['v'], ['cat' => 'system', 'k' => 'build']); + if (!empty($dbConfig)) { + $build = $dbConfig['v']; + } + } + + if (empty($build)) { + DI::config()->set('system', 'build', DB_UPDATE_VERSION - 1); + $build = DB_UPDATE_VERSION - 1; + } } // We don't support upgrading from very old versions anymore @@ -119,11 +129,21 @@ class Update DI::lock()->release('dbupdate', true); } - $build = DI::config()->get('system', 'build', null); + $build = DI::config()->get('system', 'build'); - if (empty($build) || ($build > DB_UPDATE_VERSION)) { - $build = DB_UPDATE_VERSION - 1; - DI::config()->set('system', 'build', $build); + if (empty($build)) { + // legacy option - check if there's something in the Config table + if (DBStructure::existsTable('config')) { + $dbConfig = DBA::selectFirst('config', ['v'], ['cat' => 'system', 'k' => 'build']); + if (!empty($dbConfig)) { + $build = $dbConfig['v']; + } + } + + if (empty($build) || ($build > DB_UPDATE_VERSION)) { + DI::config()->set('system', 'build', DB_UPDATE_VERSION - 1); + $build = DB_UPDATE_VERSION - 1; + } } if ($build != DB_UPDATE_VERSION || $force) { @@ -141,7 +161,15 @@ class Update Logger::notice('Update starting.', ['from' => $stored, 'to' => $current]); // Checks if the build changed during Lock acquiring (so no double update occurs) - $retryBuild = DI::config()->get('system', 'build', null); + $retryBuild = DI::config()->get('system', 'build'); + // legacy option - check if there's something in the Config table + if (DBStructure::existsTable('config')) { + $dbConfig = DBA::selectFirst('config', ['v'], ['cat' => 'system', 'k' => 'build']); + if (!empty($dbConfig)) { + $retryBuild = $dbConfig['v']; + } + } + if ($retryBuild !== $build) { Logger::notice('Update already done.', ['from' => $stored, 'to' => $current]); DI::lock()->release('dbupdate'); From cdd57275eb7c0b6d41d5fb1f09b3f2816b3972f1 Mon Sep 17 00:00:00 2001 From: Philipp Date: Thu, 5 Jan 2023 22:13:10 +0100 Subject: [PATCH 48/71] Some improvements - Move $_SERVER into ConfigFileManager constructor - Rename "creatConfigFileLoader" to "createConfigFileManager" - Rename variable "loader" to "manager" in all tests --- src/App.php | 2 +- src/Core/Config/Factory/Config.php | 9 +- src/Core/Config/Model/Config.php | 9 +- src/Core/Config/Util/ConfigFileManager.php | 25 +-- src/Module/Admin/Summary.php | 2 +- static/dependencies.config.php | 4 +- tests/FixtureTest.php | 2 +- tests/src/Core/Cache/DatabaseCacheTest.php | 8 +- .../Config/Cache/ConfigFileManagerTest.php | 166 +++++++++--------- tests/src/Core/Config/ConfigTest.php | 2 +- .../src/Core/Storage/DatabaseStorageTest.php | 8 +- .../Storage/Repository/StorageManagerTest.php | 22 +-- 12 files changed, 134 insertions(+), 125 deletions(-) diff --git a/src/App.php b/src/App.php index 18a696734e..fc70f920de 100644 --- a/src/App.php +++ b/src/App.php @@ -359,7 +359,7 @@ class App $this->profiler->update($this->config); Core\Hook::loadHooks(); - $loader = (new Config())->createConfigFileLoader($this->getBasePath(), $_SERVER); + $loader = (new Config())->createConfigFileManager($this->getBasePath(), $_SERVER); Core\Hook::callAll('load_config', $loader); // Hooks are now working, reload the whole definitions with hook enabled diff --git a/src/Core/Config/Factory/Config.php b/src/Core/Config/Factory/Config.php index e1094fd70d..75c91540ca 100644 --- a/src/Core/Config/Factory/Config.php +++ b/src/Core/Config/Factory/Config.php @@ -56,7 +56,7 @@ class Config * * @return Util\ConfigFileManager */ - public function createConfigFileLoader(string $basePath, array $server = []): Util\ConfigFileManager + public function createConfigFileManager(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,18 @@ class Config } $staticDir = $basePath . DIRECTORY_SEPARATOR . self::STATIC_DIR; - return new Util\ConfigFileManager($basePath, $configDir, $staticDir, new Util\ConfigFileTransformer()); + return new Util\ConfigFileManager($basePath, $configDir, $staticDir, $server); } /** * @param Util\ConfigFileManager $configFileManager The Config Cache manager (INI/config/.htconfig) - * @param array $server * * @return Cache */ - public function createCache(Util\ConfigFileManager $configFileManager, array $server = []): Cache + public function createCache(Util\ConfigFileManager $configFileManager): Cache { $configCache = new Cache(); - $configFileManager->setupCache($configCache, $server); + $configFileManager->setupCache($configCache); return $configCache; } diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php index 593ab04070..51025c8747 100644 --- a/src/Core/Config/Model/Config.php +++ b/src/Core/Config/Model/Config.php @@ -39,19 +39,14 @@ class Config implements IManageConfigValues /** @var ConfigFileManager */ protected $configFileManager; - /** @var array */ - protected $server; - /** * @param ConfigFileManager $configFileManager The configuration file manager to save back configs * @param Cache $configCache The configuration cache (based on the config-files) - * @param array $server The $_SERVER variable */ - public function __construct(ConfigFileManager $configFileManager, Cache $configCache, array $server = []) + public function __construct(ConfigFileManager $configFileManager, Cache $configCache) { $this->configFileManager = $configFileManager; $this->configCache = $configCache; - $this->server = $server; } /** @@ -87,7 +82,7 @@ class Config implements IManageConfigValues $configCache = new Cache(); try { - $this->configFileManager->setupCache($configCache, $this->server); + $this->configFileManager->setupCache($configCache); } catch (ConfigFileException $e) { throw new ConfigPersistenceException('Cannot reload config', $e); } diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index cc264ea26c..6951c24286 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -69,16 +69,22 @@ class ConfigFileManager */ private $staticDir; + /** + * @var array + */ + private $server; + /** * @param string $baseDir The base * @param string $configDir * @param string $staticDir */ - public function __construct(string $baseDir, string $configDir, string $staticDir) + public function __construct(string $baseDir, string $configDir, string $staticDir, array $server = []) { $this->baseDir = $baseDir; $this->configDir = $configDir; $this->staticDir = $staticDir; + $this->server = $server; } /** @@ -88,12 +94,11 @@ class ConfigFileManager * expected local.config.php * * @param Cache $config The config cache to load to - * @param array $server The $_SERVER array * @param bool $raw Set up the raw config format * * @throws ConfigFileException */ - public function setupCache(Cache $config, array $server = [], bool $raw = false) + public function setupCache(Cache $config, bool $raw = false) { // Load static config files first, the order is important $config->load($this->loadStaticConfig('defaults'), Cache::SOURCE_STATIC); @@ -109,7 +114,7 @@ class ConfigFileManager // 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); + $config->load($this->loadEnvConfig(), Cache::SOURCE_ENV); // In case of install mode, add the found basepath (because there isn't a basepath set yet if (!$raw && empty($config->get('system', 'basepath'))) { @@ -250,13 +255,11 @@ class ConfigFileManager /** * Tries to load environment specific variables, based on the `env.config.php` mapping table * - * @param array $server The $_SERVER variable - * * @return array The config array (empty if no config was found) * * @throws ConfigFileException if the configuration file isn't readable */ - public function loadEnvConfig(array $server): array + protected function loadEnvConfig(): array { $filepath = $this->staticDir . DIRECTORY_SEPARATOR . // /var/www/html/static/ "env.config.php"; // env.config.php @@ -270,8 +273,8 @@ class ConfigFileManager $return = []; foreach ($envConfig as $envKey => $configStructure) { - if (isset($server[$envKey])) { - $return[$configStructure[0]][$configStructure[1]] = $server[$envKey]; + if (isset($this->server[$envKey])) { + $return[$configStructure[0]][$configStructure[1]] = $this->server[$envKey]; } } @@ -296,7 +299,9 @@ class ConfigFileManager $sampleEnd = self::SAMPLE_END . ($ini ? '.ini.php' : '.config.php'); foreach ($files as $filename) { - if (fnmatch($filePattern, $filename) && substr_compare($filename, $sampleEnd, -strlen($sampleEnd))) { + if (fnmatch($filePattern, $filename) && + substr_compare($filename, $sampleEnd, -strlen($sampleEnd)) && + $filename !== self::CONFIG_DATA_FILE) { $found[] = $this->configDir . '/' . $filename; } } diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index 6244c5c9b6..038628ee1e 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -154,7 +154,7 @@ class Summary extends BaseAdmin } // check legacy basepath settings - $configLoader = (new Config())->createConfigFileLoader($a->getBasePath(), $_SERVER); + $configLoader = (new Config())->createConfigFileManager($a->getBasePath(), $_SERVER); $configCache = new Cache(); $configLoader->setupCache($configCache); $confBasepath = $configCache->get('system', 'basepath'); diff --git a/static/dependencies.config.php b/static/dependencies.config.php index 1844001bb4..6feb76989b 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -79,7 +79,7 @@ return [ Config\Util\ConfigFileManager::class => [ 'instanceOf' => Config\Factory\Config::class, 'call' => [ - ['createConfigFileLoader', [ + ['createConfigFileManager', [ [Dice::INSTANCE => '$basepath'], $_SERVER, ], Dice::CHAIN_CALL], @@ -88,7 +88,7 @@ return [ Config\ValueObject\Cache::class => [ 'instanceOf' => Config\Factory\Config::class, 'call' => [ - ['createCache', [$_SERVER], Dice::CHAIN_CALL], + ['createCache', [], Dice::CHAIN_CALL], ], ], App\Mode::class => [ diff --git a/tests/FixtureTest.php b/tests/FixtureTest.php index efb9280310..e0d2f23379 100644 --- a/tests/FixtureTest.php +++ b/tests/FixtureTest.php @@ -62,7 +62,7 @@ abstract class FixtureTest extends DatabaseTest ->addRules(include __DIR__ . '/../static/dependencies.config.php') ->addRule(ConfigFileManager::class, [ 'instanceOf' => Config::class, - 'call' => [['createConfigFileLoader', [$this->root->url(), $server,], + 'call' => [['createConfigFileManager', [$this->root->url(), $server,], Dice::CHAIN_CALL]]]) ->addRule(Database::class, ['instanceOf' => StaticDatabase::class, 'shared' => true]) ->addRule(IHandleSessions::class, ['instanceOf' => Memory::class, 'shared' => true, 'call' => null]) diff --git a/tests/src/Core/Cache/DatabaseCacheTest.php b/tests/src/Core/Cache/DatabaseCacheTest.php index 634e2dcf7f..2a837b71f3 100644 --- a/tests/src/Core/Cache/DatabaseCacheTest.php +++ b/tests/src/Core/Cache/DatabaseCacheTest.php @@ -54,11 +54,11 @@ class DatabaseCacheTest extends CacheTest $profiler->shouldReceive('saveTimestamp')->withAnyArgs()->andReturn(true); // load real config to avoid mocking every config-entry which is related to the Database class - $configFactory = new Config(); - $loader = (new Config())->createConfigFileLoader($this->root->url(), []); - $configCache = $configFactory->createCache($loader); + $configFactory = new Config(); + $configFileManager = (new Config())->createConfigFileManager($this->root->url(), []); + $configCache = $configFactory->createCache($configFileManager); - $dbaDefinition = (new DbaDefinition($configCache->get('system', 'basepath')))->load(); + $dbaDefinition = (new DbaDefinition($configCache->get('system', 'basepath')))->load(); $viewDefinition = (new ViewDefinition($configCache->get('system', 'basepath')))->load(); $dba = new StaticDatabase($configCache, $profiler, $dbaDefinition, $viewDefinition); diff --git a/tests/src/Core/Config/Cache/ConfigFileManagerTest.php b/tests/src/Core/Config/Cache/ConfigFileManagerTest.php index 55dff2f4a9..89d757e320 100644 --- a/tests/src/Core/Config/Cache/ConfigFileManagerTest.php +++ b/tests/src/Core/Config/Cache/ConfigFileManagerTest.php @@ -51,6 +51,7 @@ class ConfigFileManagerTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -69,15 +70,15 @@ class ConfigFileManagerTest extends MockedTest $this->delConfigFile('local.config.php'); vfsStream::newFile('local.config.php') - ->at($this->root->getChild('config')) - ->setContent('at($this->root->getChild('config')) + ->setContent('root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); } @@ -90,23 +91,23 @@ class ConfigFileManagerTest extends MockedTest $this->delConfigFile('local.config.php'); $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR . - 'A.config.php'; + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + 'A.config.php'; vfsStream::newFile('local.config.php') - ->at($this->root->getChild('config')) - ->setContent(file_get_contents($file)); + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($file)); $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -127,23 +128,23 @@ class ConfigFileManagerTest extends MockedTest $this->delConfigFile('local.config.php'); $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR . - 'A.ini.php'; + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + 'A.ini.php'; vfsStream::newFile('local.ini.php') - ->at($this->root->getChild('config')) - ->setContent(file_get_contents($file)); + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($file)); $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -163,23 +164,23 @@ class ConfigFileManagerTest extends MockedTest $this->delConfigFile('local.config.php'); $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR . - '.htconfig.php'; + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + '.htconfig.php'; vfsStream::newFile('.htconfig.php') - ->at($this->root) - ->setContent(file_get_contents($file)); + ->at($this->root) + ->setContent(file_get_contents($file)); $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -217,16 +218,16 @@ class ConfigFileManagerTest extends MockedTest vfsStream::create($structure, $this->root); $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR . - 'A.config.php'; + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + 'A.config.php'; vfsStream::newFile('test.config.php') - ->at($this->root->getChild('addon')->getChild('test')->getChild('config')) - ->setContent(file_get_contents($file)); + ->at($this->root->getChild('addon')->getChild('test')->getChild('config')) + ->setContent(file_get_contents($file)); $configFileLoader = new ConfigFileManager( $this->root->url(), @@ -252,25 +253,25 @@ class ConfigFileManagerTest extends MockedTest $this->delConfigFile('local.config.php'); $fileDir = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR; + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR; vfsStream::newFile('A.config.php') - ->at($this->root->getChild('config')) - ->setContent(file_get_contents($fileDir . 'A.config.php')); + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($fileDir . 'A.config.php')); vfsStream::newFile('B.config.php') - ->at($this->root->getChild('config')) - ->setContent(file_get_contents($fileDir . 'B.config.php')); + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($fileDir . 'B.config.php')); $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -286,25 +287,25 @@ class ConfigFileManagerTest extends MockedTest $this->delConfigFile('local.config.php'); $fileDir = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR; + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR; vfsStream::newFile('A.ini.php') - ->at($this->root->getChild('config')) - ->setContent(file_get_contents($fileDir . 'A.ini.php')); + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($fileDir . 'A.ini.php')); vfsStream::newFile('B.ini.php') - ->at($this->root->getChild('config')) - ->setContent(file_get_contents($fileDir . 'B.ini.php')); + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($fileDir . 'B.ini.php')); $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -320,24 +321,25 @@ class ConfigFileManagerTest extends MockedTest $this->delConfigFile('local.config.php'); $fileDir = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR; + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR; vfsStream::newFile('A.ini.php') - ->at($this->root->getChild('config')) - ->setContent(file_get_contents($fileDir . 'A.ini.php')); + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($fileDir . 'A.ini.php')); vfsStream::newFile('B-sample.ini.php') - ->at($this->root->getChild('config')) - ->setContent(file_get_contents($fileDir . 'B.ini.php')); + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($fileDir . 'B.ini.php')); $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -353,10 +355,10 @@ class ConfigFileManagerTest extends MockedTest { $this->delConfigFile('local.config.php'); - $configFileLoader = (new Config())->createConfigFileLoader($this->root->url(), ['FRIENDICA_CONFIG_DIR' => '/a/wrong/dir/']); - $configCache = new Cache(); + $configFileManager = (new Config())->createConfigFileManager($this->root->url(), ['FRIENDICA_CONFIG_DIR' => '/a/wrong/dir/']); + $configCache = new Cache(); - $configFileLoader->setupCache($configCache); + $configFileManager->setupCache($configCache); self::assertEquals($this->root->url(), $configCache->get('system', 'basepath')); } @@ -379,10 +381,13 @@ class ConfigFileManagerTest extends MockedTest ->at($this->root->getChild('config2')) ->setContent(file_get_contents($fileDir . 'B.config.php')); - $configFileLoader = (new Config())->createConfigFileLoader($this->root->url(), ['FRIENDICA_CONFIG_DIR' => $this->root->getChild('config2')->url()]); - $configCache = new Cache(); + $configFileManager = (new Config())->createConfigFileManager($this->root->url(), + [ + 'FRIENDICA_CONFIG_DIR' => $this->root->getChild('config2')->url(), + ]); + $configCache = new Cache(); - $configFileLoader->setupCache($configCache); + $configFileManager->setupCache($configCache); self::assertEquals('newValue', $configCache->get('system', 'newKey')); } @@ -402,10 +407,13 @@ class ConfigFileManagerTest extends MockedTest ->at($this->root->getChild('config2')) ->setContent(file_get_contents($fileDir . 'B.config.php')); - $configFileLoader = (new Config())->createConfigFileLoader($this->root->url(), ['FRIENDICA_CONFIG_DIR' => $this->root->getChild('config2')->url()]); - $configCache = new Cache(); + $configFileManager = (new Config())->createConfigFileManager($this->root->url(), + [ + 'FRIENDICA_CONFIG_DIR' => $this->root->getChild('config2')->url(), + ]); + $configCache = new Cache(); - $configFileLoader->setupCache($configCache); + $configFileManager->setupCache($configCache); $specialChars = '!"§$%&/()(/&%$\'>set('config', 'test', 'it', Cache::SOURCE_DATA); $configCache->set('system', 'test_2', 2, Cache::SOURCE_DATA); $configCache->set('special_chars', 'special', $specialChars, Cache::SOURCE_DATA); - $configFileLoader->saveData($configCache); + $configFileManager->saveData($configCache); // Reload the configCache with the new values $configCache2 = new Cache(); - $configFileLoader->setupCache($configCache2); + $configFileManager->setupCache($configCache2); self::assertEquals($configCache, $configCache2); self::assertEquals([ - 'system' => [ - 'test' => 'it', + 'system' => [ + 'test' => 'it', 'test_2' => 2 ], - 'config' => [ + 'config' => [ 'test' => 'it', ], 'special_chars' => [ diff --git a/tests/src/Core/Config/ConfigTest.php b/tests/src/Core/Config/ConfigTest.php index 876a0b05b3..d40af47d51 100644 --- a/tests/src/Core/Config/ConfigTest.php +++ b/tests/src/Core/Config/ConfigTest.php @@ -76,7 +76,7 @@ class ConfigTest extends MockedTest */ public function getInstance() { - $this->configFileManager->setupCache($this->configCache, []); + $this->configFileManager->setupCache($this->configCache); return new Config($this->configFileManager, $this->configCache); } diff --git a/tests/src/Core/Storage/DatabaseStorageTest.php b/tests/src/Core/Storage/DatabaseStorageTest.php index 30626a78a5..7ac87245a9 100644 --- a/tests/src/Core/Storage/DatabaseStorageTest.php +++ b/tests/src/Core/Storage/DatabaseStorageTest.php @@ -53,11 +53,11 @@ class DatabaseStorageTest extends StorageTest $profiler->shouldReceive('saveTimestamp')->withAnyArgs()->andReturn(true); // load real config to avoid mocking every config-entry which is related to the Database class - $configFactory = new Config(); - $loader = (new Config())->createConfigFileLoader($this->root->url(), []); - $configCache = $configFactory->createCache($loader); + $configFactory = new Config(); + $configFileManager = (new Config())->createConfigFileManager($this->root->url()); + $configCache = $configFactory->createCache($configFileManager); - $dbaDefinition = (new DbaDefinition($configCache->get('system', 'basepath')))->load(); + $dbaDefinition = (new DbaDefinition($configCache->get('system', 'basepath')))->load(); $viewDefinition = (new ViewDefinition($configCache->get('system', 'basepath')))->load(); $dba = new StaticDatabase($configCache, $profiler, $dbaDefinition, $viewDefinition); diff --git a/tests/src/Core/Storage/Repository/StorageManagerTest.php b/tests/src/Core/Storage/Repository/StorageManagerTest.php index db4ca8ef2b..a55819ee0b 100644 --- a/tests/src/Core/Storage/Repository/StorageManagerTest.php +++ b/tests/src/Core/Storage/Repository/StorageManagerTest.php @@ -52,6 +52,7 @@ use Friendica\Test\Util\SampleStorageBackend; class StorageManagerTest extends DatabaseTest { use VFSTrait; + /** @var Database */ private $dba; /** @var IManageConfigValues */ @@ -77,18 +78,19 @@ class StorageManagerTest extends DatabaseTest $profiler->shouldReceive('saveTimestamp')->withAnyArgs()->andReturn(true); // load real config to avoid mocking every config-entry which is related to the Database class - $configFactory = new Config(); - $loader = $configFactory->createConfigFileLoader($this->root->url(), []); - $configCache = $configFactory->createCache($loader); + $configFactory = new Config(); + $configFileManager = $configFactory->createConfigFileManager($this->root->url()); + $configCache = $configFactory->createCache($configFileManager); - $dbaDefinition = (new DbaDefinition($configCache->get('system', 'basepath')))->load(); + $dbaDefinition = (new DbaDefinition($configCache->get('system', 'basepath')))->load(); $viewDefinition = (new ViewDefinition($configCache->get('system', 'basepath')))->load(); $this->dba = new StaticDatabase($configCache, $profiler, $dbaDefinition, $viewDefinition); - $this->config = new \Friendica\Core\Config\Model\Config($loader, $configCache); + $this->config = new \Friendica\Core\Config\Model\Config($configFileManager, $configCache); $this->config->set('storage', 'name', 'Database'); - $this->config->set('storage', 'filesystem_path', $this->root->getChild(Type\FilesystemConfig::DEFAULT_BASE_FOLDER)->url()); + $this->config->set('storage', 'filesystem_path', $this->root->getChild(Type\FilesystemConfig::DEFAULT_BASE_FOLDER) + ->url()); $this->l10n = \Mockery::mock(L10n::class); } @@ -113,21 +115,21 @@ class StorageManagerTest extends DatabaseTest public function dataStorages() { return [ - 'empty' => [ + 'empty' => [ 'name' => '', 'valid' => false, 'interface' => ICanReadFromStorage::class, 'assert' => null, 'assertName' => '', ], - 'database' => [ + 'database' => [ 'name' => Type\Database::NAME, 'valid' => true, 'interface' => ICanWriteToStorage::class, 'assert' => Type\Database::class, 'assertName' => Type\Database::NAME, ], - 'filesystem' => [ + 'filesystem' => [ 'name' => Filesystem::NAME, 'valid' => true, 'interface' => ICanWriteToStorage::class, @@ -141,7 +143,7 @@ class StorageManagerTest extends DatabaseTest 'assert' => SystemResource::class, 'assertName' => SystemResource::NAME, ], - 'invalid' => [ + 'invalid' => [ 'name' => 'invalid', 'valid' => false, 'interface' => null, From 5aa8e8adf1af017ced555d8c33b78e71114f5cd9 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 01:02:47 +0100 Subject: [PATCH 49/71] Config fixings - Delete now really overwrites static default/setting.config.php keys - Delete now really overwrites static default/setting.config.php categories - The Update::check() routine is added to different places - Merge the given config file with the new config before writing - Remove ConfigTransaction::get() because it's no more reliable --- bin/auth_ejabberd.php | 4 + bin/daemon.php | 5 +- bin/worker.php | 3 +- index.php | 3 + src/Core/Config/Model/Config.php | 2 +- src/Core/Config/Model/ConfigTransaction.php | 40 +------ src/Core/Config/Util/ConfigFileManager.php | 104 +++++++++++------ .../Config/Util/ConfigFileTransformer.php | 10 +- src/Core/Config/ValueObject/Cache.php | 107 ++++++++---------- tests/src/Core/Config/Cache/CacheTest.php | 40 ++----- .../Config/Cache/ConfigFileManagerTest.php | 70 ++++++++++++ tests/src/Core/Config/ConfigTest.php | 2 +- .../src/Core/Config/ConfigTransactionTest.php | 12 -- 13 files changed, 221 insertions(+), 181 deletions(-) diff --git a/bin/auth_ejabberd.php b/bin/auth_ejabberd.php index ecff7218dd..9c5dc05285 100755 --- a/bin/auth_ejabberd.php +++ b/bin/auth_ejabberd.php @@ -82,6 +82,10 @@ $dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['auth_ejabb \Friendica\DI::init($dice); \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); + +// Check the database structure and possibly fixes it +\Friendica\Core\Update::check(\Friendica\DI::basePath(), true, \Friendica\DI::mode()); + $appMode = $dice->create(Mode::class); if ($appMode->isNormal()) { diff --git a/bin/daemon.php b/bin/daemon.php index 577e884ebf..6237df49de 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -33,6 +33,7 @@ if (php_sapi_name() !== 'cli') { use Dice\Dice; use Friendica\App\Mode; use Friendica\Core\Logger; +use Friendica\Core\Update; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; @@ -63,7 +64,9 @@ $dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['daemon']]) DI::init($dice); \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); -$a = DI::app(); + +// Check the database structure and possibly fixes it +Update::check(DI::basePath(), true, DI::mode()); if (DI::mode()->isInstall()) { die("Friendica isn't properly installed yet.\n"); diff --git a/bin/worker.php b/bin/worker.php index 707e57972d..aceeff4818 100755 --- a/bin/worker.php +++ b/bin/worker.php @@ -58,12 +58,11 @@ $dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['worker']]) DI::init($dice); \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); -$a = DI::app(); DI::mode()->setExecutor(Mode::WORKER); // Check the database structure and possibly fixes it -Update::check($a->getBasePath(), true, DI::mode()); +Update::check(DI::basePath(), true, DI::mode()); // Quit when in maintenance if (!DI::mode()->has(App\Mode::MAINTENANCEDISABLED)) { diff --git a/index.php b/index.php index fa6d8b0871..10f04996af 100644 --- a/index.php +++ b/index.php @@ -36,6 +36,9 @@ $dice = $dice->addRule(Friendica\App\Mode::class, ['call' => [['determineRunMode \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); +// Check the database structure and possibly fixes it +\Friendica\Core\Update::check(\Friendica\DI::basePath(), true, \Friendica\DI::mode()); + $a = \Friendica\DI::app(); \Friendica\DI::mode()->setExecutor(\Friendica\App\Mode::INDEX); diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php index 51025c8747..c96750694b 100644 --- a/src/Core/Config/Model/Config.php +++ b/src/Core/Config/Model/Config.php @@ -116,7 +116,7 @@ class Config implements IManageConfigValues /** {@inheritDoc} */ public function delete(string $cat, string $key): bool { - if ($this->configCache->delete($cat, $key)) { + if ($this->configCache->delete($cat, $key, Cache::SOURCE_DATA)) { $this->save(); return true; } else { diff --git a/src/Core/Config/Model/ConfigTransaction.php b/src/Core/Config/Model/ConfigTransaction.php index 26420b0788..ec0a2b9c8b 100644 --- a/src/Core/Config/Model/ConfigTransaction.php +++ b/src/Core/Config/Model/ConfigTransaction.php @@ -35,37 +35,13 @@ class ConfigTransaction implements ISetConfigValuesTransactionally protected $config; /** @var Cache */ protected $cache; - /** @var Cache */ - protected $delCache; /** @var bool field to check if something is to save */ protected $changedConfig = false; public function __construct(IManageConfigValues $config) { - $this->config = $config; - $this->cache = new Cache(); - $this->delCache = new Cache(); - } - - /** - * Get a particular user's config variable given the category name - * ($cat) and a $key from the current transaction. - * - * Isn't part of the interface because of it's rare use case - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to query - * - * @return mixed Stored value or null if it does not exist - * - * @throws ConfigPersistenceException In case the persistence layer throws errors - * - */ - public function get(string $cat, string $key) - { - return !$this->delCache->get($cat, $key) ? - ($this->cache->get($cat, $key) ?? $this->config->get($cat, $key)) : - null; + $this->config = $config; + $this->cache = clone $config->getCache(); } /** {@inheritDoc} */ @@ -81,8 +57,7 @@ class ConfigTransaction implements ISetConfigValuesTransactionally /** {@inheritDoc} */ public function delete(string $cat, string $key): ISetConfigValuesTransactionally { - $this->cache->delete($cat, $key); - $this->delCache->set($cat, $key, 'deleted'); + $this->cache->delete($cat, $key, Cache::SOURCE_DATA); $this->changedConfig = true; return $this; @@ -97,13 +72,8 @@ class ConfigTransaction implements ISetConfigValuesTransactionally } try { - $newCache = $this->config->getCache()->merge($this->cache); - $newCache = $newCache->diff($this->delCache); - $this->config->load($newCache); - - // flush current cache - $this->cache = new Cache(); - $this->delCache = new Cache(); + $this->config->load($this->cache); + $this->cache = clone $this->config->getCache(); } catch (\Exception $e) { throw new ConfigPersistenceException('Cannot save config', $e); } diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index 6951c24286..569bcdb97d 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -93,33 +93,33 @@ class ConfigFileManager * First loads the default value for all the configuration keys, then the legacy configuration files, then the * expected local.config.php * - * @param Cache $config The config cache to load to - * @param bool $raw Set up the raw config format + * @param Cache $configCache The config cache to load to + * @param bool $raw Set up the raw config format * * @throws ConfigFileException */ - public function setupCache(Cache $config, bool $raw = false) + public function setupCache(Cache $configCache, bool $raw = false) { // Load static config files first, the order is important - $config->load($this->loadStaticConfig('defaults'), Cache::SOURCE_STATIC); - $config->load($this->loadStaticConfig('settings'), Cache::SOURCE_STATIC); + $configCache->load($this->loadStaticConfig('defaults'), Cache::SOURCE_STATIC); + $configCache->load($this->loadStaticConfig('settings'), Cache::SOURCE_STATIC); // try to load the legacy config first - $config->load($this->loadLegacyConfig('htpreconfig'), Cache::SOURCE_FILE); - $config->load($this->loadLegacyConfig('htconfig'), Cache::SOURCE_FILE); + $configCache->load($this->loadLegacyConfig('htpreconfig'), Cache::SOURCE_FILE); + $configCache->load($this->loadLegacyConfig('htconfig'), Cache::SOURCE_FILE); // Now load every other config you find inside the 'config/' directory - $this->loadCoreConfig($config); + $this->loadCoreConfig($configCache); // Now load the node.config.php file with the node specific config values (based on admin gui/console actions) - $this->loadDataConfig($config); + $this->loadDataConfig($configCache); - $config->load($this->loadEnvConfig(), Cache::SOURCE_ENV); + $configCache->load($this->loadEnvConfig(), Cache::SOURCE_ENV); // In case of install mode, add the found basepath (because there isn't a basepath set yet - if (!$raw && empty($config->get('system', 'basepath'))) { + if (!$raw && empty($configCache->get('system', 'basepath'))) { // Setting at least the basepath we know - $config->set('system', 'basepath', $this->baseDir, Cache::SOURCE_FILE); + $configCache->set('system', 'basepath', $this->baseDir, Cache::SOURCE_FILE); } } @@ -139,7 +139,7 @@ class ConfigFileManager if (file_exists($configName)) { return $this->loadConfigFile($configName); - } elseif (file_exists($iniName)) { + } else if (file_exists($iniName)) { return $this->loadINIConfigFile($iniName); } else { return []; @@ -149,31 +149,31 @@ class ConfigFileManager /** * Tries to load the specified core-configuration into the config cache. * - * @param Cache $config The Config cache + * @param Cache $configCache The Config cache * * @throws ConfigFileException if the configuration file isn't readable */ - private function loadCoreConfig(Cache $config) + private function loadCoreConfig(Cache $configCache) { // try to load legacy ini-files first foreach ($this->getConfigFiles(true) as $configFile) { - $config->load($this->loadINIConfigFile($configFile), Cache::SOURCE_FILE); + $configCache->load($this->loadINIConfigFile($configFile), Cache::SOURCE_FILE); } // try to load supported config at last to overwrite it foreach ($this->getConfigFiles() as $configFile) { - $config->load($this->loadConfigFile($configFile), Cache::SOURCE_FILE); + $configCache->load($this->loadConfigFile($configFile), Cache::SOURCE_FILE); } } /** * Tries to load the data config file with the overridden data * - * @param Cache $config The Config cache + * @param Cache $configCache The Config cache * * @throws ConfigFileException In case the config file isn't loadable */ - private function loadDataConfig(Cache $config) + private function loadDataConfig(Cache $configCache) { $filename = $this->configDir . '/' . self::CONFIG_DATA_FILE; @@ -194,37 +194,69 @@ class ConfigFileManager $dataArray = eval('?>' . $content); - if (!is_array($dataArray)) { - throw new ConfigFileException(sprintf('Error loading config file %s', $filename)); + if (is_array($dataArray)) { + $configCache->load($dataArray, Cache::SOURCE_DATA); } - - $config->load($dataArray, Cache::SOURCE_DATA); } } /** * Saves overridden config entries back into the data.config.phpR * - * @param Cache $config The config cache + * @param Cache $configCache The config cache * * @throws ConfigFileException In case the config file isn't writeable or the data is invalid */ - public function saveData(Cache $config) + public function saveData(Cache $configCache) { - $data = $config->getDataBySource(Cache::SOURCE_DATA); + $filename = $this->configDir . '/' . self::CONFIG_DATA_FILE; - $encodedData = ConfigFileTransformer::encode($data); - - if (!$encodedData) { - throw new ConfigFileException('config source cannot get encoded'); + if (file_exists($filename)) { + $fileExists = true; + } else { + $fileExists = false; } - $configStream = fopen($this->configDir . '/' . self::CONFIG_DATA_FILE, 'w'); + $configStream = fopen($filename, 'c+'); - if (flock($configStream, LOCK_EX)) { - fwrite($configStream, $encodedData); - fflush($configStream); - flock($configStream, LOCK_UN); + try { + if (flock($configStream, LOCK_EX)) { + + if ($fileExists) { + clearstatcache(true, $filename); + $content = fread($configStream, filesize($filename)); + rewind($configStream); + if (!$content) { + throw new ConfigFileException(sprintf('Couldn\'t read file %s', $filename)); + } + + $dataArray = eval('?>' . $content); + + if (is_array($dataArray)) { + $fileConfigCache = new Cache(); + $fileConfigCache->load($dataArray, Cache::SOURCE_DATA); + $configCache = $fileConfigCache->merge($configCache); + } + } + + $data = $configCache->getDataBySource(Cache::SOURCE_DATA); + + $encodedData = ConfigFileTransformer::encode($data); + + if (!$encodedData) { + throw new ConfigFileException('config source cannot get encoded'); + } + + clearstatcache(true, $filename); + if (!ftruncate($configStream, 0) || + !fwrite($configStream, $encodedData) || + !fflush($configStream) || + !flock($configStream, LOCK_UN)) { + throw new ConfigFileException(sprintf('Cannot modify locked file %s', $filename)); + } + } + } finally { + fclose($configStream); } } @@ -242,7 +274,7 @@ class ConfigFileManager $filepath = $this->baseDir . DIRECTORY_SEPARATOR . // /var/www/html/ Addon::DIRECTORY . DIRECTORY_SEPARATOR . // addon/ $name . DIRECTORY_SEPARATOR . // openstreetmap/ - 'config'. DIRECTORY_SEPARATOR . // config/ + 'config' . DIRECTORY_SEPARATOR . // config/ $name . ".config.php"; // openstreetmap.config.php if (file_exists($filepath)) { diff --git a/src/Core/Config/Util/ConfigFileTransformer.php b/src/Core/Config/Util/ConfigFileTransformer.php index 282714df2a..4eaafe0610 100644 --- a/src/Core/Config/Util/ConfigFileTransformer.php +++ b/src/Core/Config/Util/ConfigFileTransformer.php @@ -34,6 +34,12 @@ class ConfigFileTransformer $categories = array_keys($data); foreach ($categories as $category) { + + if (is_null($data[$category])) { + $dataString .= "\t'$category' => null," . PHP_EOL; + continue; + } + $dataString .= "\t'$category' => [" . PHP_EOL; if (is_array($data[$category])) { @@ -66,7 +72,9 @@ class ConfigFileTransformer { $string = str_repeat("\t", $level + 2) . "'$key' => "; - if (is_array($value)) { + if (is_null($value)) { + $string .= "null,"; + } elseif (is_array($value)) { $string .= "[" . PHP_EOL; $string .= static::extractArray($value, ++$level); $string .= str_repeat("\t", $level + 1) . '],'; diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index b5af3280c0..ca76bb4edd 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -65,7 +65,7 @@ class Cache * @param bool $hidePasswordOutput True, if cache variables should take extra care of password values * @param int $source Sets a source of the initial config values */ - public function __construct(array $config = [], bool $hidePasswordOutput = true, $source = self::SOURCE_DEFAULT) + public function __construct(array $config = [], bool $hidePasswordOutput = true, int $source = self::SOURCE_DEFAULT) { $this->hidePasswordOutput = $hidePasswordOutput; $this->load($config, $source); @@ -87,11 +87,10 @@ class Cache $keys = array_keys($config[$category]); foreach ($keys as $key) { - $value = $config[$category][$key]; - if (isset($value)) { - $this->set($category, $key, $value, $source); - } + $this->set($category, $key, $config[$category][$key] ?? null, $source); } + } else { + $this->set($category, null, $config[$category], $source); } } } @@ -150,6 +149,8 @@ class Cache $data[$category][$key] = $this->config[$category][$key]; } } + } elseif (is_int($this->source[$category])) { + $data[$category] = null; } } @@ -159,40 +160,49 @@ class Cache /** * Sets a value in the config cache. Accepts raw output from the config table * - * @param string $cat Config category - * @param string $key Config key - * @param mixed $value Value to set - * @param int $source The source of the current config key + * @param string $cat Config category + * @param ?string $key Config key + * @param ?mixed $value Value to set + * @param int $source The source of the current config key * * @return bool True, if the value is set */ - public function set(string $cat, string $key, $value, int $source = self::SOURCE_DEFAULT): bool + public function set(string $cat, string $key = null, $value = null, int $source = self::SOURCE_DEFAULT): bool { - if (!isset($this->config[$cat])) { + if (!isset($this->config[$cat]) && $key !== null) { $this->config[$cat] = []; $this->source[$cat] = []; } - if (isset($this->source[$cat][$key]) && - $source < $this->source[$cat][$key]) { + if ((isset($this->source[$cat][$key]) && $source < $this->source[$cat][$key]) || + (is_int($this->source[$cat] ?? null) && $source < $this->source[$cat])) { return false; } if ($this->hidePasswordOutput && $key == 'password' && is_string($value)) { - $this->config[$cat][$key] = new HiddenString((string)$value); + $this->setCatKeyValueSource($cat, $key, new HiddenString((string)$value), $source); } else if (is_string($value)) { - $this->config[$cat][$key] = self::toConfigValue($value); + $this->setCatKeyValueSource($cat, $key, self::toConfigValue($value), $source); } else { - $this->config[$cat][$key] = $value; + $this->setCatKeyValueSource($cat, $key, $value, $source); } - $this->source[$cat][$key] = $source; - return true; } + private function setCatKeyValueSource(string $cat, string $key = null, $value = null, int $source = self::SOURCE_DEFAULT) + { + if (isset($key)) { + $this->config[$cat][$key] = $value; + $this->source[$cat][$key] = $source; + } else { + $this->config[$cat] = $value; + $this->source[$cat] = $source; + } + } + /** * Formats a DB value to a config value * - null = The db-value isn't set @@ -222,24 +232,27 @@ class Cache /** * Deletes a value from the config cache. * - * @param string $cat Config category - * @param string $key Config key + * @param string $cat Config category + * @param ?string $key Config key (if not set, the whole category will get deleted) + * @param int $source The source of the current config key * * @return bool true, if deleted */ - public function delete(string $cat, string $key): bool + public function delete(string $cat, string $key = null, int $source = self::SOURCE_DEFAULT): bool { if (isset($this->config[$cat][$key])) { - unset($this->config[$cat][$key]); - unset($this->source[$cat][$key]); - if (count($this->config[$cat]) == 0) { - unset($this->config[$cat]); - unset($this->source[$cat]); + $this->config[$cat][$key] = null; + $this->source[$cat][$key] = $source; + if (empty(array_filter($this->config[$cat], function($value) { return !is_null($value); }))) { + $this->config[$cat] = null; + $this->source[$cat] = $source; } - return true; - } else { - return false; + } elseif (isset($this->config[$cat])) { + $this->config[$cat] = null; + $this->source[$cat] = $source; } + + return true; } /** @@ -302,39 +315,9 @@ class Cache $newConfig[$category][$key] = $cache->config[$category][$key]; $newSource[$category][$key] = $cache->source[$category][$key]; } - } - } - - $newCache = new Cache(); - $newCache->config = $newConfig; - $newCache->source = $newSource; - - return $newCache; - } - - - /** - * Diffs a new Cache into the existing one and returns the diffed Cache - * - * @param Cache $cache The cache, which should get deleted for the current Cache - * - * @return Cache The diffed Cache - */ - public function diff(Cache $cache): Cache - { - $newConfig = $this->config; - $newSource = $this->source; - - $categories = array_keys($cache->config); - - foreach ($categories as $category) { - if (is_array($cache->config[$category])) { - $keys = array_keys($cache->config[$category]); - - foreach ($keys as $key) { - unset($newConfig[$category][$key]); - unset($newSource[$category][$key]); - } + } else { + $newConfig[$category] = $cache->config[$category]; + $newSource[$category] = $cache->source[$category]; } } diff --git a/tests/src/Core/Config/Cache/CacheTest.php b/tests/src/Core/Config/Cache/CacheTest.php index 024c9c7e3e..6fe2fe15f0 100644 --- a/tests/src/Core/Config/Cache/CacheTest.php +++ b/tests/src/Core/Config/Cache/CacheTest.php @@ -67,6 +67,8 @@ class CacheTest extends MockedTest $configCache = new Cache(); $configCache->load($data); + print_r($configCache); + self::assertConfigValues($data, $configCache); } @@ -111,9 +113,9 @@ class CacheTest extends MockedTest } /** - * Test the loadConfigArray() method with wrong/empty datasets + * Test the loadConfigArray() method with only a category */ - public function testLoadConfigArrayWrong() + public function testLoadConfigArrayWithOnlyCategory() { $configCache = new Cache(); @@ -123,9 +125,10 @@ class CacheTest extends MockedTest // wrong dataset $configCache->load(['system' => 'not_array']); - self::assertEmpty($configCache->getAll()); + self::assertEquals(['system' => 'not_array'], $configCache->getAll()); // incomplete dataset (key is integer ID of the array) + $configCache = new Cache(); $configCache->load(['system' => ['value']]); self::assertEquals('value', $configCache->get('system', 0)); } @@ -207,13 +210,16 @@ class CacheTest extends MockedTest { $configCache = new Cache($data); + $assertion = []; + foreach ($data as $cat => $values) { + $assertion[$cat] = null; foreach ($values as $key => $value) { $configCache->delete($cat, $key); } } - self::assertEmpty($configCache->getAll()); + self::assertEquals($assertion, $configCache->getAll()); } /** @@ -385,32 +391,6 @@ class CacheTest extends MockedTest self::assertEquals('added category', $mergedCache->get('new_category', 'test_23')); } - /** - * @dataProvider dataTests - */ - public function testDiff($data) - { - $configCache = new Cache(); - $configCache->load($data, Cache::SOURCE_FILE); - - $configCache->set('system', 'test_2','with_data', Cache::SOURCE_DATA); - $configCache->set('config', 'test_override','with_another_data', Cache::SOURCE_DATA); - - $newCache = new Cache(); - $newCache->set('config', 'test_override','override it again', Cache::SOURCE_DATA); - $newCache->set('system', 'test_3','new value', Cache::SOURCE_DATA); - - $mergedCache = $configCache->diff($newCache); - - print_r($mergedCache); - - self::assertEquals('with_data', $mergedCache->get('system', 'test_2')); - // existing entry was dropped - self::assertNull($mergedCache->get('config', 'test_override')); - // the newCache entry wasn't set, because we Diff - self::assertNull($mergedCache->get('system', 'test_3')); - } - public function dataTestCat() { return [ diff --git a/tests/src/Core/Config/Cache/ConfigFileManagerTest.php b/tests/src/Core/Config/Cache/ConfigFileManagerTest.php index 89d757e320..2adfc27ef7 100644 --- a/tests/src/Core/Config/Cache/ConfigFileManagerTest.php +++ b/tests/src/Core/Config/Cache/ConfigFileManagerTest.php @@ -441,4 +441,74 @@ class ConfigFileManagerTest extends MockedTest 'special' => $specialChars, ]], $configCache2->getDataBySource(Cache::SOURCE_DATA)); } + + /** + * If we delete something with the Cache::delete() functionality, be sure to override the underlying source as well + */ + public function testDeleteKeyOverwrite() + { + $this->delConfigFile('node.config.php'); + + $fileDir = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR; + + vfsStream::newFile('B.config.php') + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($fileDir . 'B.config.php')); + + $configFileManager = (new Config())->createConfigFileManager($this->root->url()); + $configCache = new Cache(); + + $configFileManager->setupCache($configCache); + + $configCache->delete('system', 'default_timezone', Cache::SOURCE_DATA); + + $configFileManager->saveData($configCache); + + // assert that system.default_timezone is now null, even it's set with settings.conf.php + $configCache = new Cache(); + + $configFileManager->setupCache($configCache); + + self::assertNull($configCache->get('system', 'default_timezone')); + } + + /** + * If we delete something with the Cache::delete() functionality, be sure to override the underlying source as well + */ + public function testDeleteCategoryOverwrite() + { + $this->delConfigFile('node.config.php'); + + $fileDir = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR; + + vfsStream::newFile('B.config.php') + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($fileDir . 'B.config.php')); + + $configFileManager = (new Config())->createConfigFileManager($this->root->url()); + $configCache = new Cache(); + + $configFileManager->setupCache($configCache); + + $configCache->delete('system'); + + $configFileManager->saveData($configCache); + + // assert that system.default_timezone is now null, even it's set with settings.conf.php + $configCache = new Cache(); + + $configFileManager->setupCache($configCache); + + self::assertNull($configCache->get('system', 'default_timezone')); + } } diff --git a/tests/src/Core/Config/ConfigTest.php b/tests/src/Core/Config/ConfigTest.php index d40af47d51..f7d7c070d3 100644 --- a/tests/src/Core/Config/ConfigTest.php +++ b/tests/src/Core/Config/ConfigTest.php @@ -364,7 +364,7 @@ class ConfigTest extends MockedTest self::assertNull($this->testedConfig->get('test', 'it')); self::assertNull($this->testedConfig->getCache()->get('test', 'it')); - self::assertEmpty($this->testedConfig->getCache()->getAll()); + self::assertEquals(['test' => null], $this->testedConfig->getCache()->getAll()); } /** diff --git a/tests/src/Core/Config/ConfigTransactionTest.php b/tests/src/Core/Config/ConfigTransactionTest.php index 454c760d4a..604ed7cd66 100644 --- a/tests/src/Core/Config/ConfigTransactionTest.php +++ b/tests/src/Core/Config/ConfigTransactionTest.php @@ -54,10 +54,6 @@ class ConfigTransactionTest extends MockedTest $config->set('delete', 'keyDel', 'catDel'); $configTransaction = new ConfigTransaction($config); - self::assertEquals('value1', $configTransaction->get('config', 'key1')); - self::assertEquals('value2', $configTransaction->get('system', 'key2')); - self::assertEquals('valueDel', $configTransaction->get('system', 'keyDel')); - self::assertEquals('catDel', $configTransaction->get('delete', 'keyDel')); // the config file knows it as well immediately $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; self::assertEquals('value1', $tempData['config']['key1'] ?? null); @@ -77,11 +73,6 @@ class ConfigTransactionTest extends MockedTest self::assertEquals('value1', $config->get('config', 'key1')); self::assertEquals('valueDel', $config->get('system', 'keyDel')); self::assertEquals('catDel', $config->get('delete', 'keyDel')); - // but the transaction config of course knows it - self::assertEquals('value3', $configTransaction->get('transaction', 'key3')); - self::assertEquals('changedValue1', $configTransaction->get('config', 'key1')); - self::assertNull($configTransaction->get('system', 'keyDel')); - self::assertNull($configTransaction->get('delete', 'keyDel')); // The config file still doesn't know it either $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; self::assertEquals('value1', $tempData['config']['key1'] ?? null); @@ -97,9 +88,6 @@ class ConfigTransactionTest extends MockedTest self::assertEquals('value3', $config->get('transaction', 'key3')); self::assertNull($config->get('system', 'keyDel')); self::assertNull($config->get('delete', 'keyDel')); - self::assertEquals('value3', $configTransaction->get('transaction', 'key3')); - self::assertEquals('changedValue1', $configTransaction->get('config', 'key1')); - self::assertNull($configTransaction->get('system', 'keyDel')); $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; self::assertEquals('changedValue1', $tempData['config']['key1'] ?? null); self::assertEquals('value2', $tempData['system']['key2'] ?? null); From e14050491ae8db5814ef9e7fceeabc5f75a8b8a6 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 01:10:57 +0100 Subject: [PATCH 50/71] Config fixing - unlock/close the `node.config.php` in every circumstances --- src/Core/Config/Util/ConfigFileManager.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index 569bcdb97d..e0b82e6566 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -182,16 +182,18 @@ class ConfigFileManager $content = '' . $content); if (is_array($dataArray)) { @@ -250,12 +252,12 @@ class ConfigFileManager clearstatcache(true, $filename); if (!ftruncate($configStream, 0) || !fwrite($configStream, $encodedData) || - !fflush($configStream) || - !flock($configStream, LOCK_UN)) { + !fflush($configStream)) { throw new ConfigFileException(sprintf('Cannot modify locked file %s', $filename)); } } } finally { + flock($configStream, LOCK_UN); fclose($configStream); } } From 18293280b70e9c63e76792aadac7c8db4197feaa Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 01:12:54 +0100 Subject: [PATCH 51/71] Add license --- .../src/Core/Config/ConfigTransactionTest.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/src/Core/Config/ConfigTransactionTest.php b/tests/src/Core/Config/ConfigTransactionTest.php index 604ed7cd66..c6eb7e2685 100644 --- a/tests/src/Core/Config/ConfigTransactionTest.php +++ b/tests/src/Core/Config/ConfigTransactionTest.php @@ -1,4 +1,23 @@ . + * + */ namespace Friendica\Test\src\Core\Config; From d53cb318694414961acbf7274ffe07710f5e959e Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 02:16:35 +0100 Subject: [PATCH 52/71] Update src/Core/Config/Util/ConfigFileManager.php Co-authored-by: Hypolite Petovan --- src/Core/Config/Util/ConfigFileManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index e0b82e6566..e4b84d1587 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -203,7 +203,7 @@ class ConfigFileManager } /** - * Saves overridden config entries back into the data.config.phpR + * Saves overridden config entries back into the data.config.php * * @param Cache $configCache The config cache * From c8b9e40b85767ff84c6da33233aa9bbb0bb2e880 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 02:54:28 +0100 Subject: [PATCH 53/71] remove print_r --- tests/src/Core/Config/Cache/CacheTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/src/Core/Config/Cache/CacheTest.php b/tests/src/Core/Config/Cache/CacheTest.php index 6fe2fe15f0..dc9b62dc00 100644 --- a/tests/src/Core/Config/Cache/CacheTest.php +++ b/tests/src/Core/Config/Cache/CacheTest.php @@ -67,8 +67,6 @@ class CacheTest extends MockedTest $configCache = new Cache(); $configCache->load($data); - print_r($configCache); - self::assertConfigValues($data, $configCache); } From ce8c8202217bc0d357678d1b4155d3ded6ddb5f2 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 02:53:23 +0100 Subject: [PATCH 54/71] add description --- src/Core/Config/Util/ConfigFileManager.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index e4b84d1587..eb20317cb4 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -179,8 +179,15 @@ class ConfigFileManager if (file_exists($filename)) { + // The fallback empty return content $content = '". Now we're in plain HTML again and can evaluate any PHP file :-) + */ $dataArray = eval('?>' . $content); if (is_array($dataArray)) { @@ -229,7 +244,7 @@ class ConfigFileManager $content = fread($configStream, filesize($filename)); rewind($configStream); if (!$content) { - throw new ConfigFileException(sprintf('Couldn\'t read file %s', $filename)); + throw new ConfigFileException(sprintf('Cannot read file %s', $filename)); } $dataArray = eval('?>' . $content); From b3772163d821401427f4cb3996b4bff8d89337cb Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 03:06:11 +0100 Subject: [PATCH 55/71] Add doc --- src/Core/Config/Util/ConfigFileManager.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index eb20317cb4..cdef2dd699 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -234,14 +234,31 @@ class ConfigFileManager $fileExists = false; } + /** + * Creates a read-write stream + * + * @see https://www.php.net/manual/en/function.fopen.php + * @note Open the file for reading and writing. If the file does not exist, it is created. + * If it exists, it is neither truncated (as opposed to 'w'), nor the call to this function fails + * (as is the case with 'x'). The file pointer is positioned on the beginning of the file. + * + */ $configStream = fopen($filename, 'c+'); try { + // We do want an exclusive lock, so we wait until every LOCK_SH (config reading) is unlocked if (flock($configStream, LOCK_EX)) { + /** + * If the file exists, read the whole file again + * Since we're currently exclusive locked, no other process can now change the config again + */ if ($fileExists) { + // When reading the config file too fast, we get a wrong filesize, "clearstatcache" prevents that clearstatcache(true, $filename); $content = fread($configStream, filesize($filename)); + // Event truncating the whole content wouldn't automatically rewind the stream, + // so we need to do it manually rewind($configStream); if (!$content) { throw new ConfigFileException(sprintf('Cannot read file %s', $filename)); @@ -249,6 +266,8 @@ class ConfigFileManager $dataArray = eval('?>' . $content); + // Merge the new content into the existing file based config cache and use it + // as the new config cache if (is_array($dataArray)) { $fileConfigCache = new Cache(); $fileConfigCache->load($dataArray, Cache::SOURCE_DATA); @@ -256,6 +275,7 @@ class ConfigFileManager } } + // Only SOURCE_DATA is wanted, the rest isn't part of the node.config.php file $data = $configCache->getDataBySource(Cache::SOURCE_DATA); $encodedData = ConfigFileTransformer::encode($data); @@ -264,6 +284,7 @@ class ConfigFileManager throw new ConfigFileException('config source cannot get encoded'); } + // Once again to avoid wrong, implicit "filesize" calls during the fwrite() or ftruncate() call clearstatcache(true, $filename); if (!ftruncate($configStream, 0) || !fwrite($configStream, $encodedData) || @@ -272,6 +293,7 @@ class ConfigFileManager } } } finally { + // unlock and close the stream for every circumstances flock($configStream, LOCK_UN); fclose($configStream); } From 9462bfa763b67cb937e2d426095ff192654bc234 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 12:42:43 +0100 Subject: [PATCH 56/71] Update src/Core/Config/Util/ConfigFileManager.php Co-authored-by: Hypolite Petovan --- src/Core/Config/Util/ConfigFileManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index cdef2dd699..c7e63a63b0 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -250,7 +250,7 @@ class ConfigFileManager if (flock($configStream, LOCK_EX)) { /** - * If the file exists, read the whole file again + * If the file exists, we read the whole file again to avoid a race condition with concurrent threads that could have modified the file between the first config read of this thread and now * Since we're currently exclusive locked, no other process can now change the config again */ if ($fileExists) { From 70704ccb1911ec9aabe7921376907c03b2bf37df Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 12:42:56 +0100 Subject: [PATCH 57/71] Update src/Core/Update.php Co-authored-by: Hypolite Petovan --- src/Core/Update.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Core/Update.php b/src/Core/Update.php index d440136b55..03badb1b5d 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -162,14 +162,6 @@ class Update // Checks if the build changed during Lock acquiring (so no double update occurs) $retryBuild = DI::config()->get('system', 'build'); - // legacy option - check if there's something in the Config table - if (DBStructure::existsTable('config')) { - $dbConfig = DBA::selectFirst('config', ['v'], ['cat' => 'system', 'k' => 'build']); - if (!empty($dbConfig)) { - $retryBuild = $dbConfig['v']; - } - } - if ($retryBuild !== $build) { Logger::notice('Update already done.', ['from' => $stored, 'to' => $current]); DI::lock()->release('dbupdate'); From 05048d4abf93169c5e0c3bc464a9b8548563d4f7 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 12:43:04 +0100 Subject: [PATCH 58/71] Update src/Core/Config/ValueObject/Cache.php Co-authored-by: Hypolite Petovan --- src/Core/Config/ValueObject/Cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index ca76bb4edd..2082511641 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -182,7 +182,7 @@ class Cache if ($this->hidePasswordOutput && $key == 'password' && is_string($value)) { - $this->setCatKeyValueSource($cat, $key, new HiddenString((string)$value), $source); + $this->setCatKeyValueSource($cat, $key, new HiddenString($value), $source); } else if (is_string($value)) { $this->setCatKeyValueSource($cat, $key, self::toConfigValue($value), $source); } else { From beb3d376b23a02c0256b58d6cd6b3a99d8d85af4 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 12:46:06 +0100 Subject: [PATCH 59/71] Apply suggestions from code review Co-authored-by: Hypolite Petovan --- src/Core/Config/Util/ConfigFileManager.php | 8 ++++---- src/Core/Config/Util/ConfigFileTransformer.php | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index c7e63a63b0..3a4ebb8931 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -257,13 +257,14 @@ class ConfigFileManager // When reading the config file too fast, we get a wrong filesize, "clearstatcache" prevents that clearstatcache(true, $filename); $content = fread($configStream, filesize($filename)); - // Event truncating the whole content wouldn't automatically rewind the stream, - // so we need to do it manually - rewind($configStream); if (!$content) { throw new ConfigFileException(sprintf('Cannot read file %s', $filename)); } + // Event truncating the whole content wouldn't automatically rewind the stream, + // so we need to do it manually + rewind($configStream); + $dataArray = eval('?>' . $content); // Merge the new content into the existing file based config cache and use it @@ -279,7 +280,6 @@ class ConfigFileManager $data = $configCache->getDataBySource(Cache::SOURCE_DATA); $encodedData = ConfigFileTransformer::encode($data); - if (!$encodedData) { throw new ConfigFileException('config source cannot get encoded'); } diff --git a/src/Core/Config/Util/ConfigFileTransformer.php b/src/Core/Config/Util/ConfigFileTransformer.php index 4eaafe0610..ac4990df13 100644 --- a/src/Core/Config/Util/ConfigFileTransformer.php +++ b/src/Core/Config/Util/ConfigFileTransformer.php @@ -34,7 +34,6 @@ class ConfigFileTransformer $categories = array_keys($data); foreach ($categories as $category) { - if (is_null($data[$category])) { $dataString .= "\t'$category' => null," . PHP_EOL; continue; From c35fd68ec24638eae0520ed020d0f89b3f628bc0 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 12:47:00 +0100 Subject: [PATCH 60/71] Adapt doc --- src/Core/Config/Util/ConfigFileManager.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index 3a4ebb8931..f9d3b32b64 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -203,11 +203,12 @@ class ConfigFileManager } /** - * Evaluate the fetched content + * Evaluate the content string as PHP code + * + * @see https://www.php.net/manual/en/function.eval.php * * @note - * Because `eval()` directly evaluates PHP content, we need to "close" the expected PHP content again with - * the prefixed "?>". Now we're in plain HTML again and can evaluate any PHP file :-) + * To leave the PHP mode, we have to use the appropriate PHP tags '?>' as prefix. */ $dataArray = eval('?>' . $content); From baf3225916dc44cca95aaf5a6c2d73b1a037b90a Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 12:50:14 +0100 Subject: [PATCH 61/71] Apply Update::check() suggestions --- bin/auth_ejabberd.php | 2 +- bin/daemon.php | 4 ---- bin/worker.php | 2 +- index.php | 2 +- src/App.php | 2 +- src/Core/Update.php | 2 +- 6 files changed, 5 insertions(+), 9 deletions(-) diff --git a/bin/auth_ejabberd.php b/bin/auth_ejabberd.php index 9c5dc05285..40e7d3b97c 100755 --- a/bin/auth_ejabberd.php +++ b/bin/auth_ejabberd.php @@ -84,7 +84,7 @@ $dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['auth_ejabb \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); // Check the database structure and possibly fixes it -\Friendica\Core\Update::check(\Friendica\DI::basePath(), true, \Friendica\DI::mode()); +\Friendica\Core\Update::check(\Friendica\DI::basePath(), true); $appMode = $dice->create(Mode::class); diff --git a/bin/daemon.php b/bin/daemon.php index 6237df49de..b970f4a758 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -33,7 +33,6 @@ if (php_sapi_name() !== 'cli') { use Dice\Dice; use Friendica\App\Mode; use Friendica\Core\Logger; -use Friendica\Core\Update; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; @@ -65,9 +64,6 @@ $dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['daemon']]) DI::init($dice); \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); -// Check the database structure and possibly fixes it -Update::check(DI::basePath(), true, DI::mode()); - if (DI::mode()->isInstall()) { die("Friendica isn't properly installed yet.\n"); } diff --git a/bin/worker.php b/bin/worker.php index aceeff4818..de207ae98f 100755 --- a/bin/worker.php +++ b/bin/worker.php @@ -62,7 +62,7 @@ DI::init($dice); DI::mode()->setExecutor(Mode::WORKER); // Check the database structure and possibly fixes it -Update::check(DI::basePath(), true, DI::mode()); +Update::check(DI::basePath(), true); // Quit when in maintenance if (!DI::mode()->has(App\Mode::MAINTENANCEDISABLED)) { diff --git a/index.php b/index.php index 10f04996af..e0f33666b4 100644 --- a/index.php +++ b/index.php @@ -37,7 +37,7 @@ $dice = $dice->addRule(Friendica\App\Mode::class, ['call' => [['determineRunMode \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); // Check the database structure and possibly fixes it -\Friendica\Core\Update::check(\Friendica\DI::basePath(), true, \Friendica\DI::mode()); +\Friendica\Core\Update::check(\Friendica\DI::basePath(), true); $a = \Friendica\DI::app(); diff --git a/src/App.php b/src/App.php index fc70f920de..224a895e29 100644 --- a/src/App.php +++ b/src/App.php @@ -659,7 +659,7 @@ class App $this->baseURL->redirect('install'); } else { $this->checkURL(); - Core\Update::check($this->getBasePath(), false, $this->mode); + Core\Update::check($this->getBasePath(), false); Core\Addon::loadAddons(); Core\Hook::loadHooks(); } diff --git a/src/Core/Update.php b/src/Core/Update.php index 03badb1b5d..eb207b6c25 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -47,7 +47,7 @@ class Update * @return void * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function check(string $basePath, bool $via_worker, App\Mode $mode) + public static function check(string $basePath, bool $via_worker) { if (!DBA::connected()) { return; From 081dbae7c2a206dc69b1e8d4ceb9a69d5113e05d Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 12:50:32 +0100 Subject: [PATCH 62/71] Apply Update::check() suggestions --- index.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/index.php b/index.php index e0f33666b4..fa6d8b0871 100644 --- a/index.php +++ b/index.php @@ -36,9 +36,6 @@ $dice = $dice->addRule(Friendica\App\Mode::class, ['call' => [['determineRunMode \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); -// Check the database structure and possibly fixes it -\Friendica\Core\Update::check(\Friendica\DI::basePath(), true); - $a = \Friendica\DI::app(); \Friendica\DI::mode()->setExecutor(\Friendica\App\Mode::INDEX); From b2e14f209be1873bbe45cce2bcfb9db3eec04afb Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 12:50:14 +0100 Subject: [PATCH 63/71] Move Update::check() into daemon loop --- bin/daemon.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bin/daemon.php b/bin/daemon.php index b970f4a758..7182cb8eca 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -33,6 +33,7 @@ if (php_sapi_name() !== 'cli') { use Dice\Dice; use Friendica\App\Mode; use Friendica\Core\Logger; +use Friendica\Core\Update; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; @@ -192,6 +193,9 @@ $last_cron = 0; // Now running as a daemon. while (true) { + // Check the database structure and possibly fixes it + Update::check(DI::basePath(), true); + if (!$do_cron && ($last_cron + $wait_interval) < time()) { Logger::info('Forcing cron worker call.', ['pid' => $pid]); $do_cron = true; From 5b2e02889ea656707e96a04b09cd6321f5a223dc Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 6 Jan 2023 17:50:56 +0100 Subject: [PATCH 64/71] Fix Update::run() --- src/Core/Update.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Core/Update.php b/src/Core/Update.php index eb207b6c25..cef72f34bf 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -162,10 +162,20 @@ class Update // Checks if the build changed during Lock acquiring (so no double update occurs) $retryBuild = DI::config()->get('system', 'build'); - if ($retryBuild !== $build) { - Logger::notice('Update already done.', ['from' => $stored, 'to' => $current]); - DI::lock()->release('dbupdate'); - return ''; + if ($retryBuild != $build) { + // legacy option - check if there's something in the Config table + if (DBStructure::existsTable('config')) { + $dbConfig = DBA::selectFirst('config', ['v'], ['cat' => 'system', 'k' => 'build']); + if (!empty($dbConfig)) { + $retryBuild = intval($dbConfig['v']); + } + } + + if ($retryBuild != $build) { + Logger::notice('Update already done.', ['from' => $build, 'retry' => $retryBuild, 'to' => $current]); + DI::lock()->release('dbupdate'); + return ''; + } } DI::config()->set('system', 'maintenance', 1); From 80e8f4aa34c4ccc5297e1b4c06735f0badc34a86 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sat, 7 Jan 2023 13:43:16 +0100 Subject: [PATCH 65/71] Execute critical worker tasks, even if we're in daemon mode --- src/Core/Worker.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Worker.php b/src/Core/Worker.php index 2db9256d13..90bce0a88e 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -1315,8 +1315,8 @@ class Worker return $added; } - // Quit on daemon mode - if (Worker\Daemon::isMode()) { + // Quit on daemon mode, except the priority is critical (like for db updates) + if (Worker\Daemon::isMode() && $priority !== self::PRIORITY_CRITICAL) { return $added; } From 6e0d16f22b94bb528185d16d61c2ed93de38fcbd Mon Sep 17 00:00:00 2001 From: Philipp Date: Sat, 7 Jan 2023 15:16:55 +0100 Subject: [PATCH 66/71] Add warning message in case node.config.php isn't writable --- src/Core/Config/Util/ConfigFileManager.php | 27 ++++++++++++++++++++-- src/DI.php | 5 ++++ src/Module/Admin/Summary.php | 5 ++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index f9d3b32b64..8705bf76e7 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -218,6 +218,22 @@ class ConfigFileManager } } + /** + * Checks, if the node.config.php is writable + * + * @return bool + */ + public function dataIsWritable(): bool + { + $filename = $this->configDir . '/' . self::CONFIG_DATA_FILE; + + if (file_exists($filename)) { + return is_writable($filename); + } else { + return is_writable($this->configDir); + } + } + /** * Saves overridden config entries back into the data.config.php * @@ -229,6 +245,11 @@ class ConfigFileManager { $filename = $this->configDir . '/' . self::CONFIG_DATA_FILE; + // fail at a early stage, if we already know that we cannot save the data + if (!$this->dataIsWritable()) { + throw new ConfigFileException(sprintf('Cannot open file "%s" in mode c+', $filename)); + } + if (file_exists($filename)) { $fileExists = true; } else { @@ -238,13 +259,15 @@ class ConfigFileManager /** * Creates a read-write stream * - * @see https://www.php.net/manual/en/function.fopen.php + * @see https://www.php.net/manual/en/function.fopen.php * @note Open the file for reading and writing. If the file does not exist, it is created. * If it exists, it is neither truncated (as opposed to 'w'), nor the call to this function fails * (as is the case with 'x'). The file pointer is positioned on the beginning of the file. * */ - $configStream = fopen($filename, 'c+'); + if (($configStream = @fopen($filename, 'c+')) !== false) { + throw new ConfigFileException(sprintf('Cannot open file "%s" in mode c+', $filename)); + } try { // We do want an exclusive lock, so we wait until every LOCK_SH (config reading) is unlocked diff --git a/src/DI.php b/src/DI.php index 0ef18a1aa3..6fd0e3a7ad 100644 --- a/src/DI.php +++ b/src/DI.php @@ -181,6 +181,11 @@ abstract class DI return self::$dice->create(Core\Config\Capability\IManageConfigValues::class); } + public static function configFileManager(): Core\Config\Util\ConfigFileManager + { + return self::$dice->create(Core\Config\Util\ConfigFileManager::class); + } + public static function keyValue(): Core\KeyValueStorage\Capabilities\IManageKeyValuePairs { return self::$dice->create(Core\KeyValueStorage\Capabilities\IManageKeyValuePairs::class); diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index 038628ee1e..325e392a18 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -23,6 +23,7 @@ namespace Friendica\Module\Admin; use Friendica\App; use Friendica\Core\Addon; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Core\Logger; use Friendica\Core\Renderer; @@ -114,6 +115,10 @@ class Summary extends BaseAdmin $warningtext[] = DI::l10n()->t('Friendica\'s configuration now is stored in config/local.config.php, please copy config/local-sample.config.php and move your config from config/local.ini.php. See the Config help page for help with the transition.', DI::baseUrl()->get() . '/help/Config'); } + if (!DI::configFileManager()->dataIsWritable()) { + $warningtext[] = DI::l10n()->t('Friendica\'s configuration store "%s" isn\'t writable. Beware that updates, gui changes and console changes aren\'t working reliable.', ConfigFileManager::CONFIG_DATA_FILE); + } + // Check server vitality if (!self::checkSelfHostMeta()) { $well_known = DI::baseUrl()->get() . Probe::HOST_META; From f2253991e728a0afa1bcbc795f95492fc9f581d6 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sat, 7 Jan 2023 15:18:23 +0100 Subject: [PATCH 67/71] Update messages.po --- view/lang/C/messages.po | 73 ++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index d10d339fc6..076587da7b 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2023.03-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-05 10:33-0500\n" +"POT-Creation-Date: 2023-01-07 14:18+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -2731,36 +2731,36 @@ msgstr "" msgid "Enter a valid existing folder" msgstr "" -#: src/Core/Update.php:70 +#: src/Core/Update.php:80 #, php-format msgid "" "Updates from version %s are not supported. Please update at least to version " "2021.01 and wait until the postupdate finished version 1383." msgstr "" -#: src/Core/Update.php:81 +#: src/Core/Update.php:91 #, php-format msgid "" "Updates from postupdate version %s are not supported. Please update at least " "to version 2021.01 and wait until the postupdate finished version 1383." msgstr "" -#: src/Core/Update.php:156 +#: src/Core/Update.php:186 #, php-format msgid "%s: executing pre update %d" msgstr "" -#: src/Core/Update.php:198 +#: src/Core/Update.php:228 #, php-format msgid "%s: executing post update %d" msgstr "" -#: src/Core/Update.php:272 +#: src/Core/Update.php:302 #, php-format msgid "Update %s failed. See error logs." msgstr "" -#: src/Core/Update.php:312 +#: src/Core/Update.php:342 #, php-format msgid "" "\n" @@ -2772,16 +2772,16 @@ msgid "" "might be invalid." msgstr "" -#: src/Core/Update.php:318 +#: src/Core/Update.php:348 #, php-format msgid "The error message is\\n[pre]%s[/pre]" msgstr "" -#: src/Core/Update.php:322 src/Core/Update.php:350 +#: src/Core/Update.php:352 src/Core/Update.php:380 msgid "[Friendica Notify] Database update" msgstr "" -#: src/Core/Update.php:344 +#: src/Core/Update.php:374 #, php-format msgid "" "\n" @@ -3265,7 +3265,7 @@ msgstr "" msgid "Title/Description:" msgstr "" -#: src/Model/Profile.php:1023 src/Module/Admin/Summary.php:217 +#: src/Model/Profile.php:1023 src/Module/Admin/Summary.php:222 #: src/Module/Moderation/Summary.php:77 msgid "Summary" msgstr "" @@ -3593,7 +3593,7 @@ msgstr "" #: src/Module/Admin/Federation.php:207 src/Module/Admin/Logs/Settings.php:79 #: src/Module/Admin/Logs/View.php:84 src/Module/Admin/Queue.php:72 #: src/Module/Admin/Site.php:435 src/Module/Admin/Storage.php:138 -#: src/Module/Admin/Summary.php:216 src/Module/Admin/Themes/Details.php:90 +#: src/Module/Admin/Summary.php:221 src/Module/Admin/Themes/Details.php:90 #: src/Module/Admin/Themes/Index.php:111 src/Module/Admin/Tos.php:77 #: src/Module/Moderation/Users/Create.php:61 #: src/Module/Moderation/Users/Pending.php:96 @@ -4976,12 +4976,12 @@ msgstr "" msgid "Database (legacy)" msgstr "" -#: src/Module/Admin/Summary.php:56 +#: src/Module/Admin/Summary.php:57 #, php-format msgid "Template engine (%s) error: %s" msgstr "" -#: src/Module/Admin/Summary.php:60 +#: src/Module/Admin/Summary.php:61 #, php-format msgid "" "Your DB still runs with MyISAM tables. You should change the engine type to " @@ -4992,7 +4992,7 @@ msgid "" "automatic conversion.
" msgstr "" -#: src/Module/Admin/Summary.php:65 +#: src/Module/Admin/Summary.php:66 #, php-format msgid "" "Your DB still runs with InnoDB tables in the Antelope file format. You " @@ -5003,7 +5003,7 @@ msgid "" "installation for an automatic conversion.
" msgstr "" -#: src/Module/Admin/Summary.php:75 +#: src/Module/Admin/Summary.php:76 #, php-format msgid "" "Your table_definition_cache is too low (%d). This can lead to the database " @@ -5011,39 +5011,39 @@ msgid "" "to %d. See here for more information.
" msgstr "" -#: src/Module/Admin/Summary.php:85 +#: src/Module/Admin/Summary.php:86 #, php-format msgid "" "There is a new version of Friendica available for download. Your current " "version is %1$s, upstream version is %2$s" msgstr "" -#: src/Module/Admin/Summary.php:94 +#: src/Module/Admin/Summary.php:95 msgid "" "The database update failed. Please run \"php bin/console.php dbstructure " "update\" from the command line and have a look at the errors that might " "appear." msgstr "" -#: src/Module/Admin/Summary.php:98 +#: src/Module/Admin/Summary.php:99 msgid "" "The last update failed. Please run \"php bin/console.php dbstructure update" "\" from the command line and have a look at the errors that might appear. " "(Some of the errors are possibly inside the logfile.)" msgstr "" -#: src/Module/Admin/Summary.php:103 +#: src/Module/Admin/Summary.php:104 msgid "The worker was never executed. Please check your database structure!" msgstr "" -#: src/Module/Admin/Summary.php:105 +#: src/Module/Admin/Summary.php:106 #, php-format msgid "" "The last worker execution was on %s UTC. This is older than one hour. Please " "check your crontab settings." msgstr "" -#: src/Module/Admin/Summary.php:110 +#: src/Module/Admin/Summary.php:111 #, php-format msgid "" "Friendica's configuration now is stored in config/local.config.php, please " @@ -5052,7 +5052,7 @@ msgid "" "with the transition." msgstr "" -#: src/Module/Admin/Summary.php:114 +#: src/Module/Admin/Summary.php:115 #, php-format msgid "" "Friendica's configuration now is stored in config/local.config.php, please " @@ -5061,7 +5061,14 @@ msgid "" "with the transition." msgstr "" -#: src/Module/Admin/Summary.php:120 +#: src/Module/Admin/Summary.php:119 +#, php-format +msgid "" +"Friendica's configuration store \"%s\" isn't writable. Beware that updates, " +"gui changes and console changes aren't working reliable." +msgstr "" + +#: src/Module/Admin/Summary.php:125 #, php-format msgid "" "%s is not reachable on your system. This is a severe " @@ -5069,50 +5076,50 @@ msgid "" "href=\"%s\">the installation page for help." msgstr "" -#: src/Module/Admin/Summary.php:138 +#: src/Module/Admin/Summary.php:143 #, php-format msgid "The logfile '%s' is not usable. No logging possible (error: '%s')" msgstr "" -#: src/Module/Admin/Summary.php:152 +#: src/Module/Admin/Summary.php:157 #, php-format msgid "The debug logfile '%s' is not usable. No logging possible (error: '%s')" msgstr "" -#: src/Module/Admin/Summary.php:168 +#: src/Module/Admin/Summary.php:173 #, php-format msgid "" "Friendica's system.basepath was updated from '%s' to '%s'. Please remove the " "system.basepath from your db to avoid differences." msgstr "" -#: src/Module/Admin/Summary.php:176 +#: src/Module/Admin/Summary.php:181 #, php-format msgid "" "Friendica's current system.basepath '%s' is wrong and the config file '%s' " "isn't used." msgstr "" -#: src/Module/Admin/Summary.php:184 +#: src/Module/Admin/Summary.php:189 #, php-format msgid "" "Friendica's current system.basepath '%s' is not equal to the config file " "'%s'. Please fix your configuration." msgstr "" -#: src/Module/Admin/Summary.php:195 +#: src/Module/Admin/Summary.php:200 msgid "Message queues" msgstr "" -#: src/Module/Admin/Summary.php:201 +#: src/Module/Admin/Summary.php:206 msgid "Server Settings" msgstr "" -#: src/Module/Admin/Summary.php:219 +#: src/Module/Admin/Summary.php:224 msgid "Version" msgstr "" -#: src/Module/Admin/Summary.php:223 +#: src/Module/Admin/Summary.php:228 msgid "Active addons" msgstr "" From b7a2b6b3527faa309a3498c887ee5c31cbdb7ce7 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sat, 7 Jan 2023 15:28:49 +0100 Subject: [PATCH 68/71] Update src/Module/Admin/Summary.php Co-authored-by: Hypolite Petovan --- src/Module/Admin/Summary.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index 325e392a18..e7c0dea517 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -116,7 +116,7 @@ class Summary extends BaseAdmin } if (!DI::configFileManager()->dataIsWritable()) { - $warningtext[] = DI::l10n()->t('Friendica\'s configuration store "%s" isn\'t writable. Beware that updates, gui changes and console changes aren\'t working reliable.', ConfigFileManager::CONFIG_DATA_FILE); + $warningtext[] = DI::l10n()->t('Friendica\'s configuration store "%s" isn\'t writable. Until then database updates won't be applied automatically, admin settings and console configuration changes won't be saved.', ConfigFileManager::CONFIG_DATA_FILE); } // Check server vitality From 6e4e2c4a82384b614cda8b1fabe7288f117113c1 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sat, 7 Jan 2023 15:30:45 +0100 Subject: [PATCH 69/71] Fix warning text --- src/Core/Config/Util/ConfigFileManager.php | 5 ----- src/Module/Admin/Summary.php | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index 8705bf76e7..9e07d27aec 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -245,11 +245,6 @@ class ConfigFileManager { $filename = $this->configDir . '/' . self::CONFIG_DATA_FILE; - // fail at a early stage, if we already know that we cannot save the data - if (!$this->dataIsWritable()) { - throw new ConfigFileException(sprintf('Cannot open file "%s" in mode c+', $filename)); - } - if (file_exists($filename)) { $fileExists = true; } else { diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index e7c0dea517..13ef0fd716 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -116,7 +116,7 @@ class Summary extends BaseAdmin } if (!DI::configFileManager()->dataIsWritable()) { - $warningtext[] = DI::l10n()->t('Friendica\'s configuration store "%s" isn\'t writable. Until then database updates won't be applied automatically, admin settings and console configuration changes won't be saved.', ConfigFileManager::CONFIG_DATA_FILE); + $warningtext[] = DI::l10n()->t('Friendica\'s configuration store "%s" isn\'t writable. Until then database updates won\'t be applied automatically, admin settings and console configuration changes won\'t be saved.', ConfigFileManager::CONFIG_DATA_FILE); } // Check server vitality From 6bd1740a94a694ca4fcb8ef21ee9536a80925f8f Mon Sep 17 00:00:00 2001 From: Philipp Date: Sat, 7 Jan 2023 15:49:55 +0100 Subject: [PATCH 70/71] omg .. wrong assertion .. --- src/Core/Config/Util/ConfigFileManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index 9e07d27aec..f3627cf477 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -260,7 +260,7 @@ class ConfigFileManager * (as is the case with 'x'). The file pointer is positioned on the beginning of the file. * */ - if (($configStream = @fopen($filename, 'c+')) !== false) { + if (($configStream = @fopen($filename, 'c+')) === false) { throw new ConfigFileException(sprintf('Cannot open file "%s" in mode c+', $filename)); } From 7b30c44b60e943de7e8b2bb473247d4eaf2f8a7f Mon Sep 17 00:00:00 2001 From: Philipp Date: Sat, 7 Jan 2023 15:50:03 +0100 Subject: [PATCH 71/71] Update messages.po --- view/lang/C/messages.po | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index 076587da7b..091e61a7f4 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2023.03-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-07 14:18+0000\n" +"POT-Creation-Date: 2023-01-07 14:32+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -5064,8 +5064,9 @@ msgstr "" #: src/Module/Admin/Summary.php:119 #, php-format msgid "" -"Friendica's configuration store \"%s\" isn't writable. Beware that updates, " -"gui changes and console changes aren't working reliable." +"Friendica's configuration store \"%s\" isn't writable. Until then database " +"updates won't be applied automatically, admin settings and console " +"configuration changes won't be saved." msgstr "" #: src/Module/Admin/Summary.php:125