From 0f93a467a7ddaa0b6b3557efce01b4a37f7dfe1a Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sat, 23 Mar 2019 15:20:51 +0100 Subject: [PATCH 1/8] Rename basepath to getBasePath() --- src/App.php | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/App.php b/src/App.php index 4f55b558c..2e149edef 100644 --- a/src/App.php +++ b/src/App.php @@ -187,7 +187,7 @@ class App */ public function registerStylesheet($path) { - $url = str_replace($this->basePath . DIRECTORY_SEPARATOR, '', $path); + $url = str_replace($this->getBasePath() . DIRECTORY_SEPARATOR, '', $path); $this->stylesheets[] = trim($url, '/'); } @@ -204,7 +204,7 @@ class App */ public function registerFooterScript($path) { - $url = str_replace($this->basePath . DIRECTORY_SEPARATOR, '', $path); + $url = str_replace($this->getBasePath() . DIRECTORY_SEPARATOR, '', $path); $this->footerScripts[] = trim($url, '/'); } @@ -236,10 +236,10 @@ class App $cfgBasePath = $this->config->get('system', 'basepath'); $this->basePath = !empty($cfgBasePath) ? $cfgBasePath : $basePath; - if (!Core\System::isDirectoryUsable($this->basePath, false)) { - throw new Exception('Basepath \'' . $this->basePath . '\' isn\'t usable.'); + if (!Core\System::isDirectoryUsable($this->getBasePath(), false)) { + throw new Exception('Basepath \'' . $this->getBasePath() . '\' isn\'t usable.'); } - $this->basePath = rtrim($this->basePath, DIRECTORY_SEPARATOR); + $this->basePath = rtrim($this->getBasePath(), DIRECTORY_SEPARATOR); $this->checkBackend($isBackend); $this->checkFriendicaApp(); @@ -275,9 +275,9 @@ class App set_include_path( get_include_path() . PATH_SEPARATOR - . $this->basePath . DIRECTORY_SEPARATOR . 'include' . PATH_SEPARATOR - . $this->basePath . DIRECTORY_SEPARATOR . 'library' . PATH_SEPARATOR - . $this->basePath); + . $this->getBasePath() . DIRECTORY_SEPARATOR . 'include' . PATH_SEPARATOR + . $this->getBasePath() . DIRECTORY_SEPARATOR . 'library' . PATH_SEPARATOR + . $this->getBasePath()); if (!empty($_SERVER['QUERY_STRING']) && strpos($_SERVER['QUERY_STRING'], 'pagename=') === 0) { $this->query_string = substr($_SERVER['QUERY_STRING'], 9); @@ -352,10 +352,10 @@ class App { $this->determineURLPath(); - $this->getMode()->determine($this->basePath); + $this->getMode()->determine($this->getBasePath()); if ($this->getMode()->has(App\Mode::DBAVAILABLE)) { - $loader = new ConfigCacheLoader($this->basePath, $this->getMode()); + $loader = new ConfigCacheLoader($this->getBasePath(), $this->getMode()); $this->config->getCache()->load($loader->loadCoreConfig('addon'), true); $this->profiler->update( @@ -363,7 +363,7 @@ class App $this->config->get('rendertime', 'callstack', false)); Core\Hook::loadHooks(); - $loader = new ConfigCacheLoader($this->basePath, $this->mode); + $loader = new ConfigCacheLoader($this->getBasePath(), $this->mode); Core\Hook::callAll('load_config', $loader); } @@ -516,8 +516,8 @@ class App $this->urlPath = trim($parsed['path'], '\\/'); } - if (file_exists($this->basePath . '/.htpreconfig.php')) { - include $this->basePath . '/.htpreconfig.php'; + if (file_exists($this->getBasePath() . '/.htpreconfig.php')) { + include $this->getBasePath() . '/.htpreconfig.php'; } if (Core\Config::get('config', 'hostname') != '') { @@ -914,9 +914,9 @@ class App } if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - $resource = proc_open('cmd /c start /b ' . $cmdline, [], $foo, $this->basePath); + $resource = proc_open('cmd /c start /b ' . $cmdline, [], $foo, $this->getBasePath()); } else { - $resource = proc_open($cmdline . ' &', [], $foo, $this->basePath); + $resource = proc_open($cmdline . ' &', [], $foo, $this->getBasePath()); } if (!is_resource($resource)) { Core\Logger::log('We got no resource for command ' . $cmdline, Core\Logger::DEBUG); @@ -1199,7 +1199,7 @@ class App $this->module = 'maintenance'; } else { $this->checkURL(); - Core\Update::check($this->basePath, false); + Core\Update::check($this->getBasePath(), false); Core\Addon::loadAddons(); Core\Hook::loadHooks(); } From dda26a46f58bc2f11095ef15b52f6e8c9b33f597 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sat, 23 Mar 2019 15:23:23 +0100 Subject: [PATCH 2/8] Rename Core\Conifg to $this->config --- src/App.php | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/App.php b/src/App.php index 2e149edef..7893cbe70 100644 --- a/src/App.php +++ b/src/App.php @@ -465,14 +465,14 @@ class App { $scheme = $this->scheme; - if (Core\Config::get('system', 'ssl_policy') == SSL_POLICY_FULL) { + if ($this->config->get('system', 'ssl_policy') == SSL_POLICY_FULL) { $scheme = 'https'; } // Basically, we have $ssl = true on any links which can only be seen by a logged in user // (and also the login link). Anything seen by an outsider will have it turned off. - if (Core\Config::get('system', 'ssl_policy') == SSL_POLICY_SELFSIGN) { + if ($this->config->get('system', 'ssl_policy') == SSL_POLICY_SELFSIGN) { if ($ssl) { $scheme = 'https'; } else { @@ -480,8 +480,8 @@ class App } } - if (Core\Config::get('config', 'hostname') != '') { - $this->hostname = Core\Config::get('config', 'hostname'); + if ($this->config->get('config', 'hostname') != '') { + $this->hostname = $this->config->get('config', 'hostname'); } return $scheme . '://' . $this->hostname . (!empty($this->getURLPath()) ? '/' . $this->getURLPath() : '' ); @@ -520,8 +520,8 @@ class App include $this->getBasePath() . '/.htpreconfig.php'; } - if (Core\Config::get('config', 'hostname') != '') { - $this->hostname = Core\Config::get('config', 'hostname'); + if ($this->config->get('config', 'hostname') != '') { + $this->hostname = $this->config->get('config', 'hostname'); } if (!isset($this->hostname) || ($this->hostname == '')) { @@ -532,8 +532,8 @@ class App public function getHostName() { - if (Core\Config::get('config', 'hostname') != '') { - $this->hostname = Core\Config::get('config', 'hostname'); + if ($this->config->get('config', 'hostname') != '') { + $this->hostname = $this->config->get('config', 'hostname'); } return $this->hostname; @@ -583,12 +583,12 @@ class App $this->registerStylesheet($stylesheet); - $shortcut_icon = Core\Config::get('system', 'shortcut_icon'); + $shortcut_icon = $this->config->get('system', 'shortcut_icon'); if ($shortcut_icon == '') { $shortcut_icon = 'images/friendica-32.png'; } - $touch_icon = Core\Config::get('system', 'touch_icon'); + $touch_icon = $this->config->get('system', 'touch_icon'); if ($touch_icon == '') { $touch_icon = 'images/friendica-128.png'; } @@ -608,7 +608,7 @@ class App '$update_interval' => $interval, '$shortcut_icon' => $shortcut_icon, '$touch_icon' => $touch_icon, - '$block_public' => intval(Core\Config::get('system', 'block_public')), + '$block_public' => intval($this->config->get('system', 'block_public')), '$stylesheets' => $this->stylesheets, ]) . $this->page['htmlhead']; } @@ -781,13 +781,13 @@ class App * if ($this->is_backend()) { $process = 'backend'; - $max_processes = Core\Config::get('system', 'max_processes_backend'); + $max_processes = $this->config->get('system', 'max_processes_backend'); if (intval($max_processes) == 0) { $max_processes = 5; } } else { $process = 'frontend'; - $max_processes = Core\Config::get('system', 'max_processes_frontend'); + $max_processes = $this->config->get('system', 'max_processes_frontend'); if (intval($max_processes) == 0) { $max_processes = 20; } @@ -814,7 +814,7 @@ class App */ public function isMinMemoryReached() { - $min_memory = Core\Config::get('system', 'min_memory', 0); + $min_memory = $this->config->get('system', 'min_memory', 0); if ($min_memory == 0) { return false; } @@ -861,13 +861,13 @@ class App { if ($this->isBackend()) { $process = 'backend'; - $maxsysload = intval(Core\Config::get('system', 'maxloadavg')); + $maxsysload = intval($this->config->get('system', 'maxloadavg')); if ($maxsysload < 1) { $maxsysload = 50; } } else { $process = 'frontend'; - $maxsysload = intval(Core\Config::get('system', 'maxloadavg_frontend')); + $maxsysload = intval($this->config->get('system', 'maxloadavg_frontend')); if ($maxsysload < 1) { $maxsysload = 50; } @@ -933,7 +933,7 @@ class App */ public function getSenderEmailAddress() { - $sender_email = Core\Config::get('config', 'sender_email'); + $sender_email = $this->config->get('config', 'sender_email'); if (empty($sender_email)) { $hostname = $this->getHostName(); if (strpos($hostname, ':')) { @@ -977,7 +977,7 @@ class App */ private function computeCurrentTheme() { - $system_theme = Core\Config::get('system', 'theme'); + $system_theme = $this->config->get('system', 'theme'); if (!$system_theme) { throw new Exception(Core\L10n::t('No system theme config value set.')); } @@ -985,7 +985,7 @@ class App // Sane default $this->currentTheme = $system_theme; - $allowed_themes = explode(',', Core\Config::get('system', 'allowed_themes', $system_theme)); + $allowed_themes = explode(',', $this->config->get('system', 'allowed_themes', $system_theme)); $page_theme = null; // Find the theme that belongs to the user whose stuff we are looking at @@ -1002,7 +1002,7 @@ class App // Specific mobile theme override if (($this->is_mobile || $this->is_tablet) && Core\Session::get('show-mobile', true)) { - $system_mobile_theme = Core\Config::get('system', 'mobile-theme'); + $system_mobile_theme = $this->config->get('system', 'mobile-theme'); $user_mobile_theme = Core\Session::get('mobile-theme', $system_mobile_theme); // --- means same mobile theme as desktop @@ -1073,7 +1073,7 @@ class App */ public function checkURL() { - $url = Core\Config::get('system', 'url'); + $url = $this->config->get('system', 'url'); // if the url isn't set or the stored url is radically different // than the currently visited url, store the current value accordingly. @@ -1082,7 +1082,7 @@ class App // We will only change the url to an ip address if there is no existing setting if (empty($url) || (!Util\Strings::compareLink($url, $this->getBaseURL())) && (!preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $this->getHostName()))) { - Core\Config::set('system', 'url', $this->getBaseURL()); + $this->config->set('system', 'url', $this->getBaseURL()); } } @@ -1115,8 +1115,8 @@ class App if (!$this->getMode()->isInstall()) { // Force SSL redirection - if (Core\Config::get('system', 'force_ssl') && ($this->getScheme() == "http") - && intval(Core\Config::get('system', 'ssl_policy')) == SSL_POLICY_FULL + if ($this->config->get('system', 'force_ssl') && ($this->getScheme() == "http") + && intval($this->config->get('system', 'ssl_policy')) == SSL_POLICY_FULL && strpos($this->getBaseURL(), 'https://') === 0 && $_SERVER['REQUEST_METHOD'] == 'GET') { header('HTTP/1.1 302 Moved Temporarily'); @@ -1256,7 +1256,7 @@ class App $this->module = "login"; } - $privateapps = Core\Config::get('config', 'private_addons', false); + $privateapps = $this->config->get('config', 'private_addons', false); if (Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) { //Check if module is an app and if public access to apps is allowed or not if ((!local_user()) && Core\Hook::isAddonApp($this->module) && $privateapps) { @@ -1441,7 +1441,7 @@ class App header("X-Friendica-Version: " . FRIENDICA_VERSION); header("Content-type: text/html; charset=utf-8"); - if (Core\Config::get('system', 'hsts') && (Core\Config::get('system', 'ssl_policy') == SSL_POLICY_FULL)) { + if ($this->config->get('system', 'hsts') && ($this->config->get('system', 'ssl_policy') == SSL_POLICY_FULL)) { header("Strict-Transport-Security: max-age=31536000"); } From 383a6715c3c10a500dd880f0217eb7e013d68b4d Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sat, 23 Mar 2019 15:37:05 +0100 Subject: [PATCH 3/8] Added first version of ConfigCacheSaver --- src/App.php | 2 +- src/Core/Config/Configuration.php | 12 +- src/Core/Console/AutomaticInstallation.php | 3 +- src/Factory/ConfigFactory.php | 5 +- src/Factory/DependencyFactory.php | 4 +- .../Config}/ConfigCacheLoader.php | 34 +-- src/Util/Config/ConfigCacheManager.php | 39 ++++ src/Util/Config/ConfigCacheSaver.php | 198 ++++++++++++++++++ tests/DatabaseTest.php | 4 +- tests/include/ApiTest.php | 4 +- tests/src/Database/DBATest.php | 4 +- tests/src/Database/DBStructureTest.php | 4 +- .../Config}/ConfigCacheLoaderTest.php | 36 +--- .../src/Util/Config/ConfigCacheSaverTest.php | 128 +++++++++++ 14 files changed, 409 insertions(+), 68 deletions(-) rename src/{Core/Config/Cache => Util/Config}/ConfigCacheLoader.php (86%) create mode 100644 src/Util/Config/ConfigCacheManager.php create mode 100644 src/Util/Config/ConfigCacheSaver.php rename tests/src/{Core/Config/Cache => Util/Config}/ConfigCacheLoaderTest.php (84%) create mode 100644 tests/src/Util/Config/ConfigCacheSaverTest.php diff --git a/src/App.php b/src/App.php index 7893cbe70..7c88f0f02 100644 --- a/src/App.php +++ b/src/App.php @@ -8,12 +8,12 @@ use Detection\MobileDetect; use DOMDocument; use DOMXPath; use Exception; -use Friendica\Core\Config\Cache\ConfigCacheLoader; use Friendica\Core\Config\Cache\IConfigCache; use Friendica\Core\Config\Configuration; use Friendica\Database\DBA; use Friendica\Model\Profile; use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Util\Config\ConfigCacheLoader; use Friendica\Util\HTTPSignature; use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; diff --git a/src/Core/Config/Configuration.php b/src/Core/Config/Configuration.php index 532ed982a..abf6af37c 100644 --- a/src/Core/Config/Configuration.php +++ b/src/Core/Config/Configuration.php @@ -10,6 +10,16 @@ namespace Friendica\Core\Config; */ class Configuration { + /** + * The blacklist of configuration settings, which should not get saved to the backend + * @var array + */ + private $configSaveBlacklist = [ + 'config' => [ + 'hostname' => true, + ] + ]; + /** * @var Cache\IConfigCache */ @@ -117,7 +127,7 @@ class Configuration $cached = $this->configCache->set($cat, $key, $value); // If there is no connected adapter, we're finished - if (!$this->configAdapter->isConnected()) { + if (!$this->configAdapter->isConnected() && !empty($this->configSaveBlacklist[$cat][$key])) { return $cached; } diff --git a/src/Core/Console/AutomaticInstallation.php b/src/Core/Console/AutomaticInstallation.php index 280e3d107..0c73c82b1 100644 --- a/src/Core/Console/AutomaticInstallation.php +++ b/src/Core/Console/AutomaticInstallation.php @@ -7,6 +7,7 @@ use Friendica\BaseObject; use Friendica\Core\Config; use Friendica\Core\Installer; use Friendica\Core\Theme; +use Friendica\Util\Config\ConfigCacheLoader; use RuntimeException; class AutomaticInstallation extends Console @@ -103,7 +104,7 @@ HELP; } //reload the config cache - $loader = new Config\Cache\ConfigCacheLoader($a->getBasePath(), $a->getMode()); + $loader = new ConfigCacheLoader($a->getBasePath(), $a->getMode()); $loader->loadConfigFiles($configCache); } else { diff --git a/src/Factory/ConfigFactory.php b/src/Factory/ConfigFactory.php index 6a30cf0e0..7a281d97a 100644 --- a/src/Factory/ConfigFactory.php +++ b/src/Factory/ConfigFactory.php @@ -6,15 +6,16 @@ use Friendica\Core; use Friendica\Core\Config; use Friendica\Core\Config\Adapter; use Friendica\Core\Config\Cache; +use Friendica\Util\Config\ConfigCacheLoader; class ConfigFactory { /** - * @param Cache\ConfigCacheLoader $loader The Config Cache loader (INI/config/.htconfig) + * @param ConfigCacheLoader $loader The Config Cache loader (INI/config/.htconfig) * * @return Cache\ConfigCache */ - public static function createCache(Cache\ConfigCacheLoader $loader) + public static function createCache(ConfigCacheLoader $loader) { $configCache = new Cache\ConfigCache(); $loader->loadConfigFiles($configCache); diff --git a/src/Factory/DependencyFactory.php b/src/Factory/DependencyFactory.php index 265dca88e..9322a44cf 100644 --- a/src/Factory/DependencyFactory.php +++ b/src/Factory/DependencyFactory.php @@ -3,9 +3,9 @@ namespace Friendica\Factory; use Friendica\App; -use Friendica\Core\Config\Cache; use Friendica\Factory; use Friendica\Util\BasePath; +use Friendica\Util\Config; class DependencyFactory { @@ -24,7 +24,7 @@ class DependencyFactory { $basePath = BasePath::create($directory, $_SERVER); $mode = new App\Mode($basePath); - $configLoader = new Cache\ConfigCacheLoader($basePath, $mode); + $configLoader = new Config\ConfigCacheLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); Factory\DBFactory::init($basePath, $configCache, $profiler, $_SERVER); diff --git a/src/Core/Config/Cache/ConfigCacheLoader.php b/src/Util/Config/ConfigCacheLoader.php similarity index 86% rename from src/Core/Config/Cache/ConfigCacheLoader.php rename to src/Util/Config/ConfigCacheLoader.php index 9e06d8fb8..f2f4f7542 100644 --- a/src/Core/Config/Cache/ConfigCacheLoader.php +++ b/src/Util/Config/ConfigCacheLoader.php @@ -1,29 +1,21 @@ appMode = $mode; - $this->baseDir = $baseDir; - $this->configDir = $baseDir . DIRECTORY_SEPARATOR . self::SUBDIRECTORY; } /** @@ -74,10 +65,10 @@ class ConfigCacheLoader */ public function loadCoreConfig($name) { - if (file_exists($this->configDir . DIRECTORY_SEPARATOR . $name . '.config.php')) { - return $this->loadConfigFile($this->configDir . DIRECTORY_SEPARATOR . $name . '.config.php'); - } elseif (file_exists($this->configDir . DIRECTORY_SEPARATOR . $name . '.ini.php')) { - return $this->loadINIConfigFile($this->configDir . DIRECTORY_SEPARATOR . $name . '.ini.php'); + if (!empty($this->getConfigFullName($name))) { + return $this->loadConfigFile($this->getConfigFullName($name)); + } elseif (!empty($this->getIniFullName($name))) { + return $this->loadINIConfigFile($this->getIniFullName($name)); } else { return []; } @@ -118,14 +109,11 @@ class ConfigCacheLoader */ private function loadLegacyConfig($name) { - $filePath = $this->baseDir . DIRECTORY_SEPARATOR . '.' . $name . '.php'; - $config = []; - - if (file_exists($filePath)) { + if (!empty($this->getHtConfigFullName($name))) { $a = new \stdClass(); $a->config = []; - include $filePath; + include $this->getHtConfigFullName($name); $htConfigCategories = array_keys($a->config); diff --git a/src/Util/Config/ConfigCacheManager.php b/src/Util/Config/ConfigCacheManager.php new file mode 100644 index 000000000..198171770 --- /dev/null +++ b/src/Util/Config/ConfigCacheManager.php @@ -0,0 +1,39 @@ +baseDir = $baseDir; + $this->configDir = $baseDir . DIRECTORY_SEPARATOR . self::SUBDIRECTORY; + } + + protected function getConfigFullName($name) + { + $fullName = $this->configDir . DIRECTORY_SEPARATOR . $name . '.config.php'; + return file_exists($fullName) ? $fullName : ''; + } + + protected function getIniFullName($name) + { + $fullName = $this->configDir . DIRECTORY_SEPARATOR . $name . '.ini.php'; + return file_exists($fullName) ? $fullName : ''; + } + + protected function getHtConfigFullName($name) + { + $fullName = $this->baseDir . DIRECTORY_SEPARATOR . '.' . $name . '.php'; + return file_exists($fullName) ? $fullName : ''; + } +} diff --git a/src/Util/Config/ConfigCacheSaver.php b/src/Util/Config/ConfigCacheSaver.php new file mode 100644 index 000000000..d84ec6a92 --- /dev/null +++ b/src/Util/Config/ConfigCacheSaver.php @@ -0,0 +1,198 @@ +saveToLegacyConfig('htpreconfig', $cat, $key, $value); + $this->saveToLegacyConfig('htconfig', $cat, $key, $value); + $this->saveToCoreConfig('local', $cat, $key, $value); + } + + /** + * Saves a value to either an config or an ini file + * + * @param string $name The configuration file name ('local', 'addon', ..) + * @param string $cat The configuration category + * @param string $key The configuration key + * @param string $value The new value + */ + private function saveToCoreConfig($name, $cat, $key, $value) + { + if (!empty($this->getConfigFullName($name))) { + $this->saveConfigFile($this->getConfigFullName($name), $cat, $key, $value); + } elseif (!empty($this->getIniFullName($name))) { + $this->saveINIConfigFile($this->getIniFullName($name), $cat, $key, $value); + } else { + return; + } + } + + /** + * Saves a value to a config file + * + * @param string $fullName The configuration full name (including the path) + * @param string $cat The configuration category + * @param string $key The configuration key + * @param string $value The new value + * + * @throws \Exception In case a file operation doesn't work + */ + private function saveConfigFile($fullName, $cat, $key, $value) + { + $reading = fopen($fullName, 'r'); + if (!$reading) { + throw new \Exception('Cannot open config file \'' . $fullName . '\'.'); + } + $writing = fopen($fullName . '.tmp', 'w'); + if (!$writing) { + throw new \Exception('Cannot create temporary config file \'' . $fullName . '.tmp\'.'); + } + $categoryFound = false; + $categoryBracketFound = false; + $lineFound = false; + $lineArrowFound = false; + while (!feof($reading)) { + $line = fgets($reading); + // find the first line like "'system' =>" + if (!$categoryFound && stristr($line, sprintf('\'%s\'', $cat))) { + $categoryFound = true; + } + // find the first line with a starting bracket ( "[" ) + if ($categoryFound && !$categoryBracketFound && stristr($line, '[')) { + $categoryBracketFound = true; + } + // find the first line with the key like "'value'" + if ($categoryBracketFound && !$lineFound && stristr($line, sprintf('\'%s\'', $key))) { + $lineFound = true; + } + // find the first line with an arrow ("=>") after finding the key + if ($lineFound && !$lineArrowFound && stristr($line, '=>')) { + $lineArrowFound = true; + } + // find the current value and replace it + if ($lineArrowFound && preg_match_all('/\'(.*?)\'/', $line, $matches, PREG_SET_ORDER)) { + $lineVal = end($matches)[0]; + $writeLine = str_replace($lineVal, '\'' . $value . '\'', $line); + $categoryFound = false; + $categoryBracketFound = false; + $lineFound = false; + $lineArrowFound = false; + // if a line contains a closing bracket for the category ( "]" ) and we didn't find the key/value pair, + // add it as a new line before the closing bracket + } elseif ($categoryBracketFound && !$lineArrowFound && stristr($line, ']')) { + $categoryFound = false; + $categoryBracketFound = false; + $lineFound = false; + $lineArrowFound = false; + $writeLine = sprintf(self::INDENT . self::INDENT .'\'%s\' => \'%s\',' . PHP_EOL, $key, $value); + $writeLine .= $line; + } else { + $writeLine = $line; + } + fputs($writing, $writeLine); + } + if (!fclose($reading)) { + throw new \Exception('Cannot close config file \'' . $fullName . '\'.'); + }; + if (!fclose($writing)) { + throw new \Exception('Cannot close temporary config file \'' . $fullName . '.tmp\'.'); + }; + if (!rename($fullName, $fullName . '.old')) { + throw new \Exception('Cannot backup current config file \'' . $fullName . '\'.'); + } + if (!rename($fullName . '.tmp', $fullName)) { + throw new \Exception('Cannot move temporary config file \'' . $fullName . '.tmp\' to current.'); + } + } + + /** + * Saves a value to a ini file + * + * @param string $fullName The configuration full name (including the path) + * @param string $cat The configuration category + * @param string $key The configuration key + * @param string $value The new value + */ + private function saveINIConfigFile($fullName, $cat, $key, $value) + { + $reading = fopen($fullName, 'r'); + $writing = fopen($fullName . '.tmp', 'w'); + $categoryFound = false; + while (!feof($reading)) { + $line = fgets($reading); + if (!$categoryFound && stristr($line, sprintf('[%s]', $cat))) { + $categoryFound = true; + $writeLine = $line; + } elseif ($categoryFound && preg_match_all('/^' . $key . '\s*=\s*(.*?)$/', $line, $matches, PREG_SET_ORDER)) { + $writeLine = $key . ' = ' . $value . PHP_EOL; + $categoryFound = false; + } elseif ($categoryFound && (preg_match_all('/^\[.*?\]$/', $line) || preg_match_all('/^INI;.*$/', $line))) { + $categoryFound = false; + $writeLine = $key . ' = ' . $value . PHP_EOL; + $writeLine .= $line; + } else { + $writeLine = $line; + } + fputs($writing, $writeLine); + } + fclose($reading); + fclose($writing); + rename($fullName, $fullName . '.old'); + rename($fullName . '.tmp', $fullName); + } + + private function saveToLegacyConfig($name, $cat, $key, $value) + { + if (empty($this->getHtConfigFullName($name))) { + return; + } + $fullName = $this->getHtConfigFullName($name); + $reading = fopen($fullName, 'r'); + $writing = fopen($fullName . '.tmp', 'w'); + $found = false; + while (!feof($reading)) { + $line = fgets($reading); + if (preg_match_all('/^\$a\-\>config\[\'' . $cat . '\',\'' . $key . '\'\]\s*=\s\'*(.*?)\'$/', $line, $matches, PREG_SET_ORDER)) { + $writeLine = $key . ' = ' . $value . PHP_EOL; + $found = true; + } else { + $writeLine = $line; + } + fputs($writing, $writeLine); + } + if (!$found) { + $writeLine = $key . ' = ' . $value . PHP_EOL; + fputs($writing, $writeLine); + } + fclose($reading); + fclose($writing); + rename($fullName, $fullName . '.old'); + rename($fullName . '.tmp', $fullName); + } +} diff --git a/tests/DatabaseTest.php b/tests/DatabaseTest.php index 6a64b2881..d43467406 100644 --- a/tests/DatabaseTest.php +++ b/tests/DatabaseTest.php @@ -6,10 +6,10 @@ namespace Friendica\Test; use Friendica\App; -use Friendica\Core\Config\Cache; use Friendica\Database\DBA; use Friendica\Factory; use Friendica\Util\BasePath; +use Friendica\Util\Config\ConfigCacheLoader; use Friendica\Util\Profiler; use PHPUnit\DbUnit\DataSet\YamlDataSet; use PHPUnit\DbUnit\TestCaseTrait; @@ -43,7 +43,7 @@ abstract class DatabaseTest extends MockedTest $basePath = BasePath::create(dirname(__DIR__)); $mode = new App\Mode($basePath); - $configLoader = new Cache\ConfigCacheLoader($basePath, $mode); + $configLoader = new ConfigCacheLoader($basePath, $mode); $config = Factory\ConfigFactory::createCache($configLoader); $profiler = \Mockery::mock(Profiler::class); diff --git a/tests/include/ApiTest.php b/tests/include/ApiTest.php index 3d7e5bcfb..bd1b04108 100644 --- a/tests/include/ApiTest.php +++ b/tests/include/ApiTest.php @@ -7,13 +7,13 @@ namespace Friendica\Test; use Friendica\App; use Friendica\Core\Config; -use Friendica\Core\Config\Cache; use Friendica\Core\PConfig; use Friendica\Core\Protocol; use Friendica\Core\System; use Friendica\Factory; use Friendica\Network\HTTPException; use Friendica\Util\BasePath; +use Friendica\Util\Config\ConfigCacheLoader; use Monolog\Handler\TestHandler; require_once __DIR__ . '/../../include/api.php'; @@ -38,7 +38,7 @@ class ApiTest extends DatabaseTest { $basePath = BasePath::create(dirname(__DIR__) . '/../'); $mode = new App\Mode($basePath); - $configLoader = new Cache\ConfigCacheLoader($basePath, $mode); + $configLoader = new ConfigCacheLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); Factory\DBFactory::init($basePath, $configCache, $profiler, $_SERVER); diff --git a/tests/src/Database/DBATest.php b/tests/src/Database/DBATest.php index e638e2740..28f5afbd7 100644 --- a/tests/src/Database/DBATest.php +++ b/tests/src/Database/DBATest.php @@ -3,11 +3,11 @@ namespace Friendica\Test\src\Database; use Friendica\App; use Friendica\Core\Config; -use Friendica\Core\Config\Cache; use Friendica\Database\DBA; use Friendica\Factory; use Friendica\Test\DatabaseTest; use Friendica\Util\BasePath; +use Friendica\Util\Config\ConfigCacheLoader; class DBATest extends DatabaseTest { @@ -15,7 +15,7 @@ class DBATest extends DatabaseTest { $basePath = BasePath::create(dirname(__DIR__) . '/../../'); $mode = new App\Mode($basePath); - $configLoader = new Cache\ConfigCacheLoader($basePath, $mode); + $configLoader = new ConfigCacheLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); Factory\DBFactory::init($basePath, $configCache, $profiler, $_SERVER); diff --git a/tests/src/Database/DBStructureTest.php b/tests/src/Database/DBStructureTest.php index 34f659b51..65d5c85a4 100644 --- a/tests/src/Database/DBStructureTest.php +++ b/tests/src/Database/DBStructureTest.php @@ -3,11 +3,11 @@ namespace Friendica\Test\src\Database; use Friendica\App; -use Friendica\Core\Config\Cache; use Friendica\Database\DBStructure; use Friendica\Factory; use Friendica\Test\DatabaseTest; use Friendica\Util\BasePath; +use Friendica\Util\Config\ConfigCacheLoader; class DBStructureTest extends DatabaseTest { @@ -15,7 +15,7 @@ class DBStructureTest extends DatabaseTest { $basePath = BasePath::create(dirname(__DIR__) . '/../../'); $mode = new App\Mode($basePath); - $configLoader = new Cache\ConfigCacheLoader($basePath, $mode); + $configLoader = new ConfigCacheLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); Factory\DBFactory::init($basePath, $configCache, $profiler, $_SERVER); diff --git a/tests/src/Core/Config/Cache/ConfigCacheLoaderTest.php b/tests/src/Util/Config/ConfigCacheLoaderTest.php similarity index 84% rename from tests/src/Core/Config/Cache/ConfigCacheLoaderTest.php rename to tests/src/Util/Config/ConfigCacheLoaderTest.php index 39dc20efd..be0c5c01a 100644 --- a/tests/src/Core/Config/Cache/ConfigCacheLoaderTest.php +++ b/tests/src/Util/Config/ConfigCacheLoaderTest.php @@ -1,12 +1,12 @@ delConfigFile('local.config.php'); - $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR . - 'local.config.php'; + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR; vfsStream::newFile('local.config.php') ->at($this->root->getChild('config')) @@ -101,13 +95,7 @@ class ConfigCacheLoaderTest extends MockedTest { $this->delConfigFile('local.config.php'); - $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR . - 'local.ini.php'; + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR; vfsStream::newFile('local.ini.php') ->at($this->root->getChild('config')) @@ -133,13 +121,7 @@ class ConfigCacheLoaderTest 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.test.php'; + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR; vfsStream::newFile('.htconfig.php') ->at($this->root) @@ -183,13 +165,7 @@ class ConfigCacheLoaderTest extends MockedTest vfsStream::create($structure, $this->root); - $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR . - 'local.config.php'; + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR; vfsStream::newFile('test.config.php') ->at($this->root->getChild('addon')->getChild('test')->getChild('config')) diff --git a/tests/src/Util/Config/ConfigCacheSaverTest.php b/tests/src/Util/Config/ConfigCacheSaverTest.php new file mode 100644 index 000000000..da2f050eb --- /dev/null +++ b/tests/src/Util/Config/ConfigCacheSaverTest.php @@ -0,0 +1,128 @@ +setUpVfsDir(); + $this->mode = \Mockery::mock(App\Mode::class); + $this->mode->shouldReceive('isInstall')->andReturn(true); + } + /** + * Test the saveToConfigFile() method with a local.config.php file + */ + public function testSaveToConfigFileLocal() + { + $this->delConfigFile('local.config.php'); + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + 'local.config.php'; + vfsStream::newFile('local.config.php') + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($file)); + $configCacheSaver = new ConfigCacheSaver($this->root->url()); + $configCacheLoader = new ConfigCacheLoader($this->root->url(), $this->mode); + $configCache = new ConfigCache(); + $configCacheLoader->loadConfigFiles($configCache); + $this->assertEquals('admin@test.it', $configCache->get('config', 'admin_email')); + $this->assertEquals('!!', $configCache->get('config', 'test_val')); + $configCacheSaver->saveToConfigFile('config', 'admin_email', 'new@mail.it'); + $configCacheSaver->saveToConfigFile('config', 'test_val', 'Testing$!"$with@all.we can!'); + $newConfigCache = new ConfigCache(); + $configCacheLoader->loadConfigFiles($newConfigCache); + $this->assertEquals('new@mail.it', $newConfigCache->get('config', 'admin_email')); + $this->assertEquals('Testing$!"$with@all.we can!', $newConfigCache->get('config', 'test_val')); + $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php')); + $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php.old')); + $this->assertFalse($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php.tmp')); + $this->assertEquals(file_get_contents($file), file_get_contents($this->root->getChild('config' . DIRECTORY_SEPARATOR . 'local.config.php.old')->url())); + } + /** + * Test the saveToConfigFile() method with a local.ini.php file + */ + public function testSaveToConfigFileINI() + { + $this->delConfigFile('local.config.php'); + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + 'local.ini.php'; + vfsStream::newFile('local.ini.php') + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($file)); + $configCacheSaver = new ConfigCacheSaver($this->root->url()); + $configCacheLoader = new ConfigCacheLoader($this->root->url(), $this->mode); + $configCache = new ConfigCache(); + $configCacheLoader->loadConfigFiles($configCache); + $this->assertEquals('admin@test.it', $configCache->get('config', 'admin_email')); + $this->assertEquals('!!', $configCache->get('config', 'test_val')); + $configCacheSaver->saveToConfigFile('config', 'admin_email', 'new@mail.it'); + $configCacheSaver->saveToConfigFile('config', 'test_val', "Testing@with.all we can"); + $newConfigCache = new ConfigCache(); + $configCacheLoader->loadConfigFiles($newConfigCache); + $this->assertEquals('new@mail.it', $newConfigCache->get('config', 'admin_email')); + $this->assertEquals("Testing@with.all we can", $newConfigCache->get('config', 'test_val')); + $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.ini.php')); + $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.ini.php.old')); + $this->assertFalse($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.ini.php.tmp')); + $this->assertEquals(file_get_contents($file), file_get_contents($this->root->getChild('config' . DIRECTORY_SEPARATOR . 'local.ini.old')->url())); + } + /** + * Test the saveToConfigFile() method with a .htconfig.php file + * @todo fix it after 2019.03 merge to develop + */ + public function testSaveToConfigFileHtconfig() + { + $this->markTestSkipped('Needs 2019.03 merge to develop first'); + $this->delConfigFile('local.config.php'); + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + '.htconfig.test.php'; + vfsStream::newFile('.htconfig.php') + ->at($this->root) + ->setContent(file_get_contents($file)); + $configCacheSaver = new ConfigCacheSaver($this->root->url(), $this->mode); + $configCache = new ConfigCache(); + $configCacheSaver->loadConfigFiles($configCache); + $this->assertEquals('admin@test.it', $configCache->get('config', 'admin_email')); + $this->assertEquals('!!', $configCache->get('config', 'test_val')); + $configCacheSaver->saveToConfigFile('config', 'admin_email', 'new@mail.it'); + $configCacheSaver->saveToConfigFile('config', 'test_val', 'Testing$!"$with@all.we can!'); + $newConfigCache = new ConfigCache(); + $configCacheSaver->loadConfigFiles($newConfigCache); + $this->assertEquals('new@mail.it', $newConfigCache->get('config', 'admin_email')); + $this->assertEquals('Testing$!"$with@all.we can!', $newConfigCache->get('config', 'test_val')); + $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . '.htconfig.php')); + $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . '.htconfig.php.old')); + $this->assertFalse($this->root->hasChild('config' . DIRECTORY_SEPARATOR . '.htconfig.php.tmp')); + $this->assertEquals(file_get_contents($file), file_get_contents($this->root->getChild('config' . DIRECTORY_SEPARATOR . '.htconfig.php.old')->url())); + } +} From fa31bb6dde8106fcc7e16c1f6c359f2d58723805 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sat, 23 Mar 2019 15:40:09 +0100 Subject: [PATCH 4/8] remove basepath and hostname from admin panel and add update path --- config/dbstructure.config.php | 2 +- mod/admin.php | 14 +---- src/Util/Config/ConfigCacheLoader.php | 8 +-- src/Util/Config/ConfigCacheManager.php | 54 +++++++++++++++++-- src/Util/Config/ConfigCacheSaver.php | 41 ++++++++++++-- .../src/Util/Config/ConfigCacheLoaderTest.php | 28 ++++++++-- .../src/Util/Config/ConfigCacheSaverTest.php | 13 +++-- update.php | 30 +++++++++++ view/templates/admin/site.tpl | 2 - view/templates/local.config.tpl | 1 + 10 files changed, 158 insertions(+), 35 deletions(-) diff --git a/config/dbstructure.config.php b/config/dbstructure.config.php index f03132add..97068415a 100644 --- a/config/dbstructure.config.php +++ b/config/dbstructure.config.php @@ -34,7 +34,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1304); + define('DB_UPDATE_VERSION', 1305); } return [ diff --git a/mod/admin.php b/mod/admin.php index f8a75b7a2..7808a87ef 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -1087,6 +1087,8 @@ function admin_page_site_post(App $a) update_table($a, "gcontact", ['connect', 'addr'], $old_host, $new_host); // update config + $configCacheSaver = new \Friendica\Util\Config\ConfigCacheSaver($a->getBasePath()); + $configCacheSaver->saveToConfigFile('system', 'hostname', parse_url($new_url, PHP_URL_HOST)); Config::set('system', 'hostname', parse_url($new_url, PHP_URL_HOST)); Config::set('system', 'url', $new_url); $a->setBaseURL($new_url); @@ -1105,7 +1107,6 @@ function admin_page_site_post(App $a) // end relocate $sitename = (!empty($_POST['sitename']) ? Strings::escapeTags(trim($_POST['sitename'])) : ''); - $hostname = (!empty($_POST['hostname']) ? Strings::escapeTags(trim($_POST['hostname'])) : ''); $sender_email = (!empty($_POST['sender_email']) ? Strings::escapeTags(trim($_POST['sender_email'])) : ''); $banner = (!empty($_POST['banner']) ? trim($_POST['banner']) : false); $shortcut_icon = (!empty($_POST['shortcut_icon']) ? Strings::escapeTags(trim($_POST['shortcut_icon'])) : ''); @@ -1176,7 +1177,6 @@ function admin_page_site_post(App $a) $itemcache_duration = (!empty($_POST['itemcache_duration']) ? intval($_POST['itemcache_duration']) : 0); $max_comments = (!empty($_POST['max_comments']) ? intval($_POST['max_comments']) : 0); $temppath = (!empty($_POST['temppath']) ? Strings::escapeTags(trim($_POST['temppath'])) : ''); - $basepath = (!empty($_POST['basepath']) ? Strings::escapeTags(trim($_POST['basepath'])) : ''); $singleuser = (!empty($_POST['singleuser']) ? Strings::escapeTags(trim($_POST['singleuser'])) : ''); $proxy_disabled = !empty($_POST['proxy_disabled']); $only_tag_search = !empty($_POST['only_tag_search']); @@ -1296,7 +1296,6 @@ function admin_page_site_post(App $a) Config::set('system', 'poco_local_search' , $poco_local_search); Config::set('system', 'nodeinfo' , $nodeinfo); Config::set('config', 'sitename' , $sitename); - Config::set('config', 'hostname' , $hostname); Config::set('config', 'sender_email' , $sender_email); Config::set('system', 'suppress_tags' , $suppress_tags); Config::set('system', 'shortcut_icon' , $shortcut_icon); @@ -1392,10 +1391,6 @@ function admin_page_site_post(App $a) Config::set('system', 'temppath', $temppath); - if ($basepath != '') { - $basepath = BasePath::getRealPath($basepath); - } - Config::set('system', 'basepath' , $basepath); Config::set('system', 'proxy_disabled' , $proxy_disabled); Config::set('system', 'only_tag_search' , $only_tag_search); @@ -1536,9 +1531,6 @@ function admin_page_site(App $a) "develop" => L10n::t("check the development version") ]; - if (empty(Config::get('config', 'hostname'))) { - Config::set('config', 'hostname', $a->getHostName()); - } $diaspora_able = ($a->getURLPath() == ""); $optimize_max_tablesize = Config::get('system', 'optimize_max_tablesize', -1); @@ -1597,7 +1589,6 @@ function admin_page_site(App $a) // name, label, value, help string, extra data... '$sitename' => ['sitename', L10n::t("Site name"), Config::get('config', 'sitename'), ''], - '$hostname' => ['hostname', L10n::t("Host name"), Config::get('config', 'hostname'), ""], '$sender_email' => ['sender_email', L10n::t("Sender Email"), Config::get('config', 'sender_email'), L10n::t("The email address your server shall use to send notification emails from."), "", "", "email"], '$banner' => ['banner', L10n::t("Banner/Logo"), $banner, ""], '$shortcut_icon' => ['shortcut_icon', L10n::t("Shortcut icon"), Config::get('system', 'shortcut_icon'), L10n::t("Link to an icon that will be used for browsers.")], @@ -1675,7 +1666,6 @@ function admin_page_site(App $a) '$itemcache_duration' => ['itemcache_duration', L10n::t("Cache duration in seconds"), Config::get('system', 'itemcache_duration'), L10n::t("How long should the cache files be hold? Default value is 86400 seconds \x28One day\x29. To disable the item cache, set the value to -1.")], '$max_comments' => ['max_comments', L10n::t("Maximum numbers of comments per post"), Config::get('system', 'max_comments'), L10n::t("How much comments should be shown for each post? Default value is 100.")], '$temppath' => ['temppath', L10n::t("Temp path"), Config::get('system', 'temppath'), L10n::t("If you have a restricted system where the webserver can't access the system temp path, enter another path here.")], - '$basepath' => ['basepath', L10n::t("Base path to installation"), Config::get('system', 'basepath'), L10n::t("If the system cannot detect the correct path to your installation, enter the correct path here. This setting should only be set if you are using a restricted system and symbolic links to your webroot.")], '$proxy_disabled' => ['proxy_disabled', L10n::t("Disable picture proxy"), Config::get('system', 'proxy_disabled'), L10n::t("The picture proxy increases performance and privacy. It shouldn't be used on systems with very low bandwidth.")], '$only_tag_search' => ['only_tag_search', L10n::t("Only search in tags"), Config::get('system', 'only_tag_search'), L10n::t("On large systems the text search can slow down the system extremely.")], diff --git a/src/Util/Config/ConfigCacheLoader.php b/src/Util/Config/ConfigCacheLoader.php index f2f4f7542..6eced061e 100644 --- a/src/Util/Config/ConfigCacheLoader.php +++ b/src/Util/Config/ConfigCacheLoader.php @@ -57,13 +57,13 @@ class ConfigCacheLoader extends ConfigCacheManager /** * Tries to load the specified core-configuration and returns the config array. * - * @param string $name The name of the configuration + * @param string $name The name of the configuration (default is empty, which means 'local') * * @return array The config array (empty if no config found) * * @throws \Exception if the configuration file isn't readable */ - public function loadCoreConfig($name) + public function loadCoreConfig($name = '') { if (!empty($this->getConfigFullName($name))) { return $this->loadConfigFile($this->getConfigFullName($name)); @@ -101,13 +101,13 @@ class ConfigCacheLoader extends ConfigCacheManager /** * Tries to load the legacy config files (.htconfig.php, .htpreconfig.php) and returns the config array. * - * @param string $name The name of the config file + * @param string $name The name of the config file (default is empty, which means .htconfig.php) * * @return array The configuration array (empty if no config found) * * @deprecated since version 2018.09 */ - private function loadLegacyConfig($name) + private function loadLegacyConfig($name = '') { $config = []; if (!empty($this->getHtConfigFullName($name))) { diff --git a/src/Util/Config/ConfigCacheManager.php b/src/Util/Config/ConfigCacheManager.php index 198171770..8fb923216 100644 --- a/src/Util/Config/ConfigCacheManager.php +++ b/src/Util/Config/ConfigCacheManager.php @@ -10,29 +10,77 @@ abstract class ConfigCacheManager */ const SUBDIRECTORY = 'config'; + /** + * The default name of the user defined config file + * @var string + */ + const CONFIG_LOCAL = 'local'; + + /** + * The default name of the user defined ini file + * @var string + */ + const CONFIG_INI = 'ini'; + + /** + * The default name of the user defined legacy config file + * @var string + */ + const CONFIG_HTCONFIG = 'htconfig'; + protected $baseDir; protected $configDir; + /** + * @param string $baseDir The base directory of Friendica + */ public function __construct($baseDir) { $this->baseDir = $baseDir; $this->configDir = $baseDir . DIRECTORY_SEPARATOR . self::SUBDIRECTORY; } - protected function getConfigFullName($name) + /** + * Gets the full name (including the path) for a *.config.php (default is local.config.php) + * + * @param string $name The config name (default is empty, which means local.config.php) + * + * @return string The full name or empty if not found + */ + protected function getConfigFullName($name = '') { + $name = !empty($name) ? $name : self::CONFIG_LOCAL; + $fullName = $this->configDir . DIRECTORY_SEPARATOR . $name . '.config.php'; return file_exists($fullName) ? $fullName : ''; } - protected function getIniFullName($name) + /** + * Gets the full name (including the path) for a *.ini.php (default is local.ini.php) + * + * @param string $name The config name (default is empty, which means local.ini.php) + * + * @return string The full name or empty if not found + */ + protected function getIniFullName($name = '') { + $name = !empty($name) ? $name : self::CONFIG_INI; + $fullName = $this->configDir . DIRECTORY_SEPARATOR . $name . '.ini.php'; return file_exists($fullName) ? $fullName : ''; } - protected function getHtConfigFullName($name) + /** + * Gets the full name (including the path) for a .*.php (default is .htconfig.php) + * + * @param string $name The config name (default is empty, which means .htconfig.php) + * + * @return string The full name or empty if not found + */ + protected function getHtConfigFullName($name = '') { + $name = !empty($name) ? $name : self::CONFIG_HTCONFIG; + $fullName = $this->baseDir . DIRECTORY_SEPARATOR . '.' . $name . '.php'; return file_exists($fullName) ? $fullName : ''; } diff --git a/src/Util/Config/ConfigCacheSaver.php b/src/Util/Config/ConfigCacheSaver.php index d84ec6a92..41ebca140 100644 --- a/src/Util/Config/ConfigCacheSaver.php +++ b/src/Util/Config/ConfigCacheSaver.php @@ -20,18 +20,49 @@ class ConfigCacheSaver extends ConfigCacheManager const INDENT = "\t"; /** - * Saves a given value to the config file + * The settings array to save to + * @var array + */ + private $settings = []; + + /** + * Adds a given value to the config file * Either it replaces the current value or it will get added * * @param string $cat The configuration category * @param string $key The configuration key * @param string $value The new value */ - public function saveToConfigFile($cat, $key, $value) + public function addConfigValue($cat, $key, $value) { - $this->saveToLegacyConfig('htpreconfig', $cat, $key, $value); - $this->saveToLegacyConfig('htconfig', $cat, $key, $value); - $this->saveToCoreConfig('local', $cat, $key, $value); + $this->settings[$cat][$key] = $value; + } + + public function reset() + { + $this->settings = []; + } + + public function saveToConfigFile($name = '') + { + $saved = false; + + if (!empty($this->getConfigFullName($name))) { + $this->saveConfigFile($this->getConfigFullName($name)); + $saved = true; + } + + if (!empty($this->getIniFullName($name))) { + $this->saveINIConfigFile($this->getIniFullName($name)); + $saved = true; + } + + if (!empty($this->getHtConfigFullName($name))) { + $this->saveToLegacyConfig($this->getHtConfigFullName($name)); + $saved = true; + } + + return $saved; } /** diff --git a/tests/src/Util/Config/ConfigCacheLoaderTest.php b/tests/src/Util/Config/ConfigCacheLoaderTest.php index be0c5c01a..45245d47c 100644 --- a/tests/src/Util/Config/ConfigCacheLoaderTest.php +++ b/tests/src/Util/Config/ConfigCacheLoaderTest.php @@ -68,7 +68,12 @@ class ConfigCacheLoaderTest extends MockedTest { $this->delConfigFile('local.config.php'); - $file = dirname(__DIR__) . DIRECTORY_SEPARATOR; + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + 'local.config.php'; vfsStream::newFile('local.config.php') ->at($this->root->getChild('config')) @@ -95,7 +100,12 @@ class ConfigCacheLoaderTest extends MockedTest { $this->delConfigFile('local.config.php'); - $file = dirname(__DIR__) . DIRECTORY_SEPARATOR; + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + 'local.ini.php'; vfsStream::newFile('local.ini.php') ->at($this->root->getChild('config')) @@ -121,7 +131,12 @@ class ConfigCacheLoaderTest extends MockedTest { $this->delConfigFile('local.config.php'); - $file = dirname(__DIR__) . DIRECTORY_SEPARATOR; + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + '.htconfig.test.php'; vfsStream::newFile('.htconfig.php') ->at($this->root) @@ -165,7 +180,12 @@ class ConfigCacheLoaderTest extends MockedTest vfsStream::create($structure, $this->root); - $file = dirname(__DIR__) . DIRECTORY_SEPARATOR; + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + 'local.config.php'; vfsStream::newFile('test.config.php') ->at($this->root->getChild('addon')->getChild('test')->getChild('config')) diff --git a/tests/src/Util/Config/ConfigCacheSaverTest.php b/tests/src/Util/Config/ConfigCacheSaverTest.php index da2f050eb..a0d9502c2 100644 --- a/tests/src/Util/Config/ConfigCacheSaverTest.php +++ b/tests/src/Util/Config/ConfigCacheSaverTest.php @@ -32,30 +32,36 @@ class ConfigCacheSaverTest extends MockedTest { $this->delConfigFile('local.config.php'); $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'datasets' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'local.config.php'; + vfsStream::newFile('local.config.php') ->at($this->root->getChild('config')) ->setContent(file_get_contents($file)); + $configCacheSaver = new ConfigCacheSaver($this->root->url()); $configCacheLoader = new ConfigCacheLoader($this->root->url(), $this->mode); $configCache = new ConfigCache(); $configCacheLoader->loadConfigFiles($configCache); + $this->assertEquals('admin@test.it', $configCache->get('config', 'admin_email')); - $this->assertEquals('!!', $configCache->get('config', 'test_val')); + $this->assertNull($configCache->get('config', 'test_val')); + $configCacheSaver->saveToConfigFile('config', 'admin_email', 'new@mail.it'); $configCacheSaver->saveToConfigFile('config', 'test_val', 'Testing$!"$with@all.we can!'); + $newConfigCache = new ConfigCache(); $configCacheLoader->loadConfigFiles($newConfigCache); + $this->assertEquals('new@mail.it', $newConfigCache->get('config', 'admin_email')); $this->assertEquals('Testing$!"$with@all.we can!', $newConfigCache->get('config', 'test_val')); $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php')); $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php.old')); $this->assertFalse($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php.tmp')); + $this->assertEquals(file_get_contents($file), file_get_contents($this->root->getChild('config' . DIRECTORY_SEPARATOR . 'local.config.php.old')->url())); } /** @@ -65,7 +71,6 @@ class ConfigCacheSaverTest extends MockedTest { $this->delConfigFile('local.config.php'); $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'datasets' . DIRECTORY_SEPARATOR . @@ -79,7 +84,7 @@ class ConfigCacheSaverTest extends MockedTest $configCache = new ConfigCache(); $configCacheLoader->loadConfigFiles($configCache); $this->assertEquals('admin@test.it', $configCache->get('config', 'admin_email')); - $this->assertEquals('!!', $configCache->get('config', 'test_val')); + $this->assertNull($configCache->get('config', 'test_val')); $configCacheSaver->saveToConfigFile('config', 'admin_email', 'new@mail.it'); $configCacheSaver->saveToConfigFile('config', 'test_val', "Testing@with.all we can"); $newConfigCache = new ConfigCache(); diff --git a/update.php b/update.php index e619ec89d..140c8fcb5 100644 --- a/update.php +++ b/update.php @@ -12,6 +12,7 @@ use Friendica\Model\Contact; use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\User; +use Friendica\Util\Config\ConfigCacheSaver; use Friendica\Util\DateTimeFormat; /** @@ -346,3 +347,32 @@ function update_1298() } return Update::SUCCESS; } + +/** + * @see https://github.com/friendica/friendica/pull/6815 + * + * @return int Success + */ +function update_1303() +{ + $app = \Friendica\BaseObject::getApp(); + $configCache = $app->getConfigCache(); + $configCacheSaver = new ConfigCacheSaver($app->getBasePath()); + $updateConfigEntry = function($cat, $key) use ($configCache, $configCacheSaver) { + // check if the config file differs from the whole configuration (= The db contains other values) + $fileConfig = $configCache->get($cat, $key); + if ($fileConfig === '!!') { + $fileConfig = null; + } + $savedConfig = Config::get($cat, $key, null, true); + if ($fileConfig !== $savedConfig) { + Logger::info('Difference in config found', ['cat' => $cat, 'key' => $key, 'file' => $fileConfig, 'saved' => $savedConfig]); + $configCacheSaver->saveToConfigFile($cat, $key, $savedConfig); + } else { + Logger::info('No Difference in config found', ['cat' => $cat, 'key' => $key, 'value' => $fileConfig, 'saved' => $savedConfig]); + } + }; + $updateConfigEntry('config', 'hostname'); + $updateConfigEntry('system', 'basepath'); + return Update::SUCCESS; +} diff --git a/view/templates/admin/site.tpl b/view/templates/admin/site.tpl index bc5e34c3c..6da3e35f5 100644 --- a/view/templates/admin/site.tpl +++ b/view/templates/admin/site.tpl @@ -46,7 +46,6 @@ {{include file="field_input.tpl" field=$sitename}} - {{include file="field_input.tpl" field=$hostname}} {{include file="field_input.tpl" field=$sender_email}} {{include file="field_textarea.tpl" field=$banner}} {{include file="field_input.tpl" field=$shortcut_icon}} @@ -125,7 +124,6 @@ {{include file="field_input.tpl" field=$optimize_fragmentation}} {{include file="field_input.tpl" field=$abandon_days}} {{include file="field_input.tpl" field=$temppath}} - {{include file="field_input.tpl" field=$basepath}} {{include file="field_checkbox.tpl" field=$suppress_tags}} {{include file="field_checkbox.tpl" field=$nodeinfo}} {{include file="field_select.tpl" field=$check_new_version_url}} diff --git a/view/templates/local.config.tpl b/view/templates/local.config.tpl index c4c4afba4..3201a9985 100644 --- a/view/templates/local.config.tpl +++ b/view/templates/local.config.tpl @@ -29,6 +29,7 @@ return [ ], 'system' => [ 'urlpath' => '{{$urlpath}}', + 'basepath' => '{{$basepath}}', 'default_timezone' => '{{$timezone}}', 'language' => '{{$language}}', ], From 49def0dc27285557e91d7f8cf4c4ff97bac1489c Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 24 Mar 2019 12:54:26 +0100 Subject: [PATCH 5/8] Adding ConfigFileSaver and tests --- mod/admin.php | 5 +- src/App.php | 6 +- src/Core/Config/Cache/ConfigCache.php | 2 +- src/Core/Console/AutomaticInstallation.php | 6 +- src/Factory/ConfigFactory.php | 8 +- src/Factory/DependencyFactory.php | 2 +- src/Util/Config/ConfigCacheSaver.php | 229 -------------- ...igCacheLoader.php => ConfigFileLoader.php} | 8 +- ...CacheManager.php => ConfigFileManager.php} | 7 +- src/Util/Config/ConfigFileSaver.php | 294 ++++++++++++++++++ tests/DatabaseTest.php | 4 +- tests/datasets/config/.htconfig.test.php | 63 ---- tests/datasets/config/local.config.php | 1 + tests/datasets/config/local.ini.php | 3 + tests/include/ApiTest.php | 4 +- tests/src/Database/DBATest.php | 4 +- tests/src/Database/DBStructureTest.php | 4 +- .../src/Util/Config/ConfigCacheSaverTest.php | 133 -------- ...oaderTest.php => ConfigFileLoaderTest.php} | 32 +- tests/src/Util/Config/ConfigFileSaverTest.php | 139 +++++++++ update.php | 12 +- 21 files changed, 493 insertions(+), 473 deletions(-) delete mode 100644 src/Util/Config/ConfigCacheSaver.php rename src/Util/Config/{ConfigCacheLoader.php => ConfigFileLoader.php} (95%) rename src/Util/Config/{ConfigCacheManager.php => ConfigFileManager.php} (94%) create mode 100644 src/Util/Config/ConfigFileSaver.php delete mode 100644 tests/datasets/config/.htconfig.test.php delete mode 100644 tests/src/Util/Config/ConfigCacheSaverTest.php rename tests/src/Util/Config/{ConfigCacheLoaderTest.php => ConfigFileLoaderTest.php} (84%) create mode 100644 tests/src/Util/Config/ConfigFileSaverTest.php diff --git a/mod/admin.php b/mod/admin.php index 7808a87ef..851dd0b09 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -1087,8 +1087,9 @@ function admin_page_site_post(App $a) update_table($a, "gcontact", ['connect', 'addr'], $old_host, $new_host); // update config - $configCacheSaver = new \Friendica\Util\Config\ConfigCacheSaver($a->getBasePath()); - $configCacheSaver->saveToConfigFile('system', 'hostname', parse_url($new_url, PHP_URL_HOST)); + $configFileSaver = new \Friendica\Util\Config\ConfigFileSaver($a->getBasePath()); + $configFileSaver->addConfigValue('system', 'hostname', parse_url($new_url, PHP_URL_HOST)); + $configFileSaver->saveToConfigFile(); Config::set('system', 'hostname', parse_url($new_url, PHP_URL_HOST)); Config::set('system', 'url', $new_url); $a->setBaseURL($new_url); diff --git a/src/App.php b/src/App.php index 7c88f0f02..3a96182b1 100644 --- a/src/App.php +++ b/src/App.php @@ -13,7 +13,7 @@ use Friendica\Core\Config\Configuration; use Friendica\Database\DBA; use Friendica\Model\Profile; use Friendica\Network\HTTPException\InternalServerErrorException; -use Friendica\Util\Config\ConfigCacheLoader; +use Friendica\Util\Config\ConfigFileLoader; use Friendica\Util\HTTPSignature; use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; @@ -355,7 +355,7 @@ class App $this->getMode()->determine($this->getBasePath()); if ($this->getMode()->has(App\Mode::DBAVAILABLE)) { - $loader = new ConfigCacheLoader($this->getBasePath(), $this->getMode()); + $loader = new ConfigFileLoader($this->getBasePath(), $this->getMode()); $this->config->getCache()->load($loader->loadCoreConfig('addon'), true); $this->profiler->update( @@ -363,7 +363,7 @@ class App $this->config->get('rendertime', 'callstack', false)); Core\Hook::loadHooks(); - $loader = new ConfigCacheLoader($this->getBasePath(), $this->mode); + $loader = new ConfigFileLoader($this->getBasePath(), $this->mode); Core\Hook::callAll('load_config', $loader); } diff --git a/src/Core/Config/Cache/ConfigCache.php b/src/Core/Config/Cache/ConfigCache.php index cb299eb33..f61865cee 100644 --- a/src/Core/Config/Cache/ConfigCache.php +++ b/src/Core/Config/Cache/ConfigCache.php @@ -5,7 +5,7 @@ namespace Friendica\Core\Config\Cache; /** * The Friendica config cache for the application * Initial, all *.config.php files are loaded into this cache with the - * ConfigCacheLoader ( @see ConfigCacheLoader ) + * ConfigFileLoader ( @see ConfigFileLoader ) */ class ConfigCache implements IConfigCache, IPConfigCache { diff --git a/src/Core/Console/AutomaticInstallation.php b/src/Core/Console/AutomaticInstallation.php index 0c73c82b1..911c1c00a 100644 --- a/src/Core/Console/AutomaticInstallation.php +++ b/src/Core/Console/AutomaticInstallation.php @@ -7,7 +7,7 @@ use Friendica\BaseObject; use Friendica\Core\Config; use Friendica\Core\Installer; use Friendica\Core\Theme; -use Friendica\Util\Config\ConfigCacheLoader; +use Friendica\Util\Config\ConfigFileLoader; use RuntimeException; class AutomaticInstallation extends Console @@ -104,8 +104,8 @@ HELP; } //reload the config cache - $loader = new ConfigCacheLoader($a->getBasePath(), $a->getMode()); - $loader->loadConfigFiles($configCache); + $loader = new ConfigFileLoader($a->getBasePath(), $a->getMode()); + $loader->setupCache($configCache); } else { // Creating config file diff --git a/src/Factory/ConfigFactory.php b/src/Factory/ConfigFactory.php index 7a281d97a..1f9662bdd 100644 --- a/src/Factory/ConfigFactory.php +++ b/src/Factory/ConfigFactory.php @@ -6,19 +6,19 @@ use Friendica\Core; use Friendica\Core\Config; use Friendica\Core\Config\Adapter; use Friendica\Core\Config\Cache; -use Friendica\Util\Config\ConfigCacheLoader; +use Friendica\Util\Config\ConfigFileLoader; class ConfigFactory { /** - * @param ConfigCacheLoader $loader The Config Cache loader (INI/config/.htconfig) + * @param ConfigFileLoader $loader The Config Cache loader (INI/config/.htconfig) * * @return Cache\ConfigCache */ - public static function createCache(ConfigCacheLoader $loader) + public static function createCache(ConfigFileLoader $loader) { $configCache = new Cache\ConfigCache(); - $loader->loadConfigFiles($configCache); + $loader->setupCache($configCache); return $configCache; } diff --git a/src/Factory/DependencyFactory.php b/src/Factory/DependencyFactory.php index 9322a44cf..65bdf3714 100644 --- a/src/Factory/DependencyFactory.php +++ b/src/Factory/DependencyFactory.php @@ -24,7 +24,7 @@ class DependencyFactory { $basePath = BasePath::create($directory, $_SERVER); $mode = new App\Mode($basePath); - $configLoader = new Config\ConfigCacheLoader($basePath, $mode); + $configLoader = new Config\ConfigFileLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); Factory\DBFactory::init($basePath, $configCache, $profiler, $_SERVER); diff --git a/src/Util/Config/ConfigCacheSaver.php b/src/Util/Config/ConfigCacheSaver.php deleted file mode 100644 index 41ebca140..000000000 --- a/src/Util/Config/ConfigCacheSaver.php +++ /dev/null @@ -1,229 +0,0 @@ -settings[$cat][$key] = $value; - } - - public function reset() - { - $this->settings = []; - } - - public function saveToConfigFile($name = '') - { - $saved = false; - - if (!empty($this->getConfigFullName($name))) { - $this->saveConfigFile($this->getConfigFullName($name)); - $saved = true; - } - - if (!empty($this->getIniFullName($name))) { - $this->saveINIConfigFile($this->getIniFullName($name)); - $saved = true; - } - - if (!empty($this->getHtConfigFullName($name))) { - $this->saveToLegacyConfig($this->getHtConfigFullName($name)); - $saved = true; - } - - return $saved; - } - - /** - * Saves a value to either an config or an ini file - * - * @param string $name The configuration file name ('local', 'addon', ..) - * @param string $cat The configuration category - * @param string $key The configuration key - * @param string $value The new value - */ - private function saveToCoreConfig($name, $cat, $key, $value) - { - if (!empty($this->getConfigFullName($name))) { - $this->saveConfigFile($this->getConfigFullName($name), $cat, $key, $value); - } elseif (!empty($this->getIniFullName($name))) { - $this->saveINIConfigFile($this->getIniFullName($name), $cat, $key, $value); - } else { - return; - } - } - - /** - * Saves a value to a config file - * - * @param string $fullName The configuration full name (including the path) - * @param string $cat The configuration category - * @param string $key The configuration key - * @param string $value The new value - * - * @throws \Exception In case a file operation doesn't work - */ - private function saveConfigFile($fullName, $cat, $key, $value) - { - $reading = fopen($fullName, 'r'); - if (!$reading) { - throw new \Exception('Cannot open config file \'' . $fullName . '\'.'); - } - $writing = fopen($fullName . '.tmp', 'w'); - if (!$writing) { - throw new \Exception('Cannot create temporary config file \'' . $fullName . '.tmp\'.'); - } - $categoryFound = false; - $categoryBracketFound = false; - $lineFound = false; - $lineArrowFound = false; - while (!feof($reading)) { - $line = fgets($reading); - // find the first line like "'system' =>" - if (!$categoryFound && stristr($line, sprintf('\'%s\'', $cat))) { - $categoryFound = true; - } - // find the first line with a starting bracket ( "[" ) - if ($categoryFound && !$categoryBracketFound && stristr($line, '[')) { - $categoryBracketFound = true; - } - // find the first line with the key like "'value'" - if ($categoryBracketFound && !$lineFound && stristr($line, sprintf('\'%s\'', $key))) { - $lineFound = true; - } - // find the first line with an arrow ("=>") after finding the key - if ($lineFound && !$lineArrowFound && stristr($line, '=>')) { - $lineArrowFound = true; - } - // find the current value and replace it - if ($lineArrowFound && preg_match_all('/\'(.*?)\'/', $line, $matches, PREG_SET_ORDER)) { - $lineVal = end($matches)[0]; - $writeLine = str_replace($lineVal, '\'' . $value . '\'', $line); - $categoryFound = false; - $categoryBracketFound = false; - $lineFound = false; - $lineArrowFound = false; - // if a line contains a closing bracket for the category ( "]" ) and we didn't find the key/value pair, - // add it as a new line before the closing bracket - } elseif ($categoryBracketFound && !$lineArrowFound && stristr($line, ']')) { - $categoryFound = false; - $categoryBracketFound = false; - $lineFound = false; - $lineArrowFound = false; - $writeLine = sprintf(self::INDENT . self::INDENT .'\'%s\' => \'%s\',' . PHP_EOL, $key, $value); - $writeLine .= $line; - } else { - $writeLine = $line; - } - fputs($writing, $writeLine); - } - if (!fclose($reading)) { - throw new \Exception('Cannot close config file \'' . $fullName . '\'.'); - }; - if (!fclose($writing)) { - throw new \Exception('Cannot close temporary config file \'' . $fullName . '.tmp\'.'); - }; - if (!rename($fullName, $fullName . '.old')) { - throw new \Exception('Cannot backup current config file \'' . $fullName . '\'.'); - } - if (!rename($fullName . '.tmp', $fullName)) { - throw new \Exception('Cannot move temporary config file \'' . $fullName . '.tmp\' to current.'); - } - } - - /** - * Saves a value to a ini file - * - * @param string $fullName The configuration full name (including the path) - * @param string $cat The configuration category - * @param string $key The configuration key - * @param string $value The new value - */ - private function saveINIConfigFile($fullName, $cat, $key, $value) - { - $reading = fopen($fullName, 'r'); - $writing = fopen($fullName . '.tmp', 'w'); - $categoryFound = false; - while (!feof($reading)) { - $line = fgets($reading); - if (!$categoryFound && stristr($line, sprintf('[%s]', $cat))) { - $categoryFound = true; - $writeLine = $line; - } elseif ($categoryFound && preg_match_all('/^' . $key . '\s*=\s*(.*?)$/', $line, $matches, PREG_SET_ORDER)) { - $writeLine = $key . ' = ' . $value . PHP_EOL; - $categoryFound = false; - } elseif ($categoryFound && (preg_match_all('/^\[.*?\]$/', $line) || preg_match_all('/^INI;.*$/', $line))) { - $categoryFound = false; - $writeLine = $key . ' = ' . $value . PHP_EOL; - $writeLine .= $line; - } else { - $writeLine = $line; - } - fputs($writing, $writeLine); - } - fclose($reading); - fclose($writing); - rename($fullName, $fullName . '.old'); - rename($fullName . '.tmp', $fullName); - } - - private function saveToLegacyConfig($name, $cat, $key, $value) - { - if (empty($this->getHtConfigFullName($name))) { - return; - } - $fullName = $this->getHtConfigFullName($name); - $reading = fopen($fullName, 'r'); - $writing = fopen($fullName . '.tmp', 'w'); - $found = false; - while (!feof($reading)) { - $line = fgets($reading); - if (preg_match_all('/^\$a\-\>config\[\'' . $cat . '\',\'' . $key . '\'\]\s*=\s\'*(.*?)\'$/', $line, $matches, PREG_SET_ORDER)) { - $writeLine = $key . ' = ' . $value . PHP_EOL; - $found = true; - } else { - $writeLine = $line; - } - fputs($writing, $writeLine); - } - if (!$found) { - $writeLine = $key . ' = ' . $value . PHP_EOL; - fputs($writing, $writeLine); - } - fclose($reading); - fclose($writing); - rename($fullName, $fullName . '.old'); - rename($fullName . '.tmp', $fullName); - } -} diff --git a/src/Util/Config/ConfigCacheLoader.php b/src/Util/Config/ConfigFileLoader.php similarity index 95% rename from src/Util/Config/ConfigCacheLoader.php rename to src/Util/Config/ConfigFileLoader.php index 6eced061e..67a44a027 100644 --- a/src/Util/Config/ConfigCacheLoader.php +++ b/src/Util/Config/ConfigFileLoader.php @@ -7,14 +7,14 @@ use Friendica\Core\Addon; use Friendica\Core\Config\Cache\IConfigCache; /** - * The ConfigCacheLoader loads config-files and stores them in a IConfigCache ( @see IConfigCache ) + * The ConfigFileLoader loads config-files and stores them in a IConfigCache ( @see IConfigCache ) * * It is capable of loading the following config files: * - *.config.php (current) * - *.ini.php (deprecated) * - *.htconfig.php (deprecated) */ -class ConfigCacheLoader extends ConfigCacheManager +class ConfigFileLoader extends ConfigFileManager { /** * @var App\Mode @@ -28,7 +28,7 @@ class ConfigCacheLoader extends ConfigCacheManager } /** - * Load the configuration files + * Load the configuration files into an configuration cache * * First loads the default value for all the configuration keys, then the legacy configuration files, then the * expected local.config.php @@ -37,7 +37,7 @@ class ConfigCacheLoader extends ConfigCacheManager * * @throws \Exception */ - public function loadConfigFiles(IConfigCache $config) + public function setupCache(IConfigCache $config) { $config->load($this->loadCoreConfig('defaults')); $config->load($this->loadCoreConfig('settings')); diff --git a/src/Util/Config/ConfigCacheManager.php b/src/Util/Config/ConfigFileManager.php similarity index 94% rename from src/Util/Config/ConfigCacheManager.php rename to src/Util/Config/ConfigFileManager.php index 8fb923216..729e59d74 100644 --- a/src/Util/Config/ConfigCacheManager.php +++ b/src/Util/Config/ConfigFileManager.php @@ -2,7 +2,10 @@ namespace Friendica\Util\Config; -abstract class ConfigCacheManager +/** + * An abstract class in case of handling with config files + */ +abstract class ConfigFileManager { /** * The Sub directory of the config-files @@ -20,7 +23,7 @@ abstract class ConfigCacheManager * The default name of the user defined ini file * @var string */ - const CONFIG_INI = 'ini'; + const CONFIG_INI = 'local'; /** * The default name of the user defined legacy config file diff --git a/src/Util/Config/ConfigFileSaver.php b/src/Util/Config/ConfigFileSaver.php new file mode 100644 index 000000000..2c127188f --- /dev/null +++ b/src/Util/Config/ConfigFileSaver.php @@ -0,0 +1,294 @@ +settings[] = ['cat' => $cat, 'key' => $key, 'value' => $value]; + } + + /** + * Resetting all added configuration entries so far + */ + public function reset() + { + $this->settings = []; + } + + /** + * Save all added configuration entries to the given config files + * After updating the config entries, all configuration entries will be reseted + * + * @param string $name The name of the configuration file (default is empty, which means the default name each type) + * + * @return bool true, if at least one configuration file was successfully updated + */ + public function saveToConfigFile($name = '') + { + $saved = false; + + // Check for the *.config.php file inside the /config/ path + list($reading, $writing) = $this->openFile($this->getConfigFullName($name)); + if (isset($reading) && isset($writing)) { + $this->saveConfigFile($reading, $writing); + // Close the current file handler and rename them + if ($this->closeFile($this->getConfigFullName($name), $reading, $writing)) { + // just return true, if everything went fine + $saved = true; + } + } + + // Check for the *.ini.php file inside the /config/ path + list($reading, $writing) = $this->openFile($this->getIniFullName($name)); + if (isset($reading) && isset($writing)) { + $this->saveINIConfigFile($reading, $writing); + // Close the current file handler and rename them + if ($this->closeFile($this->getIniFullName($name), $reading, $writing)) { + // just return true, if everything went fine + $saved = true; + } + } + + // Check for the *.php file (normally .htconfig.php) inside the / path + list($reading, $writing) = $this->openFile($this->getHtConfigFullName($name)); + if (isset($reading) && isset($writing)) { + $this->saveToLegacyConfig($reading, $writing); + // Close the current file handler and rename them + if ($this->closeFile($this->getHtConfigFullName($name), $reading, $writing)) { + // just return true, if everything went fine + $saved = true; + } + } + + $this->reset(); + + return $saved; + } + + /** + * Opens a config file and returns two handler for reading and writing + * + * @param string $fullName The full name of the current config + * + * @return array An array containing the two reading and writing handler + */ + private function openFile($fullName) + { + if (empty($fullName)) { + return [null, null]; + } + + $reading = fopen($fullName, 'r'); + + if (!$reading) { + return [null, null]; + } + + $writing = fopen($fullName . '.tmp', 'w'); + + if (!$writing) { + fclose($reading); + return [null, null]; + } + + return [$reading, $writing]; + } + + /** + * Close and rename the config file + * + * @param string $fullName The full name of the current config + * @param resource $reading The reading resource handler + * @param resource $writing The writing resource handler + * + * @return bool True, if the close was successful + */ + private function closeFile($fullName, $reading, $writing) + { + fclose($reading); + fclose($writing); + + if (!rename($fullName, $fullName . '.old')) { + return false; + } + + if (!rename($fullName . '.tmp', $fullName)) { + // revert the move of the current config file to have at least the old config + rename($fullName . '.old', $fullName); + return false; + } + + return true; + } + + /** + * Saves all configuration values to a config file + * + * @param resource $reading The reading handler + * @param resource $writing The writing handler + */ + private function saveConfigFile($reading, $writing) + { + $settingsCount = count(array_keys($this->settings)); + $categoryFound = array_fill(0, $settingsCount, false); + $categoryBracketFound = array_fill(0, $settingsCount, false);; + $lineFound = array_fill(0, $settingsCount, false);; + $lineArrowFound = array_fill(0, $settingsCount, false);; + + while (!feof($reading)) { + + $line = fgets($reading); + + // check for each added setting if we have to replace a config line + for ($i = 0; $i < $settingsCount; $i++) { + + // find the first line like "'system' =>" + if (!$categoryFound[$i] && stristr($line, sprintf('\'%s\'', $this->settings[$i]['cat']))) { + $categoryFound[$i] = true; + } + + // find the first line with a starting bracket ( "[" ) + if ($categoryFound[$i] && !$categoryBracketFound[$i] && stristr($line, '[')) { + $categoryBracketFound[$i] = true; + } + + // find the first line with the key like "'value'" + if ($categoryBracketFound[$i] && !$lineFound[$i] && stristr($line, sprintf('\'%s\'', $this->settings[$i]['key']))) { + $lineFound[$i] = true; + } + + // find the first line with an arrow ("=>") after finding the key + if ($lineFound[$i] && !$lineArrowFound[$i] && stristr($line, '=>')) { + $lineArrowFound[$i] = true; + } + + // find the current value and replace it + if ($lineArrowFound[$i] && preg_match_all('/\'(.*?)\'/', $line, $matches, PREG_SET_ORDER)) { + $lineVal = end($matches)[0]; + $line = str_replace($lineVal, '\'' . $this->settings[$i]['value'] . '\'', $line); + $categoryFound[$i] = false; + $categoryBracketFound[$i] = false; + $lineFound[$i] = false; + $lineArrowFound[$i] = false; + // if a line contains a closing bracket for the category ( "]" ) and we didn't find the key/value pair, + // add it as a new line before the closing bracket + } elseif ($categoryBracketFound[$i] && !$lineArrowFound[$i] && stristr($line, ']')) { + $categoryFound[$i] = false; + $categoryBracketFound[$i] = false; + $lineFound[$i] = false; + $lineArrowFound[$i] = false; + $newLine = sprintf(self::INDENT . self::INDENT . '\'%s\' => \'%s\',' . PHP_EOL, $this->settings[$i]['key'], $this->settings[$i]['value']); + $line = $newLine . $line; + } + } + + fputs($writing, $line); + } + } + + /** + * Saves a value to a ini file + * + * @param resource $reading The reading handler + * @param resource $writing The writing handler + */ + private function saveINIConfigFile($reading, $writing) + { + $settingsCount = count(array_keys($this->settings)); + $categoryFound = array_fill(0, $settingsCount, false); + + while (!feof($reading)) { + + $line = fgets($reading); + + // check for each added setting if we have to replace a config line + for ($i = 0; $i < $settingsCount; $i++) { + + if (!$categoryFound[$i] && stristr($line, sprintf('[%s]', $this->settings[$i]['cat']))) { + $categoryFound[$i] = true; + } elseif ($categoryFound[$i] && preg_match_all('/^' . $this->settings[$i]['key'] . '\s*=\s*(.*?)$/', $line, $matches, PREG_SET_ORDER)) { + $line = $this->settings[$i]['key'] . ' = ' . $this->settings[$i]['value'] . PHP_EOL; + $categoryFound[$i] = false; + } elseif ($categoryFound[$i] && (preg_match_all('/^\[.*?\]$/', $line) || preg_match_all('/^INI;.*$/', $line))) { + $categoryFound[$i] = false; + $newLine = $this->settings[$i]['key'] . ' = ' . $this->settings[$i]['value'] . PHP_EOL; + $line = $newLine . $line; + } + } + + fputs($writing, $line); + } + } + + /** + * Saves a value to a .php file (normally .htconfig.php) + * + * @param resource $reading The reading handler + * @param resource $writing The writing handler + */ + private function saveToLegacyConfig($reading, $writing) + { + $settingsCount = count(array_keys($this->settings)); + $found = array_fill(0, $settingsCount, false); + while (!feof($reading)) { + + $line = fgets($reading); + + // check for each added setting if we have to replace a config line + for ($i = 0; $i < $settingsCount; $i++) { + + if ($this->settings[$i]['cat'] !== 'config' && preg_match_all('/^\$a\-\>config\[\'' . $this->settings[$i]['cat'] . '\'\]\[\'' . $this->settings[$i]['key'] . '\'\]\s*=\s\'*(.*?)\';$/', $line, $matches, PREG_SET_ORDER)) { + $line = '$a->config[\'' . $this->settings[$i]['cat'] . '\'][\'' . $this->settings[$i]['key'] . '\'] = \'' . $this->settings[$i]['value'] . '\';' . PHP_EOL; + $found[$i] = true; + } elseif ($this->settings[$i]['cat'] === 'config' && preg_match_all('/^\$a\-\>config\[\'' . $this->settings[$i]['key'] . '\'\]\s*=\s\'*(.*?)\';$/', $line, $matches, PREG_SET_ORDER)) { + $line = '$a->config[\'' . $this->settings[$i]['key'] . '\'] = \'' . $this->settings[$i]['value'] . '\';' . PHP_EOL; + $found[$i] = true; + } + } + + fputs($writing, $line); + } + + for ($i = 0; $i < $settingsCount; $i++) { + if (!$found[$i]) { + if ($this->settings[$i]['cat'] !== 'config') { + $line = '$a->config[\'' . $this->settings[$i]['cat'] . '\'][\'' . $this->settings[$i]['key'] . '\'] = \'' . $this->settings[$i]['value'] . '\';' . PHP_EOL; + } else { + $line = '$a->config[\'' . $this->settings[$i]['key'] . '\'] = \'' . $this->settings[$i]['value'] . '\';' . PHP_EOL; + } + + fputs($writing, $line); + } + } + } +} diff --git a/tests/DatabaseTest.php b/tests/DatabaseTest.php index d43467406..fec31b05a 100644 --- a/tests/DatabaseTest.php +++ b/tests/DatabaseTest.php @@ -9,7 +9,7 @@ use Friendica\App; use Friendica\Database\DBA; use Friendica\Factory; use Friendica\Util\BasePath; -use Friendica\Util\Config\ConfigCacheLoader; +use Friendica\Util\Config\ConfigFileLoader; use Friendica\Util\Profiler; use PHPUnit\DbUnit\DataSet\YamlDataSet; use PHPUnit\DbUnit\TestCaseTrait; @@ -43,7 +43,7 @@ abstract class DatabaseTest extends MockedTest $basePath = BasePath::create(dirname(__DIR__)); $mode = new App\Mode($basePath); - $configLoader = new ConfigCacheLoader($basePath, $mode); + $configLoader = new ConfigFileLoader($basePath, $mode); $config = Factory\ConfigFactory::createCache($configLoader); $profiler = \Mockery::mock(Profiler::class); diff --git a/tests/datasets/config/.htconfig.test.php b/tests/datasets/config/.htconfig.test.php deleted file mode 100644 index 193142c49..000000000 --- a/tests/datasets/config/.htconfig.test.php +++ /dev/null @@ -1,63 +0,0 @@ -config['system']['db_charset'] = "anotherCharset"; - -// Choose a legal default timezone. If you are unsure, use "America/Los_Angeles". -// It can be changed later and only applies to timestamps for anonymous viewers. -$default_timezone = 'Europe/Berlin'; -$lang = 'fr'; - -// What is your site name? -$a->config['sitename'] = "Friendica My Network"; - -// Your choices are REGISTER_OPEN, REGISTER_APPROVE, or REGISTER_CLOSED. -// Be certain to create your own personal account before setting -// REGISTER_CLOSED. 'register_text' (if set) will be displayed prominently on -// the registration page. REGISTER_APPROVE requires you set 'admin_email' -// to the email address of an already registered person who can authorise -// and/or approve/deny the request. -// In order to perform system administration via the admin panel, admin_email -// must precisely match the email address of the person logged in. -$a->config['register_policy'] = REGISTER_OPEN; -$a->config['register_text'] = 'A register text'; -$a->config['admin_email'] = 'admin@friendica.local'; -$a->config['admin_nickname'] = 'Friendly admin'; - -// Maximum size of an imported message, 0 is unlimited -$a->config['max_import_size'] = 999; - -// maximum size of uploaded photos -$a->config['system']['maximagesize'] = 666; - -// Location of PHP command line processor -$a->config['php_path'] = '/another/php'; - -// PuSH - aka pubsubhubbub URL. This makes delivery of public posts as fast as private posts -$a->config['system']['huburl'] = '[internal]'; - -// allowed themes (change this from admin panel after installation) -$a->config['system']['allowed_themes'] = 'quattro,vier,duepuntozero'; - -// default system theme -$a->config['system']['theme'] = 'duepuntozero'; - -// By default allow pseudonyms -$a->config['system']['no_regfullname'] = true; - -//Deny public access to the local directory -//$a->config['system']['block_local_dir'] = false; -// Location of the global directory -$a->config['system']['directory'] = 'http://another.url'; diff --git a/tests/datasets/config/local.config.php b/tests/datasets/config/local.config.php index 8a392909f..f28e1f2e8 100644 --- a/tests/datasets/config/local.config.php +++ b/tests/datasets/config/local.config.php @@ -23,5 +23,6 @@ return [ 'system' => [ 'default_timezone' => 'UTC', 'language' => 'en', + 'theme' => 'frio', ], ]; diff --git a/tests/datasets/config/local.ini.php b/tests/datasets/config/local.ini.php index 1fea0b028..a9e462d13 100644 --- a/tests/datasets/config/local.ini.php +++ b/tests/datasets/config/local.ini.php @@ -11,6 +11,9 @@ username = testuser password = testpw database = testdb +[system] +theme = frio + [config] admin_email = admin@test.it INI; diff --git a/tests/include/ApiTest.php b/tests/include/ApiTest.php index bd1b04108..5c2e1657a 100644 --- a/tests/include/ApiTest.php +++ b/tests/include/ApiTest.php @@ -13,7 +13,7 @@ use Friendica\Core\System; use Friendica\Factory; use Friendica\Network\HTTPException; use Friendica\Util\BasePath; -use Friendica\Util\Config\ConfigCacheLoader; +use Friendica\Util\Config\ConfigFileLoader; use Monolog\Handler\TestHandler; require_once __DIR__ . '/../../include/api.php'; @@ -38,7 +38,7 @@ class ApiTest extends DatabaseTest { $basePath = BasePath::create(dirname(__DIR__) . '/../'); $mode = new App\Mode($basePath); - $configLoader = new ConfigCacheLoader($basePath, $mode); + $configLoader = new ConfigFileLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); Factory\DBFactory::init($basePath, $configCache, $profiler, $_SERVER); diff --git a/tests/src/Database/DBATest.php b/tests/src/Database/DBATest.php index 28f5afbd7..bc8743da5 100644 --- a/tests/src/Database/DBATest.php +++ b/tests/src/Database/DBATest.php @@ -7,7 +7,7 @@ use Friendica\Database\DBA; use Friendica\Factory; use Friendica\Test\DatabaseTest; use Friendica\Util\BasePath; -use Friendica\Util\Config\ConfigCacheLoader; +use Friendica\Util\Config\ConfigFileLoader; class DBATest extends DatabaseTest { @@ -15,7 +15,7 @@ class DBATest extends DatabaseTest { $basePath = BasePath::create(dirname(__DIR__) . '/../../'); $mode = new App\Mode($basePath); - $configLoader = new ConfigCacheLoader($basePath, $mode); + $configLoader = new ConfigFileLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); Factory\DBFactory::init($basePath, $configCache, $profiler, $_SERVER); diff --git a/tests/src/Database/DBStructureTest.php b/tests/src/Database/DBStructureTest.php index 65d5c85a4..0c1da172f 100644 --- a/tests/src/Database/DBStructureTest.php +++ b/tests/src/Database/DBStructureTest.php @@ -7,7 +7,7 @@ use Friendica\Database\DBStructure; use Friendica\Factory; use Friendica\Test\DatabaseTest; use Friendica\Util\BasePath; -use Friendica\Util\Config\ConfigCacheLoader; +use Friendica\Util\Config\ConfigFileLoader; class DBStructureTest extends DatabaseTest { @@ -15,7 +15,7 @@ class DBStructureTest extends DatabaseTest { $basePath = BasePath::create(dirname(__DIR__) . '/../../'); $mode = new App\Mode($basePath); - $configLoader = new ConfigCacheLoader($basePath, $mode); + $configLoader = new ConfigFileLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); Factory\DBFactory::init($basePath, $configCache, $profiler, $_SERVER); diff --git a/tests/src/Util/Config/ConfigCacheSaverTest.php b/tests/src/Util/Config/ConfigCacheSaverTest.php deleted file mode 100644 index a0d9502c2..000000000 --- a/tests/src/Util/Config/ConfigCacheSaverTest.php +++ /dev/null @@ -1,133 +0,0 @@ -setUpVfsDir(); - $this->mode = \Mockery::mock(App\Mode::class); - $this->mode->shouldReceive('isInstall')->andReturn(true); - } - /** - * Test the saveToConfigFile() method with a local.config.php file - */ - public function testSaveToConfigFileLocal() - { - $this->delConfigFile('local.config.php'); - $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR . - 'local.config.php'; - - vfsStream::newFile('local.config.php') - ->at($this->root->getChild('config')) - ->setContent(file_get_contents($file)); - - $configCacheSaver = new ConfigCacheSaver($this->root->url()); - $configCacheLoader = new ConfigCacheLoader($this->root->url(), $this->mode); - $configCache = new ConfigCache(); - $configCacheLoader->loadConfigFiles($configCache); - - $this->assertEquals('admin@test.it', $configCache->get('config', 'admin_email')); - $this->assertNull($configCache->get('config', 'test_val')); - - $configCacheSaver->saveToConfigFile('config', 'admin_email', 'new@mail.it'); - $configCacheSaver->saveToConfigFile('config', 'test_val', 'Testing$!"$with@all.we can!'); - - $newConfigCache = new ConfigCache(); - $configCacheLoader->loadConfigFiles($newConfigCache); - - $this->assertEquals('new@mail.it', $newConfigCache->get('config', 'admin_email')); - $this->assertEquals('Testing$!"$with@all.we can!', $newConfigCache->get('config', 'test_val')); - $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php')); - $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php.old')); - $this->assertFalse($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php.tmp')); - - $this->assertEquals(file_get_contents($file), file_get_contents($this->root->getChild('config' . DIRECTORY_SEPARATOR . 'local.config.php.old')->url())); - } - /** - * Test the saveToConfigFile() method with a local.ini.php file - */ - public function testSaveToConfigFileINI() - { - $this->delConfigFile('local.config.php'); - $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR . - 'local.ini.php'; - vfsStream::newFile('local.ini.php') - ->at($this->root->getChild('config')) - ->setContent(file_get_contents($file)); - $configCacheSaver = new ConfigCacheSaver($this->root->url()); - $configCacheLoader = new ConfigCacheLoader($this->root->url(), $this->mode); - $configCache = new ConfigCache(); - $configCacheLoader->loadConfigFiles($configCache); - $this->assertEquals('admin@test.it', $configCache->get('config', 'admin_email')); - $this->assertNull($configCache->get('config', 'test_val')); - $configCacheSaver->saveToConfigFile('config', 'admin_email', 'new@mail.it'); - $configCacheSaver->saveToConfigFile('config', 'test_val', "Testing@with.all we can"); - $newConfigCache = new ConfigCache(); - $configCacheLoader->loadConfigFiles($newConfigCache); - $this->assertEquals('new@mail.it', $newConfigCache->get('config', 'admin_email')); - $this->assertEquals("Testing@with.all we can", $newConfigCache->get('config', 'test_val')); - $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.ini.php')); - $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.ini.php.old')); - $this->assertFalse($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.ini.php.tmp')); - $this->assertEquals(file_get_contents($file), file_get_contents($this->root->getChild('config' . DIRECTORY_SEPARATOR . 'local.ini.old')->url())); - } - /** - * Test the saveToConfigFile() method with a .htconfig.php file - * @todo fix it after 2019.03 merge to develop - */ - public function testSaveToConfigFileHtconfig() - { - $this->markTestSkipped('Needs 2019.03 merge to develop first'); - $this->delConfigFile('local.config.php'); - $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - '..' . DIRECTORY_SEPARATOR . - 'datasets' . DIRECTORY_SEPARATOR . - 'config' . DIRECTORY_SEPARATOR . - '.htconfig.test.php'; - vfsStream::newFile('.htconfig.php') - ->at($this->root) - ->setContent(file_get_contents($file)); - $configCacheSaver = new ConfigCacheSaver($this->root->url(), $this->mode); - $configCache = new ConfigCache(); - $configCacheSaver->loadConfigFiles($configCache); - $this->assertEquals('admin@test.it', $configCache->get('config', 'admin_email')); - $this->assertEquals('!!', $configCache->get('config', 'test_val')); - $configCacheSaver->saveToConfigFile('config', 'admin_email', 'new@mail.it'); - $configCacheSaver->saveToConfigFile('config', 'test_val', 'Testing$!"$with@all.we can!'); - $newConfigCache = new ConfigCache(); - $configCacheSaver->loadConfigFiles($newConfigCache); - $this->assertEquals('new@mail.it', $newConfigCache->get('config', 'admin_email')); - $this->assertEquals('Testing$!"$with@all.we can!', $newConfigCache->get('config', 'test_val')); - $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . '.htconfig.php')); - $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . '.htconfig.php.old')); - $this->assertFalse($this->root->hasChild('config' . DIRECTORY_SEPARATOR . '.htconfig.php.tmp')); - $this->assertEquals(file_get_contents($file), file_get_contents($this->root->getChild('config' . DIRECTORY_SEPARATOR . '.htconfig.php.old')->url())); - } -} diff --git a/tests/src/Util/Config/ConfigCacheLoaderTest.php b/tests/src/Util/Config/ConfigFileLoaderTest.php similarity index 84% rename from tests/src/Util/Config/ConfigCacheLoaderTest.php rename to tests/src/Util/Config/ConfigFileLoaderTest.php index 45245d47c..ad0fe8afc 100644 --- a/tests/src/Util/Config/ConfigCacheLoaderTest.php +++ b/tests/src/Util/Config/ConfigFileLoaderTest.php @@ -6,11 +6,11 @@ use Friendica\App; use Friendica\Core\Config\Cache\ConfigCache; use Friendica\Test\MockedTest; use Friendica\Test\Util\VFSTrait; -use Friendica\Util\Config\ConfigCacheLoader; +use Friendica\Util\Config\ConfigFileLoader; use Mockery\MockInterface; use org\bovigo\vfs\vfsStream; -class ConfigCacheLoaderTest extends MockedTest +class ConfigFileLoaderTest extends MockedTest { use VFSTrait; @@ -34,10 +34,10 @@ class ConfigCacheLoaderTest extends MockedTest */ public function testLoadConfigFiles() { - $configCacheLoader = new ConfigCacheLoader($this->root->url(), $this->mode); + $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode); $configCache = new ConfigCache(); - $configCacheLoader->loadConfigFiles($configCache); + $configFileLoader->setupCache($configCache); $this->assertEquals($this->root->url(), $configCache->get('system', 'basepath')); } @@ -55,10 +55,10 @@ class ConfigCacheLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent('root->url(), $this->mode); + $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode); $configCache = new ConfigCache(); - $configCacheLoader->loadConfigFiles($configCache); + $configFileLoader->setupCache($configCache); } /** @@ -79,10 +79,10 @@ class ConfigCacheLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($file)); - $configCacheLoader = new ConfigCacheLoader($this->root->url(), $this->mode); + $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode); $configCache = new ConfigCache(); - $configCacheLoader->loadConfigFiles($configCache); + $configFileLoader->setupCache($configCache); $this->assertEquals('testhost', $configCache->get('database', 'hostname')); $this->assertEquals('testuser', $configCache->get('database', 'username')); @@ -111,10 +111,10 @@ class ConfigCacheLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($file)); - $configCacheLoader = new ConfigCacheLoader($this->root->url(), $this->mode); + $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode); $configCache = new ConfigCache(); - $configCacheLoader->loadConfigFiles($configCache); + $configFileLoader->setupCache($configCache); $this->assertEquals('testhost', $configCache->get('database', 'hostname')); $this->assertEquals('testuser', $configCache->get('database', 'username')); @@ -136,16 +136,16 @@ class ConfigCacheLoaderTest extends MockedTest '..' . DIRECTORY_SEPARATOR . 'datasets' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . - '.htconfig.test.php'; + '.htconfig.php'; vfsStream::newFile('.htconfig.php') ->at($this->root) ->setContent(file_get_contents($file)); - $configCacheLoader = new ConfigCacheLoader($this->root->url(), $this->mode); + $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode); $configCache = new ConfigCache(); - $configCacheLoader->loadConfigFiles($configCache); + $configFileLoader->setupCache($configCache); $this->assertEquals('testhost', $configCache->get('database', 'hostname')); $this->assertEquals('testuser', $configCache->get('database', 'username')); @@ -157,7 +157,7 @@ class ConfigCacheLoaderTest extends MockedTest $this->assertEquals('Europe/Berlin', $configCache->get('system', 'default_timezone')); $this->assertEquals('fr', $configCache->get('system', 'language')); - $this->assertEquals('admin@friendica.local', $configCache->get('config', 'admin_email')); + $this->assertEquals('admin@test.it', $configCache->get('config', 'admin_email')); $this->assertEquals('Friendly admin', $configCache->get('config', 'admin_nickname')); $this->assertEquals('/another/php', $configCache->get('config', 'php_path')); @@ -191,9 +191,9 @@ class ConfigCacheLoaderTest extends MockedTest ->at($this->root->getChild('addon')->getChild('test')->getChild('config')) ->setContent(file_get_contents($file)); - $configCacheLoader = new ConfigCacheLoader($this->root->url(), $this->mode); + $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode); - $conf = $configCacheLoader->loadAddonConfig('test'); + $conf = $configFileLoader->loadAddonConfig('test'); $this->assertEquals('testhost', $conf['database']['hostname']); $this->assertEquals('testuser', $conf['database']['username']); diff --git a/tests/src/Util/Config/ConfigFileSaverTest.php b/tests/src/Util/Config/ConfigFileSaverTest.php new file mode 100644 index 000000000..5b472df49 --- /dev/null +++ b/tests/src/Util/Config/ConfigFileSaverTest.php @@ -0,0 +1,139 @@ +setUpVfsDir(); + $this->mode = \Mockery::mock(App\Mode::class); + $this->mode->shouldReceive('isInstall')->andReturn(true); + } + + public function dataConfigFiles() + { + return [ + 'config' => [ + 'fileName' => 'local.config.php', + 'filePath' => dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config', + 'relativePath' => 'config', + ], + 'ini' => [ + 'fileName' => 'local.ini.php', + 'filePath' => dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config', + 'relativePath' => 'config', + ], + 'htconfig' => [ + 'fileName' => '.htconfig.php', + 'filePath' => dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config', + 'relativePath' => '', + ], + ]; + } + + /** + * Test the saveToConfigFile() method + * @dataProvider dataConfigFiles + * + * @todo 20190324 [nupplaphil] for ini-configs, it isn't possible to use $ or ! inside values + */ + public function testSaveToConfig($fileName, $filePath, $relativePath) + { + $this->delConfigFile('local.config.php'); + + if (empty($relativePath)) { + $root = $this->root; + $relativeFullName = $fileName; + } else { + $root = $this->root->getChild($relativePath); + $relativeFullName = $relativePath . DIRECTORY_SEPARATOR . $fileName; + } + + vfsStream::newFile($fileName) + ->at($root) + ->setContent(file_get_contents($filePath . DIRECTORY_SEPARATOR . $fileName)); + + $configFileSaver = new ConfigFileSaver($this->root->url()); + $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode); + $configCache = new ConfigCache(); + $configFileLoader->setupCache($configCache); + + $this->assertEquals('admin@test.it', $configCache->get('config', 'admin_email')); + $this->assertNull($configCache->get('config', 'test_val')); + $this->assertNull($configCache->get('system', 'test_val2')); + + $configFileSaver->addConfigValue('system', 'theme', 'frio'); + $configFileSaver->addConfigValue('config', 'admin_email', 'new@mail.it'); + $configFileSaver->addConfigValue('config', 'test_val', 'Testingwith@all.we can'); + $configFileSaver->addConfigValue('system', 'theme', 'vier'); + $configFileSaver->addConfigValue('system', 'test_val2', 'TestIt Now'); + $this->assertTrue($configFileSaver->saveToConfigFile()); + + $newConfigCache = new ConfigCache(); + $configFileLoader->setupCache($newConfigCache); + + $this->assertEquals('new@mail.it', $newConfigCache->get('config', 'admin_email')); + $this->assertEquals('Testingwith@all.we can', $newConfigCache->get('config', 'test_val')); + $this->assertEquals('vier', $newConfigCache->get('system', 'theme')); + $this->assertEquals('TestIt Now', $newConfigCache->get('system', 'test_val2')); + + $this->assertTrue($this->root->hasChild($relativeFullName)); + $this->assertTrue($this->root->hasChild($relativeFullName . '.old')); + $this->assertFalse($this->root->hasChild($relativeFullName . '.tmp')); + + $this->assertEquals(file_get_contents($filePath . DIRECTORY_SEPARATOR . $fileName), file_get_contents($this->root->getChild($relativeFullName . '.old')->url())); + } + + /** + * Test the saveToConfigFile() method without permissions + * @dataProvider dataConfigFiles + */ + public function testNoPermission($fileName, $filePath, $relativePath) + { + $this->delConfigFile('local.config.php'); + + if (empty($relativePath)) { + $root = $this->root; + $relativeFullName = $fileName; + } else { + $root = $this->root->getChild($relativePath); + $relativeFullName = $relativePath . DIRECTORY_SEPARATOR . $fileName; + } + + $root->chmod(000); + + vfsStream::newFile($fileName) + ->at($root) + ->setContent(file_get_contents($filePath . DIRECTORY_SEPARATOR . $fileName)); + } +} diff --git a/update.php b/update.php index 140c8fcb5..4ee85739d 100644 --- a/update.php +++ b/update.php @@ -12,7 +12,7 @@ use Friendica\Model\Contact; use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\User; -use Friendica\Util\Config\ConfigCacheSaver; +use Friendica\Util\Config\ConfigFileSaver; use Friendica\Util\DateTimeFormat; /** @@ -357,8 +357,9 @@ function update_1303() { $app = \Friendica\BaseObject::getApp(); $configCache = $app->getConfigCache(); - $configCacheSaver = new ConfigCacheSaver($app->getBasePath()); - $updateConfigEntry = function($cat, $key) use ($configCache, $configCacheSaver) { + $configFileSaver = new ConfigFileSaver($app->getBasePath()); + + $updateConfigEntry = function($cat, $key) use ($configCache, $configFileSaver) { // check if the config file differs from the whole configuration (= The db contains other values) $fileConfig = $configCache->get($cat, $key); if ($fileConfig === '!!') { @@ -367,11 +368,14 @@ function update_1303() $savedConfig = Config::get($cat, $key, null, true); if ($fileConfig !== $savedConfig) { Logger::info('Difference in config found', ['cat' => $cat, 'key' => $key, 'file' => $fileConfig, 'saved' => $savedConfig]); - $configCacheSaver->saveToConfigFile($cat, $key, $savedConfig); + $configFileSaver->addConfigValue($cat, $key, $savedConfig); } else { Logger::info('No Difference in config found', ['cat' => $cat, 'key' => $key, 'value' => $fileConfig, 'saved' => $savedConfig]); } }; + + $configFileSaver->saveToConfigFile(); + $updateConfigEntry('config', 'hostname'); $updateConfigEntry('system', 'basepath'); return Update::SUCCESS; From 3cf0cb71f1ef3a8865e8086b0560a4f0647b086f Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 24 Mar 2019 20:41:11 +0100 Subject: [PATCH 6/8] Fixing tests --- .gitignore | 4 +- src/Core/Config/Configuration.php | 2 +- tests/datasets/config/.htconfig.php | 63 +++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 tests/datasets/config/.htconfig.php diff --git a/.gitignore b/.gitignore index 0d18ab0bd..49d08ba71 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ favicon.* -.htconfig.php -.htpreconfig.php +/.htconfig.php +/.htpreconfig.php \#* *.log *.out diff --git a/src/Core/Config/Configuration.php b/src/Core/Config/Configuration.php index abf6af37c..c6fe626d9 100644 --- a/src/Core/Config/Configuration.php +++ b/src/Core/Config/Configuration.php @@ -127,7 +127,7 @@ class Configuration $cached = $this->configCache->set($cat, $key, $value); // If there is no connected adapter, we're finished - if (!$this->configAdapter->isConnected() && !empty($this->configSaveBlacklist[$cat][$key])) { + if (!$this->configAdapter->isConnected() || !empty($this->configSaveBlacklist[$cat][$key])) { return $cached; } diff --git a/tests/datasets/config/.htconfig.php b/tests/datasets/config/.htconfig.php new file mode 100644 index 000000000..04e3e9cc9 --- /dev/null +++ b/tests/datasets/config/.htconfig.php @@ -0,0 +1,63 @@ +config['system']['db_charset'] = "anotherCharset"; + +// Choose a legal default timezone. If you are unsure, use "America/Los_Angeles". +// It can be changed later and only applies to timestamps for anonymous viewers. +$default_timezone = 'Europe/Berlin'; +$lang = 'fr'; + +// What is your site name? +$a->config['sitename'] = "Friendica My Network"; + +// Your choices are REGISTER_OPEN, REGISTER_APPROVE, or REGISTER_CLOSED. +// Be certain to create your own personal account before setting +// REGISTER_CLOSED. 'register_text' (if set) will be displayed prominently on +// the registration page. REGISTER_APPROVE requires you set 'admin_email' +// to the email address of an already registered person who can authorise +// and/or approve/deny the request. +// In order to perform system administration via the admin panel, admin_email +// must precisely match the email address of the person logged in. +$a->config['register_policy'] = REGISTER_OPEN; +$a->config['register_text'] = 'A register text'; +$a->config['admin_email'] = 'admin@test.it'; +$a->config['admin_nickname'] = 'Friendly admin'; + +// Maximum size of an imported message, 0 is unlimited +$a->config['max_import_size'] = 999; + +// maximum size of uploaded photos +$a->config['system']['maximagesize'] = 666; + +// Location of PHP command line processor +$a->config['php_path'] = '/another/php'; + +// PuSH - aka pubsubhubbub URL. This makes delivery of public posts as fast as private posts +$a->config['system']['huburl'] = '[internal]'; + +// allowed themes (change this from admin panel after installation) +$a->config['system']['allowed_themes'] = 'quattro,vier,duepuntozero'; + +// default system theme +$a->config['system']['theme'] = 'frio'; + +// By default allow pseudonyms +$a->config['system']['no_regfullname'] = true; + +//Deny public access to the local directory +//$a->config['system']['block_local_dir'] = false; +// Location of the global directory +$a->config['system']['directory'] = 'http://another.url'; From 23654ce566c18c86de30f3263752be49cb0541de Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 24 Mar 2019 22:51:30 +0100 Subject: [PATCH 7/8] Added Update checks - Logging - Console - Admin overview --- mod/admin.php | 7 +- src/App.php | 17 +--- src/Core/Update.php | 87 ++++++++++++++++++- src/Factory/DependencyFactory.php | 2 +- src/Util/Config/ConfigFileLoader.php | 2 +- src/Util/Config/ConfigFileSaver.php | 57 ++++++++++-- src/Worker/DBUpdate.php | 6 +- tests/include/ApiTest.php | 2 +- tests/src/Database/DBATest.php | 2 +- tests/src/Database/DBStructureTest.php | 2 +- tests/src/Util/Config/ConfigFileSaverTest.php | 54 +++++++++++- update.php | 37 ++------ 12 files changed, 215 insertions(+), 60 deletions(-) diff --git a/mod/admin.php b/mod/admin.php index 851dd0b09..848ed2521 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -923,6 +923,10 @@ function admin_page_summary(App $a) $showwarning = true; $warningtext[] = L10n::t('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.'); } + if (Config::get('system', 'update') == Update::FAILED) { + $showwarning = true; + $warningtext[] = L10n::t('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.)'); + } $last_worker_call = Config::get('system', 'last_worker_execution', false); if (!$last_worker_call) { @@ -1088,9 +1092,8 @@ function admin_page_site_post(App $a) // update config $configFileSaver = new \Friendica\Util\Config\ConfigFileSaver($a->getBasePath()); - $configFileSaver->addConfigValue('system', 'hostname', parse_url($new_url, PHP_URL_HOST)); + $configFileSaver->addConfigValue('config', 'hostname', parse_url($new_url, PHP_URL_HOST)); $configFileSaver->saveToConfigFile(); - Config::set('system', 'hostname', parse_url($new_url, PHP_URL_HOST)); Config::set('system', 'url', $new_url); $a->setBaseURL($new_url); diff --git a/src/App.php b/src/App.php index 3a96182b1..1ed6390b4 100644 --- a/src/App.php +++ b/src/App.php @@ -75,11 +75,6 @@ class App */ private $mode; - /** - * @var string The App base path - */ - private $basePath; - /** * @var string The App URL path */ @@ -142,7 +137,7 @@ class App */ public function getBasePath() { - return $this->basePath; + return $this->config->get('system', 'basepath'); } /** @@ -216,7 +211,6 @@ class App /** * @brief App constructor. * - * @param string $basePath The basedir of the app * @param Configuration $config The Configuration * @param App\Mode $mode The mode of this Friendica app * @param LoggerInterface $logger The current app logger @@ -225,7 +219,7 @@ class App * * @throws Exception if the Basepath is not usable */ - public function __construct($basePath, Configuration $config, App\Mode $mode, LoggerInterface $logger, Profiler $profiler, $isBackend = true) + public function __construct(Configuration $config, App\Mode $mode, LoggerInterface $logger, Profiler $profiler, $isBackend = true) { BaseObject::setApp($this); @@ -233,13 +227,6 @@ class App $this->config = $config; $this->profiler = $profiler; $this->mode = $mode; - $cfgBasePath = $this->config->get('system', 'basepath'); - $this->basePath = !empty($cfgBasePath) ? $cfgBasePath : $basePath; - - if (!Core\System::isDirectoryUsable($this->getBasePath(), false)) { - throw new Exception('Basepath \'' . $this->getBasePath() . '\' isn\'t usable.'); - } - $this->basePath = rtrim($this->getBasePath(), DIRECTORY_SEPARATOR); $this->checkBackend($isBackend); $this->checkFriendicaApp(); diff --git a/src/Core/Update.php b/src/Core/Update.php index bb2513d38..7cb321267 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -2,8 +2,12 @@ namespace Friendica\Core; +use Friendica\App; +use Friendica\Core\Config\Cache\IConfigCache; use Friendica\Database\DBA; use Friendica\Database\DBStructure; +use Friendica\Util\Config\ConfigFileLoader; +use Friendica\Util\Config\ConfigFileSaver; use Friendica\Util\Strings; class Update @@ -24,6 +28,11 @@ class Update return; } + // Don't check the status if the last update was failed + if (Config::get('system', 'update', Update::SUCCESS, true) == Update::FAILED) { + return; + } + $build = Config::get('system', 'build'); if (empty($build)) { @@ -101,7 +110,9 @@ class Update for ($x = $stored + 1; $x <= $current; $x++) { $r = self::runUpdateFunction($x, 'pre_update'); if (!$r) { - break; + Config::set('system', 'update', Update::FAILED); + Lock::release('dbupdate'); + return $r; } } @@ -115,6 +126,7 @@ class Update ); } Logger::error('Update ERROR.', ['from' => $stored, 'to' => $current, 'retval' => $retval]); + Config::set('system', 'update', Update::FAILED); Lock::release('dbupdate'); return $retval; } else { @@ -127,7 +139,9 @@ class Update for ($x = $stored + 1; $x <= $current; $x++) { $r = self::runUpdateFunction($x, 'update'); if (!$r) { - break; + Config::set('system', 'update', Update::FAILED); + Lock::release('dbupdate'); + return $r; } } @@ -136,6 +150,7 @@ class Update self::updateSuccessfull($stored, $current); } + Config::set('system', 'update', Update::SUCCESS); Lock::release('dbupdate'); } } @@ -208,6 +223,74 @@ class Update } } + /** + * Checks the config settings and saves given config values into the config file + * + * @param string $basePath The basepath of Friendica + * @param App\Mode $mode The Application mode + * + * @return bool True, if something has been saved + */ + public static function saveConfigToFile($basePath, App\Mode $mode) + { + $configFileLoader = new ConfigFileLoader($basePath, $mode); + $configCache = new Config\Cache\ConfigCache(); + $configFileLoader->setupCache($configCache); + $configFileSaver = new ConfigFileSaver($basePath); + + $updated = false; + + if (self::updateConfigEntry($configCache, $configFileSaver,'config', 'hostname')) { + $updated = true; + }; + if (self::updateConfigEntry($configCache, $configFileSaver,'system', 'basepath')) { + $updated = true; + } + + if (!$configFileSaver->saveToConfigFile()) { + Logger::alert('Config entry update failed - maybe wrong permission?'); + return false; + } + + DBA::delete('config', ['cat' => 'config', 'k' => 'hostname']); + DBA::delete('config', ['cat' => 'system', 'k' => 'basepath']); + + return $updated; + } + + /** + * Adds a value to the ConfigFileSave in case it isn't already updated + * + * @param IConfigCache $configCache The cached config file + * @param ConfigFileSaver $configFileSaver The config file saver + * @param string $cat The config category + * @param string $key The config key + * + * @return boolean True, if a value was updated + * + * @throws \Exception if DBA or Logger doesn't work + */ + private static function updateConfigEntry(IConfigCache $configCache, ConfigFileSaver $configFileSaver, $cat, $key) + { + // check if the config file differs from the whole configuration (= The db contains other values) + $fileConfig = $configCache->get($cat, $key); + + $savedConfig = DBA::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $key]); + + if (!DBA::isResult($savedConfig)) { + return false; + } + + if ($fileConfig !== $savedConfig['v']) { + Logger::info('Difference in config found', ['cat' => $cat, 'key' => $key, 'file' => $fileConfig, 'saved' => $savedConfig['v']]); + $configFileSaver->addConfigValue($cat, $key, $savedConfig['v']); + } else { + Logger::info('No Difference in config found', ['cat' => $cat, 'key' => $key, 'value' => $fileConfig, 'saved' => $savedConfig['v']]); + } + + return true; + } + /** * send the email and do what is needed to do on update fails * diff --git a/src/Factory/DependencyFactory.php b/src/Factory/DependencyFactory.php index 65bdf3714..63defd95f 100644 --- a/src/Factory/DependencyFactory.php +++ b/src/Factory/DependencyFactory.php @@ -34,6 +34,6 @@ class DependencyFactory $logger = Factory\LoggerFactory::create($channel, $config, $profiler); Factory\LoggerFactory::createDev($channel, $config, $profiler); - return new App($basePath, $config, $mode, $logger, $profiler, $isBackend); + return new App($config, $mode, $logger, $profiler, $isBackend); } } diff --git a/src/Util/Config/ConfigFileLoader.php b/src/Util/Config/ConfigFileLoader.php index 67a44a027..d677d4bfe 100644 --- a/src/Util/Config/ConfigFileLoader.php +++ b/src/Util/Config/ConfigFileLoader.php @@ -33,7 +33,7 @@ class ConfigFileLoader extends ConfigFileManager * First loads the default value for all the configuration keys, then the legacy configuration files, then the * expected local.config.php * - * @param IConfigCache The config cache to load to + * @param IConfigCache $config The config cache to load to * * @throws \Exception */ diff --git a/src/Util/Config/ConfigFileSaver.php b/src/Util/Config/ConfigFileSaver.php index 2c127188f..e32e11a4d 100644 --- a/src/Util/Config/ConfigFileSaver.php +++ b/src/Util/Config/ConfigFileSaver.php @@ -34,6 +34,17 @@ class ConfigFileSaver extends ConfigFileManager */ public function addConfigValue($cat, $key, $value) { + $settingsCount = count(array_keys($this->settings)); + + for ($i = 0; $i < $settingsCount; $i++) { + // if already set, overwrite the value + if ($this->settings[$i]['cat'] === $cat && + $this->settings[$i]['key'] === $key) { + $this->settings[$i] = ['cat' => $cat, 'key' => $key, 'value' => $value]; + return; + } + } + $this->settings[] = ['cat' => $cat, 'key' => $key, 'value' => $value]; } @@ -51,10 +62,15 @@ class ConfigFileSaver extends ConfigFileManager * * @param string $name The name of the configuration file (default is empty, which means the default name each type) * - * @return bool true, if at least one configuration file was successfully updated + * @return bool true, if at least one configuration file was successfully updated or nothing to do */ public function saveToConfigFile($name = '') { + // If no settings et, return true + if (count(array_keys($this->settings)) === 0) { + return true; + } + $saved = false; // Check for the *.config.php file inside the /config/ path @@ -108,13 +124,22 @@ class ConfigFileSaver extends ConfigFileManager return [null, null]; } - $reading = fopen($fullName, 'r'); + try { + $reading = fopen($fullName, 'r'); + } catch (\Exception $exception) { + return [null, null]; + } if (!$reading) { return [null, null]; } - $writing = fopen($fullName . '.tmp', 'w'); + try { + $writing = fopen($fullName . '.tmp', 'w'); + } catch (\Exception $exception) { + fclose($reading); + return [null, null]; + } if (!$writing) { fclose($reading); @@ -138,11 +163,25 @@ class ConfigFileSaver extends ConfigFileManager fclose($reading); fclose($writing); - if (!rename($fullName, $fullName . '.old')) { + try { + $renamed = rename($fullName, $fullName . '.old'); + } catch (\Exception $exception) { return false; } - if (!rename($fullName . '.tmp', $fullName)) { + if (!$renamed) { + return false; + } + + try { + $renamed = rename($fullName . '.tmp', $fullName); + } catch (\Exception $exception) { + // revert the move of the current config file to have at least the old config + rename($fullName . '.old', $fullName); + return false; + } + + if (!$renamed) { // revert the move of the current config file to have at least the old config rename($fullName . '.old', $fullName); return false; @@ -234,11 +273,16 @@ class ConfigFileSaver extends ConfigFileManager // check for each added setting if we have to replace a config line for ($i = 0; $i < $settingsCount; $i++) { + // find the category of the current setting if (!$categoryFound[$i] && stristr($line, sprintf('[%s]', $this->settings[$i]['cat']))) { $categoryFound[$i] = true; + + // check the current value } elseif ($categoryFound[$i] && preg_match_all('/^' . $this->settings[$i]['key'] . '\s*=\s*(.*?)$/', $line, $matches, PREG_SET_ORDER)) { $line = $this->settings[$i]['key'] . ' = ' . $this->settings[$i]['value'] . PHP_EOL; $categoryFound[$i] = false; + + // If end of INI file, add the line before the INI end } elseif ($categoryFound[$i] && (preg_match_all('/^\[.*?\]$/', $line) || preg_match_all('/^INI;.*$/', $line))) { $categoryFound[$i] = false; $newLine = $this->settings[$i]['key'] . ' = ' . $this->settings[$i]['value'] . PHP_EOL; @@ -267,9 +311,12 @@ class ConfigFileSaver extends ConfigFileManager // check for each added setting if we have to replace a config line for ($i = 0; $i < $settingsCount; $i++) { + // check for a non plain config setting (use category too) if ($this->settings[$i]['cat'] !== 'config' && preg_match_all('/^\$a\-\>config\[\'' . $this->settings[$i]['cat'] . '\'\]\[\'' . $this->settings[$i]['key'] . '\'\]\s*=\s\'*(.*?)\';$/', $line, $matches, PREG_SET_ORDER)) { $line = '$a->config[\'' . $this->settings[$i]['cat'] . '\'][\'' . $this->settings[$i]['key'] . '\'] = \'' . $this->settings[$i]['value'] . '\';' . PHP_EOL; $found[$i] = true; + + // check for a plain config setting (don't use a category) } elseif ($this->settings[$i]['cat'] === 'config' && preg_match_all('/^\$a\-\>config\[\'' . $this->settings[$i]['key'] . '\'\]\s*=\s\'*(.*?)\';$/', $line, $matches, PREG_SET_ORDER)) { $line = '$a->config[\'' . $this->settings[$i]['key'] . '\'] = \'' . $this->settings[$i]['value'] . '\';' . PHP_EOL; $found[$i] = true; diff --git a/src/Worker/DBUpdate.php b/src/Worker/DBUpdate.php index 05bace146..001df25a8 100644 --- a/src/Worker/DBUpdate.php +++ b/src/Worker/DBUpdate.php @@ -6,12 +6,16 @@ namespace Friendica\Worker; use Friendica\BaseObject; +use Friendica\Core\Config; use Friendica\Core\Update; class DBUpdate extends BaseObject { public static function execute() { - Update::run(self::getApp()->getBasePath()); + // Just in case the last update wasn't failed + if (Config::get('system', 'update', Update::SUCCESS, true) != Update::FAILED) { + Update::run(self::getApp()->getBasePath()); + } } } diff --git a/tests/include/ApiTest.php b/tests/include/ApiTest.php index 5c2e1657a..1eb7ce78d 100644 --- a/tests/include/ApiTest.php +++ b/tests/include/ApiTest.php @@ -45,7 +45,7 @@ class ApiTest extends DatabaseTest $config = Factory\ConfigFactory::createConfig($configCache); Factory\ConfigFactory::createPConfig($configCache); $logger = Factory\LoggerFactory::create('test', $config, $profiler); - $this->app = new App($basePath, $config, $mode, $logger, $profiler, false); + $this->app = new App($config, $mode, $logger, $profiler, false); parent::setUp(); diff --git a/tests/src/Database/DBATest.php b/tests/src/Database/DBATest.php index bc8743da5..c94137721 100644 --- a/tests/src/Database/DBATest.php +++ b/tests/src/Database/DBATest.php @@ -22,7 +22,7 @@ class DBATest extends DatabaseTest $config = Factory\ConfigFactory::createConfig($configCache); Factory\ConfigFactory::createPConfig($configCache); $logger = Factory\LoggerFactory::create('test', $config, $profiler); - $this->app = new App($basePath, $config, $mode, $logger, $profiler, false); + $this->app = new App($config, $mode, $logger, $profiler, false); parent::setUp(); diff --git a/tests/src/Database/DBStructureTest.php b/tests/src/Database/DBStructureTest.php index 0c1da172f..152014c11 100644 --- a/tests/src/Database/DBStructureTest.php +++ b/tests/src/Database/DBStructureTest.php @@ -22,7 +22,7 @@ class DBStructureTest extends DatabaseTest $config = Factory\ConfigFactory::createConfig($configCache); Factory\ConfigFactory::createPConfig($configCache); $logger = Factory\LoggerFactory::create('test', $config, $profiler); - $this->app = new App($basePath, $config, $mode, $logger, $profiler, false); + $this->app = new App($config, $mode, $logger, $profiler, false); parent::setUp(); } diff --git a/tests/src/Util/Config/ConfigFileSaverTest.php b/tests/src/Util/Config/ConfigFileSaverTest.php index 5b472df49..04adf6014 100644 --- a/tests/src/Util/Config/ConfigFileSaverTest.php +++ b/tests/src/Util/Config/ConfigFileSaverTest.php @@ -89,14 +89,22 @@ class ConfigFileSaverTest extends MockedTest $configFileLoader->setupCache($configCache); $this->assertEquals('admin@test.it', $configCache->get('config', 'admin_email')); + $this->assertEquals('frio', $configCache->get('system', 'theme')); $this->assertNull($configCache->get('config', 'test_val')); $this->assertNull($configCache->get('system', 'test_val2')); - $configFileSaver->addConfigValue('system', 'theme', 'frio'); + // update values (system and config value) $configFileSaver->addConfigValue('config', 'admin_email', 'new@mail.it'); - $configFileSaver->addConfigValue('config', 'test_val', 'Testingwith@all.we can'); $configFileSaver->addConfigValue('system', 'theme', 'vier'); + + // insert values (system and config value) + $configFileSaver->addConfigValue('config', 'test_val', 'Testingwith@all.we can'); + $configFileSaver->addConfigValue('system', 'test_val2', 'TestIt First'); + + // overwrite value $configFileSaver->addConfigValue('system', 'test_val2', 'TestIt Now'); + + // save it $this->assertTrue($configFileSaver->saveToConfigFile()); $newConfigCache = new ConfigCache(); @@ -135,5 +143,47 @@ class ConfigFileSaverTest extends MockedTest vfsStream::newFile($fileName) ->at($root) ->setContent(file_get_contents($filePath . DIRECTORY_SEPARATOR . $fileName)); + + $configFileSaver = new ConfigFileSaver($this->root->url()); + + $configFileSaver->addConfigValue('system', 'test_val2', 'TestIt Now'); + + // wrong mod, so return false if nothing to write + $this->assertFalse($configFileSaver->saveToConfigFile()); + } + + /** + * Test the saveToConfigFile() method with nothing to do + * @dataProvider dataConfigFiles + */ + public function testNothingToDo($fileName, $filePath, $relativePath) + { + $this->delConfigFile('local.config.php'); + + if (empty($relativePath)) { + $root = $this->root; + $relativeFullName = $fileName; + } else { + $root = $this->root->getChild($relativePath); + $relativeFullName = $relativePath . DIRECTORY_SEPARATOR . $fileName; + } + + vfsStream::newFile($fileName) + ->at($root) + ->setContent(file_get_contents($filePath . DIRECTORY_SEPARATOR . $fileName)); + + $configFileSaver = new ConfigFileSaver($this->root->url()); + $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode); + $configCache = new ConfigCache(); + $configFileLoader->setupCache($configCache); + + // save nothing + $this->assertTrue($configFileSaver->saveToConfigFile()); + + $this->assertTrue($this->root->hasChild($relativeFullName)); + $this->assertFalse($this->root->hasChild($relativeFullName . '.old')); + $this->assertFalse($this->root->hasChild($relativeFullName . '.tmp')); + + $this->assertEquals(file_get_contents($filePath . DIRECTORY_SEPARATOR . $fileName), file_get_contents($this->root->getChild($relativeFullName)->url())); } } diff --git a/update.php b/update.php index 4ee85739d..8626ed299 100644 --- a/update.php +++ b/update.php @@ -1,5 +1,6 @@ getConfigCache(); - $configFileSaver = new ConfigFileSaver($app->getBasePath()); - - $updateConfigEntry = function($cat, $key) use ($configCache, $configFileSaver) { - // check if the config file differs from the whole configuration (= The db contains other values) - $fileConfig = $configCache->get($cat, $key); - if ($fileConfig === '!!') { - $fileConfig = null; - } - $savedConfig = Config::get($cat, $key, null, true); - if ($fileConfig !== $savedConfig) { - Logger::info('Difference in config found', ['cat' => $cat, 'key' => $key, 'file' => $fileConfig, 'saved' => $savedConfig]); - $configFileSaver->addConfigValue($cat, $key, $savedConfig); - } else { - Logger::info('No Difference in config found', ['cat' => $cat, 'key' => $key, 'value' => $fileConfig, 'saved' => $savedConfig]); - } - }; - - $configFileSaver->saveToConfigFile(); - - $updateConfigEntry('config', 'hostname'); - $updateConfigEntry('system', 'basepath'); - return Update::SUCCESS; + $app = BaseObject::getApp(); + if (Update::saveConfigToFile($app->getBasePath(), $app->getMode())) { + return Update::SUCCESS; + } else { + return Update::FAILED; + } } From 06175ede1ed057db459a4f61fe31c2b48cfd564b Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 24 Mar 2019 22:58:36 +0100 Subject: [PATCH 8/8] forgot admin setting --- mod/admin.php | 1 - 1 file changed, 1 deletion(-) diff --git a/mod/admin.php b/mod/admin.php index 848ed2521..7f8e12e0c 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -1395,7 +1395,6 @@ function admin_page_site_post(App $a) Config::set('system', 'temppath', $temppath); - Config::set('system', 'basepath' , $basepath); Config::set('system', 'proxy_disabled' , $proxy_disabled); Config::set('system', 'only_tag_search' , $only_tag_search);