From 59404d7909b21682ec0782778452a8a70e38b25e Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 18 May 2016 21:43:59 +0200 Subject: Introduce a configuration manager (not plugged yet) --- application/config/ConfigIO.php | 33 ++++ application/config/ConfigManager.php | 363 +++++++++++++++++++++++++++++++++++ application/config/ConfigPhp.php | 93 +++++++++ application/config/ConfigPlugin.php | 118 ++++++++++++ 4 files changed, 607 insertions(+) create mode 100644 application/config/ConfigIO.php create mode 100644 application/config/ConfigManager.php create mode 100644 application/config/ConfigPhp.php create mode 100644 application/config/ConfigPlugin.php (limited to 'application/config') diff --git a/application/config/ConfigIO.php b/application/config/ConfigIO.php new file mode 100644 index 00000000..2b68fe6a --- /dev/null +++ b/application/config/ConfigIO.php @@ -0,0 +1,33 @@ +initialize(); + } + + return self::$instance; + } + + /** + * Rebuild the loaded config array from config files. + */ + public function reload() + { + $this->initialize(); + } + + /** + * Initialize loaded conf in ConfigManager. + */ + protected function initialize() + { + /*if (! file_exists(self::$CONFIG_FILE .'.php')) { + $this->configIO = new ConfigJson(); + } else { + $this->configIO = new ConfigPhp(); + }*/ + $this->configIO = new ConfigPhp(); + $this->loadedConfig = $this->configIO->read(self::$CONFIG_FILE); + $this->setDefaultValues(); + } + + /** + * Get a setting. + * + * Supports nested settings with dot separated keys. + * Eg. 'config.stuff.option' will find $conf[config][stuff][option], + * or in JSON: + * { "config": { "stuff": {"option": "mysetting" } } } } + * + * @param string $setting Asked setting, keys separated with dots. + * @param string $default Default value if not found. + * + * @return mixed Found setting, or the default value. + */ + public function get($setting, $default = '') + { + $settings = explode('.', $setting); + $value = self::getConfig($settings, $this->loadedConfig); + if ($value === self::$NOT_FOUND) { + return $default; + } + return $value; + } + + /** + * Set a setting, and eventually write it. + * + * Supports nested settings with dot separated keys. + * + * @param string $setting Asked setting, keys separated with dots. + * @param string $value Value to set. + * @param bool $write Write the new setting in the config file, default false. + * @param bool $isLoggedIn User login state, default false. + */ + public function set($setting, $value, $write = false, $isLoggedIn = false) + { + $settings = explode('.', $setting); + self::setConfig($settings, $value, $this->loadedConfig); + if ($write) { + $this->write($isLoggedIn); + } + } + + /** + * Check if a settings exists. + * + * Supports nested settings with dot separated keys. + * + * @param string $setting Asked setting, keys separated with dots. + * + * @return bool true if the setting exists, false otherwise. + */ + public function exists($setting) + { + $settings = explode('.', $setting); + $value = self::getConfig($settings, $this->loadedConfig); + if ($value === self::$NOT_FOUND) { + return false; + } + return true; + } + + /** + * Call the config writer. + * + * @param bool $isLoggedIn User login state. + * + * @throws MissingFieldConfigException: a mandatory field has not been provided in $conf. + * @throws UnauthorizedConfigException: user is not authorize to change configuration. + * @throws IOException: an error occurred while writing the new config file. + */ + public function write($isLoggedIn) + { + // These fields are required in configuration. + $mandatoryFields = array( + 'login', 'hash', 'salt', 'timezone', 'title', 'titleLink', + 'redirector', 'disablesessionprotection', 'privateLinkByDefault' + ); + + // Only logged in user can alter config. + if (is_file(self::$CONFIG_FILE) && !$isLoggedIn) { + throw new UnauthorizedConfigException(); + } + + // Check that all mandatory fields are provided in $conf. + foreach ($mandatoryFields as $field) { + if (! $this->exists($field)) { + throw new MissingFieldConfigException($field); + } + } + + $this->configIO->write(self::$CONFIG_FILE, $this->loadedConfig); + } + + /** + * Get the configuration file path. + * + * @return string Config file path. + */ + public function getConfigFile() + { + return self::$CONFIG_FILE . $this->configIO->getExtension(); + } + + /** + * Recursive function which find asked setting in the loaded config. + * + * @param array $settings Ordered array which contains keys to find. + * @param array $conf Loaded settings, then sub-array. + * + * @return mixed Found setting or NOT_FOUND flag. + */ + protected static function getConfig($settings, $conf) + { + if (!is_array($settings) || count($settings) == 0) { + return self::$NOT_FOUND; + } + + $setting = array_shift($settings); + if (!isset($conf[$setting])) { + return self::$NOT_FOUND; + } + + if (count($settings) > 0) { + return self::getConfig($settings, $conf[$setting]); + } + return $conf[$setting]; + } + + /** + * Recursive function which find asked setting in the loaded config. + * + * @param array $settings Ordered array which contains keys to find. + * @param mixed $value + * @param array $conf Loaded settings, then sub-array. + * + * @return mixed Found setting or NOT_FOUND flag. + */ + protected static function setConfig($settings, $value, &$conf) + { + if (!is_array($settings) || count($settings) == 0) { + return self::$NOT_FOUND; + } + + $setting = array_shift($settings); + if (count($settings) > 0) { + return self::setConfig($settings, $value, $conf[$setting]); + } + $conf[$setting] = $value; + } + + /** + * Set a bunch of default values allowing Shaarli to start without a config file. + */ + protected function setDefaultValues() + { + // Data subdirectory + $this->setEmpty('config.DATADIR', 'data'); + + // Main configuration file + $this->setEmpty('config.CONFIG_FILE', 'data/config.php'); + + // Link datastore + $this->setEmpty('config.DATASTORE', 'data/datastore.php'); + + // Banned IPs + $this->setEmpty('config.IPBANS_FILENAME', 'data/ipbans.php'); + + // Processed updates file. + $this->setEmpty('config.UPDATES_FILE', 'data/updates.txt'); + + // Access log + $this->setEmpty('config.LOG_FILE', 'data/log.txt'); + + // For updates check of Shaarli + $this->setEmpty('config.UPDATECHECK_FILENAME', 'data/lastupdatecheck.txt'); + + // Set ENABLE_UPDATECHECK to disabled by default. + $this->setEmpty('config.ENABLE_UPDATECHECK', false); + + // RainTPL cache directory (keep the trailing slash!) + $this->setEmpty('config.RAINTPL_TMP', 'tmp/'); + // Raintpl template directory (keep the trailing slash!) + $this->setEmpty('config.RAINTPL_TPL', 'tpl/'); + + // Thumbnail cache directory + $this->setEmpty('config.CACHEDIR', 'cache'); + + // Atom & RSS feed cache directory + $this->setEmpty('config.PAGECACHE', 'pagecache'); + + // Ban IP after this many failures + $this->setEmpty('config.BAN_AFTER', 4); + // Ban duration for IP address after login failures (in seconds) + $this->setEmpty('config.BAN_DURATION', 1800); + + // Feed options + // Enable RSS permalinks by default. + // This corresponds to the default behavior of shaarli before this was added as an option. + $this->setEmpty('config.ENABLE_RSS_PERMALINKS', true); + // If true, an extra "ATOM feed" button will be displayed in the toolbar + $this->setEmpty('config.SHOW_ATOM', false); + + // Link display options + $this->setEmpty('config.HIDE_PUBLIC_LINKS', false); + $this->setEmpty('config.HIDE_TIMESTAMPS', false); + $this->setEmpty('config.LINKS_PER_PAGE', 20); + + // Open Shaarli (true): anyone can add/edit/delete links without having to login + $this->setEmpty('config.OPEN_SHAARLI', false); + + // Thumbnails + // Display thumbnails in links + $this->setEmpty('config.ENABLE_THUMBNAILS', true); + // Store thumbnails in a local cache + $this->setEmpty('config.ENABLE_LOCALCACHE', true); + + // Update check frequency for Shaarli. 86400 seconds=24 hours + $this->setEmpty('config.UPDATECHECK_BRANCH', 'stable'); + $this->setEmpty('config.UPDATECHECK_INTERVAL', 86400); + + $this->setEmpty('redirector', ''); + $this->setEmpty('config.REDIRECTOR_URLENCODE', true); + + // Enabled plugins. + $this->setEmpty('config.ENABLED_PLUGINS', array('qrcode')); + + // Initialize plugin parameters array. + $this->setEmpty('plugins', array()); + } + + /** + * Set only if the setting does not exists. + * + * @param string $key Setting key. + * @param mixed $value Setting value. + */ + protected function setEmpty($key, $value) + { + if (! $this->exists($key)) { + $this->set($key, $value); + } + } +} + +/** + * Exception used if a mandatory field is missing in given configuration. + */ +class MissingFieldConfigException extends Exception +{ + public $field; + + /** + * Construct exception. + * + * @param string $field field name missing. + */ + public function __construct($field) + { + $this->field = $field; + $this->message = 'Configuration value is required for '. $this->field; + } +} + +/** + * Exception used if an unauthorized attempt to edit configuration has been made. + */ +class UnauthorizedConfigException extends Exception +{ + /** + * Construct exception. + */ + public function __construct() + { + $this->message = 'You are not authorized to alter config.'; + } +} diff --git a/application/config/ConfigPhp.php b/application/config/ConfigPhp.php new file mode 100644 index 00000000..311aeb81 --- /dev/null +++ b/application/config/ConfigPhp.php @@ -0,0 +1,93 @@ +getExtension(); + if (! file_exists($filepath) || ! is_readable($filepath)) { + return array(); + } + + include $filepath; + + $out = array(); + foreach (self::$ROOT_KEYS as $key) { + $out[$key] = $GLOBALS[$key]; + } + $out['config'] = $GLOBALS['config']; + $out['plugins'] = !empty($GLOBALS['plugins']) ? $GLOBALS['plugins'] : array(); + return $out; + } + + /** + * @inheritdoc + */ + function write($filepath, $conf) + { + $filepath .= $this->getExtension(); + + $configStr = ' $value) { + $configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($conf['config'][$key], true).';'. PHP_EOL; + } + + if (isset($conf['plugins'])) { + foreach ($conf['plugins'] as $key => $value) { + $configStr .= '$GLOBALS[\'plugins\'][\''. $key .'\'] = '.var_export($conf['plugins'][$key], true).';'. PHP_EOL; + } + } + + // FIXME! + //$configStr .= 'date_default_timezone_set('.var_export($conf['timezone'], true).');'. PHP_EOL; + + if (!file_put_contents($filepath, $configStr) + || strcmp(file_get_contents($filepath), $configStr) != 0 + ) { + throw new IOException( + $filepath, + 'Shaarli could not create the config file. + Please make sure Shaarli has the right to write in the folder is it installed in.' + ); + } + } + + /** + * @inheritdoc + */ + function getExtension() + { + return '.php'; + } +} diff --git a/application/config/ConfigPlugin.php b/application/config/ConfigPlugin.php new file mode 100644 index 00000000..8af89d04 --- /dev/null +++ b/application/config/ConfigPlugin.php @@ -0,0 +1,118 @@ + $data) { + if (startsWith($key, 'order')) { + continue; + } + + // If there is no order, it means a disabled plugin has been enabled. + if (isset($formData['order_' . $key])) { + $plugins[(int) $formData['order_' . $key]] = $key; + } + else { + $newEnabledPlugins[] = $key; + } + } + + // New enabled plugins will be added at the end of order. + $plugins = array_merge($plugins, $newEnabledPlugins); + + // Sort plugins by order. + if (!ksort($plugins)) { + throw new PluginConfigOrderException(); + } + + $finalPlugins = array(); + // Make plugins order continuous. + foreach ($plugins as $plugin) { + $finalPlugins[] = $plugin; + } + + return $finalPlugins; +} + +/** + * Validate plugin array submitted. + * Will fail if there is duplicate orders value. + * + * @param array $formData Data from submitted form. + * + * @return bool true if ok, false otherwise. + */ +function validate_plugin_order($formData) +{ + $orders = array(); + foreach ($formData as $key => $value) { + // No duplicate order allowed. + if (in_array($value, $orders)) { + return false; + } + + if (startsWith($key, 'order')) { + $orders[] = $value; + } + } + + return true; +} + +/** + * Affect plugin parameters values into plugins array. + * + * @param mixed $plugins Plugins array ($plugins[]['parameters']['param_name'] = . + * @param mixed $conf Plugins configuration. + * + * @return mixed Updated $plugins array. + */ +function load_plugin_parameter_values($plugins, $conf) +{ + $out = $plugins; + foreach ($plugins as $name => $plugin) { + if (empty($plugin['parameters'])) { + continue; + } + + foreach ($plugin['parameters'] as $key => $param) { + if (!empty($conf[$key])) { + $out[$name]['parameters'][$key] = $conf[$key]; + } + } + } + + return $out; +} + +/** + * Exception used if an error occur while saving plugin configuration. + */ +class PluginConfigOrderException extends Exception +{ + /** + * Construct exception. + */ + public function __construct() + { + $this->message = 'An error occurred while trying to save plugins loading order.'; + } +} -- cgit v1.2.3 From 684e662a58b02bde225e44d3677987b6fc3adf0b Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 18 May 2016 21:48:24 +0200 Subject: Replace $GLOBALS configuration with the configuration manager in the whole code base --- application/config/ConfigIO.php | 2 ++ application/config/ConfigManager.php | 49 +++++++++++++++++++++++++++++++++--- application/config/ConfigPhp.php | 3 --- application/config/ConfigPlugin.php | 4 ++- 4 files changed, 50 insertions(+), 8 deletions(-) (limited to 'application/config') diff --git a/application/config/ConfigIO.php b/application/config/ConfigIO.php index 2b68fe6a..4b1c9901 100644 --- a/application/config/ConfigIO.php +++ b/application/config/ConfigIO.php @@ -21,6 +21,8 @@ interface ConfigIO * * @param string $filepath Config file absolute path. * @param array $conf All configuration in an array. + * + * @return bool True if the configuration has been successfully written, false otherwise. */ function write($filepath, $conf); diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index dfe9eeb9..212aac05 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php @@ -62,16 +62,25 @@ class ConfigManager return self::$instance; } + /** + * Reset the ConfigManager instance. + */ + public static function reset() + { + self::$instance = null; + return self::getInstance(); + } + /** * Rebuild the loaded config array from config files. */ public function reload() { - $this->initialize(); + $this->load(); } /** - * Initialize loaded conf in ConfigManager. + * Initialize the ConfigIO and loaded the conf. */ protected function initialize() { @@ -81,7 +90,15 @@ class ConfigManager $this->configIO = new ConfigPhp(); }*/ $this->configIO = new ConfigPhp(); - $this->loadedConfig = $this->configIO->read(self::$CONFIG_FILE); + $this->load(); + } + + /** + * Load configuration in the ConfigurationManager. + */ + protected function load() + { + $this->loadedConfig = $this->configIO->read($this->getConfigFile()); $this->setDefaultValues(); } @@ -117,9 +134,15 @@ class ConfigManager * @param string $value Value to set. * @param bool $write Write the new setting in the config file, default false. * @param bool $isLoggedIn User login state, default false. + * + * @throws Exception Invalid */ public function set($setting, $value, $write = false, $isLoggedIn = false) { + if (empty($setting) || ! is_string($setting)) { + throw new Exception('Invalid setting key parameter. String expected, got: '. gettype($setting)); + } + $settings = explode('.', $setting); self::setConfig($settings, $value, $this->loadedConfig); if ($write) { @@ -151,6 +174,8 @@ class ConfigManager * * @param bool $isLoggedIn User login state. * + * @return bool True if the configuration has been successfully written, false otherwise. + * * @throws MissingFieldConfigException: a mandatory field has not been provided in $conf. * @throws UnauthorizedConfigException: user is not authorize to change configuration. * @throws IOException: an error occurred while writing the new config file. @@ -175,7 +200,7 @@ class ConfigManager } } - $this->configIO->write(self::$CONFIG_FILE, $this->loadedConfig); + return $this->configIO->write($this->getConfigFile(), $this->loadedConfig); } /** @@ -327,6 +352,22 @@ class ConfigManager $this->set($key, $value); } } + + /** + * @return ConfigIO + */ + public function getConfigIO() + { + return $this->configIO; + } + + /** + * @param ConfigIO $configIO + */ + public function setConfigIO($configIO) + { + $this->configIO = $configIO; + } } /** diff --git a/application/config/ConfigPhp.php b/application/config/ConfigPhp.php index 311aeb81..19fecf2b 100644 --- a/application/config/ConfigPhp.php +++ b/application/config/ConfigPhp.php @@ -28,7 +28,6 @@ class ConfigPhp implements ConfigIO */ function read($filepath) { - $filepath .= $this->getExtension(); if (! file_exists($filepath) || ! is_readable($filepath)) { return array(); } @@ -49,8 +48,6 @@ class ConfigPhp implements ConfigIO */ function write($filepath, $conf) { - $filepath .= $this->getExtension(); - $configStr = ' Date: Sun, 29 May 2016 12:32:14 +0200 Subject: Adds ConfigJson which handle the configuration in JSON format. Also use the Updater to make the transition --- application/config/ConfigIO.php | 2 -- application/config/ConfigJson.php | 66 ++++++++++++++++++++++++++++++++++++ application/config/ConfigManager.php | 7 ++-- 3 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 application/config/ConfigJson.php (limited to 'application/config') diff --git a/application/config/ConfigIO.php b/application/config/ConfigIO.php index 4b1c9901..2b68fe6a 100644 --- a/application/config/ConfigIO.php +++ b/application/config/ConfigIO.php @@ -21,8 +21,6 @@ interface ConfigIO * * @param string $filepath Config file absolute path. * @param array $conf All configuration in an array. - * - * @return bool True if the configuration has been successfully written, false otherwise. */ function write($filepath, $conf); diff --git a/application/config/ConfigJson.php b/application/config/ConfigJson.php new file mode 100644 index 00000000..cbafbf6d --- /dev/null +++ b/application/config/ConfigJson.php @@ -0,0 +1,66 @@ +configIO = new ConfigJson(); } else { $this->configIO = new ConfigPhp(); - }*/ - $this->configIO = new ConfigPhp(); + } $this->load(); } -- cgit v1.2.3 From d93d51b213bd3bda9bfa67d23c31dcce5a8b7fc0 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sun, 29 May 2016 14:26:23 +0200 Subject: Set the default timezone in index.php --- application/config/ConfigPhp.php | 3 --- 1 file changed, 3 deletions(-) (limited to 'application/config') diff --git a/application/config/ConfigPhp.php b/application/config/ConfigPhp.php index 19fecf2b..f99073af 100644 --- a/application/config/ConfigPhp.php +++ b/application/config/ConfigPhp.php @@ -66,9 +66,6 @@ class ConfigPhp implements ConfigIO } } - // FIXME! - //$configStr .= 'date_default_timezone_set('.var_export($conf['timezone'], true).');'. PHP_EOL; - if (!file_put_contents($filepath, $configStr) || strcmp(file_get_contents($filepath), $configStr) != 0 ) { -- cgit v1.2.3 From da10377b3c263d96a46cf9101c202554343d2cd0 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sun, 29 May 2016 16:10:32 +0200 Subject: Rename configuration keys and fix GLOBALS in templates --- application/config/ConfigJson.php | 33 +++++++------- application/config/ConfigManager.php | 83 +++++++++++++++++++++++------------- application/config/ConfigPhp.php | 45 +++++++++++++++++++ 3 files changed, 115 insertions(+), 46 deletions(-) (limited to 'application/config') diff --git a/application/config/ConfigJson.php b/application/config/ConfigJson.php index cbafbf6d..94693c86 100644 --- a/application/config/ConfigJson.php +++ b/application/config/ConfigJson.php @@ -7,30 +7,16 @@ */ class ConfigJson implements ConfigIO { - /** - * The JSON data is wrapped in a PHP file for security purpose. - * This way, even if the file is accessible, credentials and configuration won't be exposed. - * - * @var string PHP start tag and comment tag. - */ - public static $PHP_HEADER; - - public function __construct() - { - // The field can't be initialized directly with concatenation before PHP 5.6. - self::$PHP_HEADER = 'configIO instanceof ConfigPhp && isset(ConfigPhp::$LEGACY_KEYS_MAPPING[$setting])) { + $setting = ConfigPhp::$LEGACY_KEYS_MAPPING[$setting]; + } + $settings = explode('.', $setting); $value = self::getConfig($settings, $this->loadedConfig); if ($value === self::$NOT_FOUND) { @@ -142,6 +147,11 @@ class ConfigManager throw new Exception('Invalid setting key parameter. String expected, got: '. gettype($setting)); } + // During the ConfigIO transition, map legacy settings to the new ones. + if ($this->configIO instanceof ConfigPhp && isset(ConfigPhp::$LEGACY_KEYS_MAPPING[$setting])) { + $setting = ConfigPhp::$LEGACY_KEYS_MAPPING[$setting]; + } + $settings = explode('.', $setting); self::setConfig($settings, $value, $this->loadedConfig); if ($write) { @@ -160,6 +170,11 @@ class ConfigManager */ public function exists($setting) { + // During the ConfigIO transition, map legacy settings to the new ones. + if ($this->configIO instanceof ConfigPhp && isset(ConfigPhp::$LEGACY_KEYS_MAPPING[$setting])) { + $setting = ConfigPhp::$LEGACY_KEYS_MAPPING[$setting]; + } + $settings = explode('.', $setting); $value = self::getConfig($settings, $this->loadedConfig); if ($value === self::$NOT_FOUND) { @@ -183,8 +198,15 @@ class ConfigManager { // These fields are required in configuration. $mandatoryFields = array( - 'login', 'hash', 'salt', 'timezone', 'title', 'titleLink', - 'redirector', 'disablesessionprotection', 'privateLinkByDefault' + 'credentials.login', + 'credentials.hash', + 'credentials.salt', + 'security.session_protection_disabled', + 'general.timezone', + 'general.title', + 'general.header_link', + 'general.default_private_links', + 'extras.redirector', ); // Only logged in user can alter config. @@ -265,75 +287,78 @@ class ConfigManager protected function setDefaultValues() { // Data subdirectory - $this->setEmpty('config.DATADIR', 'data'); + $this->setEmpty('path.data_dir', 'data'); // Main configuration file - $this->setEmpty('config.CONFIG_FILE', 'data/config.php'); + $this->setEmpty('path.config', 'data/config.php'); // Link datastore - $this->setEmpty('config.DATASTORE', 'data/datastore.php'); + $this->setEmpty('path.datastore', 'data/datastore.php'); // Banned IPs - $this->setEmpty('config.IPBANS_FILENAME', 'data/ipbans.php'); + $this->setEmpty('path.ban_file', 'data/ipbans.php'); // Processed updates file. - $this->setEmpty('config.UPDATES_FILE', 'data/updates.txt'); + $this->setEmpty('path.updates', 'data/updates.txt'); // Access log - $this->setEmpty('config.LOG_FILE', 'data/log.txt'); + $this->setEmpty('path.log', 'data/log.txt'); // For updates check of Shaarli - $this->setEmpty('config.UPDATECHECK_FILENAME', 'data/lastupdatecheck.txt'); + $this->setEmpty('path.update_check', 'data/lastupdatecheck.txt'); // Set ENABLE_UPDATECHECK to disabled by default. - $this->setEmpty('config.ENABLE_UPDATECHECK', false); + $this->setEmpty('general.check_updates', false); // RainTPL cache directory (keep the trailing slash!) - $this->setEmpty('config.RAINTPL_TMP', 'tmp/'); + $this->setEmpty('path.raintpl_tmp', 'tmp/'); // Raintpl template directory (keep the trailing slash!) - $this->setEmpty('config.RAINTPL_TPL', 'tpl/'); + $this->setEmpty('path.raintpl_tpl', 'tpl/'); // Thumbnail cache directory - $this->setEmpty('config.CACHEDIR', 'cache'); + $this->setEmpty('path.thumbnails_cache', 'cache'); // Atom & RSS feed cache directory - $this->setEmpty('config.PAGECACHE', 'pagecache'); + $this->setEmpty('path.page_cache', 'pagecache'); // Ban IP after this many failures - $this->setEmpty('config.BAN_AFTER', 4); + $this->setEmpty('security.ban_after', 4); // Ban duration for IP address after login failures (in seconds) - $this->setEmpty('config.BAN_DURATION', 1800); + $this->setEmpty('security.ban_after', 1800); // Feed options // Enable RSS permalinks by default. // This corresponds to the default behavior of shaarli before this was added as an option. - $this->setEmpty('config.ENABLE_RSS_PERMALINKS', true); + $this->setEmpty('general.rss_permalinks', true); // If true, an extra "ATOM feed" button will be displayed in the toolbar - $this->setEmpty('config.SHOW_ATOM', false); + $this->setEmpty('extras.show_atom', false); // Link display options - $this->setEmpty('config.HIDE_PUBLIC_LINKS', false); - $this->setEmpty('config.HIDE_TIMESTAMPS', false); - $this->setEmpty('config.LINKS_PER_PAGE', 20); + $this->setEmpty('extras.hide_public_links', false); + $this->setEmpty('extras.hide_timestamps', false); + $this->setEmpty('general.links_per_page', 20); + + // Private checkbox is checked by default + $this->setEmpty('general.default_private_links', false); // Open Shaarli (true): anyone can add/edit/delete links without having to login - $this->setEmpty('config.OPEN_SHAARLI', false); + $this->setEmpty('extras.open_shaarli', false); // Thumbnails // Display thumbnails in links - $this->setEmpty('config.ENABLE_THUMBNAILS', true); + $this->setEmpty('general.enable_thumbnails', true); // Store thumbnails in a local cache - $this->setEmpty('config.ENABLE_LOCALCACHE', true); + $this->setEmpty('general.enable_localcache', true); // Update check frequency for Shaarli. 86400 seconds=24 hours - $this->setEmpty('config.UPDATECHECK_BRANCH', 'stable'); - $this->setEmpty('config.UPDATECHECK_INTERVAL', 86400); + $this->setEmpty('general.check_updates_branch', 'stable'); + $this->setEmpty('general.check_updates_interval', 86400); - $this->setEmpty('redirector', ''); - $this->setEmpty('config.REDIRECTOR_URLENCODE', true); + $this->setEmpty('extras.redirector', ''); + $this->setEmpty('extras.redirector_encode_url', true); // Enabled plugins. - $this->setEmpty('config.ENABLED_PLUGINS', array('qrcode')); + $this->setEmpty('general.enabled_plugins', array('qrcode')); // Initialize plugin parameters array. $this->setEmpty('plugins', array()); diff --git a/application/config/ConfigPhp.php b/application/config/ConfigPhp.php index f99073af..b122f4f1 100644 --- a/application/config/ConfigPhp.php +++ b/application/config/ConfigPhp.php @@ -23,6 +23,51 @@ class ConfigPhp implements ConfigIO 'privateLinkByDefault', ); + /** + * Map legacy config keys with the new ones. + * If ConfigPhp is used, getting will actually look for . + * The Updater will use this array to transform keys when switching to JSON. + * + * @var array current key => legacy key. + */ + public static $LEGACY_KEYS_MAPPING = array( + 'credentials.login' => 'login', + 'credentials.hash' => 'hash', + 'credentials.salt' => 'salt', + 'path.data_dir' => 'config.DATADIR', + 'path.config' => 'config.CONFIG_FILE', + 'path.datastore' => 'config.DATASTORE', + 'path.updates' => 'config.UPDATES_FILE', + 'path.log' => 'config.LOG_FILE', + 'path.update_check' => 'config.UPDATECHECK_FILENAME', + 'path.raintpl_tpl' => 'config.RAINTPL_TPL', + 'path.raintpl_tmp' => 'config.RAINTPL_TMP', + 'path.thumbnails_cache' => 'config.CACHEDIR', + 'path.page_cache' => 'config.PAGECACHE', + 'path.ban_file' => 'config.IPBANS_FILENAME', + 'security.session_protection_disabled' => 'disablesessionprotection', + 'security.ban_after' => 'config.BAN_AFTER', + 'security.ban_duration' => 'config.BAN_DURATION', + 'general.title' => 'title', + 'general.timezone' => 'timezone', + 'general.header_link' => 'titleLink', + 'general.check_updates' => 'config.ENABLE_UPDATECHECK', + 'general.check_updates_branch' => 'config.UPDATECHECK_BRANCH', + 'general.check_updates_interval' => 'config.UPDATECHECK_INTERVAL', + 'general.default_private_links' => 'privateLinkByDefault', + 'general.rss_permalinks' => 'config.ENABLE_RSS_PERMALINKS', + 'general.links_per_page' => 'config.LINKS_PER_PAGE', + 'general.enable_thumbnails' => 'config.ENABLE_THUMBNAILS', + 'general.enable_localcache' => 'config.ENABLE_LOCALCACHE', + 'general.enabled_plugins' => 'config.ENABLED_PLUGINS', + 'extras.redirector' => 'redirector', + 'extras.redirector_encode_url' => 'config.REDIRECTOR_URLENCODE', + 'extras.show_atom' => 'config.SHOW_ATOM', + 'extras.hide_public_links' => 'config.HIDE_PUBLIC_LINKS', + 'extras.hide_timestamps' => 'config.HIDE_TIMESTAMPS', + 'extras.open_shaarli' => 'config.OPEN_SHAARLI', + ); + /** * @inheritdoc */ -- cgit v1.2.3 From 7f179985b497053c59338667fe49c390aa626ab7 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Mon, 30 May 2016 20:15:36 +0200 Subject: Remove remaining settings initialization in index.php Except for those which require external data (timezone and $_SERVER). --- application/config/ConfigManager.php | 64 ++++++++---------------------------- 1 file changed, 13 insertions(+), 51 deletions(-) (limited to 'application/config') diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index a663a071..c0482cf3 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php @@ -9,6 +9,9 @@ require_once 'ConfigJson.php'; * Class ConfigManager * * Singleton, manages all Shaarli's settings. + * See the documentation for more information on settings: + * - doc/Shaarli-configuration.html + * - https://github.com/shaarli/Shaarli/wiki/Shaarli-configuration */ class ConfigManager { @@ -286,81 +289,40 @@ class ConfigManager */ protected function setDefaultValues() { - // Data subdirectory $this->setEmpty('path.data_dir', 'data'); - - // Main configuration file $this->setEmpty('path.config', 'data/config.php'); - - // Link datastore $this->setEmpty('path.datastore', 'data/datastore.php'); - - // Banned IPs $this->setEmpty('path.ban_file', 'data/ipbans.php'); - - // Processed updates file. $this->setEmpty('path.updates', 'data/updates.txt'); - - // Access log $this->setEmpty('path.log', 'data/log.txt'); - - // For updates check of Shaarli $this->setEmpty('path.update_check', 'data/lastupdatecheck.txt'); - - // Set ENABLE_UPDATECHECK to disabled by default. - $this->setEmpty('general.check_updates', false); - - // RainTPL cache directory (keep the trailing slash!) - $this->setEmpty('path.raintpl_tmp', 'tmp/'); - // Raintpl template directory (keep the trailing slash!) $this->setEmpty('path.raintpl_tpl', 'tpl/'); - - // Thumbnail cache directory + $this->setEmpty('path.raintpl_tmp', 'tmp/'); $this->setEmpty('path.thumbnails_cache', 'cache'); - - // Atom & RSS feed cache directory $this->setEmpty('path.page_cache', 'pagecache'); - // Ban IP after this many failures $this->setEmpty('security.ban_after', 4); - // Ban duration for IP address after login failures (in seconds) $this->setEmpty('security.ban_after', 1800); + $this->setEmpty('security.session_protection_disabled', false); - // Feed options - // Enable RSS permalinks by default. - // This corresponds to the default behavior of shaarli before this was added as an option. + $this->setEmpty('general.check_updates', false); $this->setEmpty('general.rss_permalinks', true); - // If true, an extra "ATOM feed" button will be displayed in the toolbar - $this->setEmpty('extras.show_atom', false); - - // Link display options - $this->setEmpty('extras.hide_public_links', false); - $this->setEmpty('extras.hide_timestamps', false); $this->setEmpty('general.links_per_page', 20); - - // Private checkbox is checked by default $this->setEmpty('general.default_private_links', false); - - // Open Shaarli (true): anyone can add/edit/delete links without having to login - $this->setEmpty('extras.open_shaarli', false); - - // Thumbnails - // Display thumbnails in links $this->setEmpty('general.enable_thumbnails', true); - // Store thumbnails in a local cache $this->setEmpty('general.enable_localcache', true); - - // Update check frequency for Shaarli. 86400 seconds=24 hours $this->setEmpty('general.check_updates_branch', 'stable'); $this->setEmpty('general.check_updates_interval', 86400); + $this->setEmpty('general.header_link', '?'); + $this->setEmpty('general.enabled_plugins', array('qrcode')); + $this->setEmpty('extras.show_atom', false); + $this->setEmpty('extras.hide_public_links', false); + $this->setEmpty('extras.hide_timestamps', false); + $this->setEmpty('extras.open_shaarli', false); $this->setEmpty('extras.redirector', ''); $this->setEmpty('extras.redirector_encode_url', true); - // Enabled plugins. - $this->setEmpty('general.enabled_plugins', array('qrcode')); - - // Initialize plugin parameters array. $this->setEmpty('plugins', array()); } @@ -370,7 +332,7 @@ class ConfigManager * @param string $key Setting key. * @param mixed $value Setting value. */ - protected function setEmpty($key, $value) + public function setEmpty($key, $value) { if (! $this->exists($key)) { $this->set($key, $value); -- cgit v1.2.3 From 278d9ee2836df7d805845077f26f8cecd16f0f4f Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 9 Jun 2016 20:04:02 +0200 Subject: ConfigManager no longer uses singleton pattern --- application/config/ConfigManager.php | 84 ++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 43 deletions(-) (limited to 'application/config') diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index c0482cf3..5aafc89d 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php @@ -2,13 +2,13 @@ // FIXME! Namespaces... require_once 'ConfigIO.php'; -require_once 'ConfigPhp.php'; require_once 'ConfigJson.php'; +require_once 'ConfigPhp.php'; /** * Class ConfigManager * - * Singleton, manages all Shaarli's settings. + * Manages all Shaarli's settings. * See the documentation for more information on settings: * - doc/Shaarli-configuration.html * - https://github.com/shaarli/Shaarli/wiki/Shaarli-configuration @@ -16,19 +16,14 @@ require_once 'ConfigJson.php'; class ConfigManager { /** - * @var ConfigManager instance. + * @var string Flag telling a setting is not found. */ - protected static $instance = null; + protected static $NOT_FOUND = 'NOT_FOUND'; /** * @var string Config folder. */ - public static $CONFIG_FILE = 'data/config'; - - /** - * @var string Flag telling a setting is not found. - */ - protected static $NOT_FOUND = 'NOT_FOUND'; + protected $configFile; /** * @var array Loaded config array. @@ -41,37 +36,20 @@ class ConfigManager protected $configIO; /** - * Private constructor: new instances not allowed. + * Constructor. */ - private function __construct() {} - - /** - * Cloning isn't allowed either. - */ - private function __clone() {} - - /** - * Return existing instance of PluginManager, or create it. - * - * @return ConfigManager instance. - */ - public static function getInstance() + public function __construct($configFile = 'data/config') { - if (!(self::$instance instanceof self)) { - self::$instance = new self(); - self::$instance->initialize(); - } - - return self::$instance; + $this->configFile = $configFile; + $this->initialize(); } /** * Reset the ConfigManager instance. */ - public static function reset() + public function reset() { - self::$instance = null; - return self::getInstance(); + $this->initialize(); } /** @@ -87,10 +65,10 @@ class ConfigManager */ protected function initialize() { - if (! file_exists(self::$CONFIG_FILE .'.php')) { - $this->configIO = new ConfigJson(); - } else { + if (file_exists($this->configFile . '.php')) { $this->configIO = new ConfigPhp(); + } else { + $this->configIO = new ConfigJson(); } $this->load(); } @@ -100,7 +78,7 @@ class ConfigManager */ protected function load() { - $this->loadedConfig = $this->configIO->read($this->getConfigFile()); + $this->loadedConfig = $this->configIO->read($this->getConfigFileExt()); $this->setDefaultValues(); } @@ -213,7 +191,7 @@ class ConfigManager ); // Only logged in user can alter config. - if (is_file(self::$CONFIG_FILE) && !$isLoggedIn) { + if (is_file($this->getConfigFileExt()) && !$isLoggedIn) { throw new UnauthorizedConfigException(); } @@ -224,17 +202,37 @@ class ConfigManager } } - return $this->configIO->write($this->getConfigFile(), $this->loadedConfig); + return $this->configIO->write($this->getConfigFileExt(), $this->loadedConfig); } /** - * Get the configuration file path. + * Set the config file path (without extension). * - * @return string Config file path. + * @param string $configFile File path. + */ + public function setConfigFile($configFile) + { + $this->configFile = $configFile; + } + + /** + * Return the configuration file path (without extension). + * + * @return string Config path. */ public function getConfigFile() { - return self::$CONFIG_FILE . $this->configIO->getExtension(); + return $this->configFile; + } + + /** + * Get the configuration file path with its extension. + * + * @return string Config file path. + */ + public function getConfigFileExt() + { + return $this->configFile . $this->configIO->getExtension(); } /** @@ -302,7 +300,7 @@ class ConfigManager $this->setEmpty('path.page_cache', 'pagecache'); $this->setEmpty('security.ban_after', 4); - $this->setEmpty('security.ban_after', 1800); + $this->setEmpty('security.ban_duration', 1800); $this->setEmpty('security.session_protection_disabled', false); $this->setEmpty('general.check_updates', false); -- cgit v1.2.3 From 894a3c4bf38d8dcadb6941049b9167e5101805bd Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 11 Jun 2016 09:08:02 +0200 Subject: Rename configuration key for better sections --- application/config/ConfigManager.php | 58 +++++++++++++++++++----------------- application/config/ConfigPhp.php | 48 ++++++++++++++--------------- 2 files changed, 55 insertions(+), 51 deletions(-) (limited to 'application/config') diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index 5aafc89d..ff41772a 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php @@ -186,8 +186,8 @@ class ConfigManager 'general.timezone', 'general.title', 'general.header_link', - 'general.default_private_links', - 'extras.redirector', + 'privacy.default_private_links', + 'redirector.url', ); // Only logged in user can alter config. @@ -287,39 +287,43 @@ class ConfigManager */ protected function setDefaultValues() { - $this->setEmpty('path.data_dir', 'data'); - $this->setEmpty('path.config', 'data/config.php'); - $this->setEmpty('path.datastore', 'data/datastore.php'); - $this->setEmpty('path.ban_file', 'data/ipbans.php'); - $this->setEmpty('path.updates', 'data/updates.txt'); - $this->setEmpty('path.log', 'data/log.txt'); - $this->setEmpty('path.update_check', 'data/lastupdatecheck.txt'); - $this->setEmpty('path.raintpl_tpl', 'tpl/'); - $this->setEmpty('path.raintpl_tmp', 'tmp/'); - $this->setEmpty('path.thumbnails_cache', 'cache'); - $this->setEmpty('path.page_cache', 'pagecache'); + $this->setEmpty('resource.data_dir', 'data'); + $this->setEmpty('resource.config', 'data/config.php'); + $this->setEmpty('resource.datastore', 'data/datastore.php'); + $this->setEmpty('resource.ban_file', 'data/ipbans.php'); + $this->setEmpty('resource.updates', 'data/updates.txt'); + $this->setEmpty('resource.log', 'data/log.txt'); + $this->setEmpty('resource.update_check', 'data/lastupdatecheck.txt'); + $this->setEmpty('resource.raintpl_tpl', 'tpl/'); + $this->setEmpty('resource.raintpl_tmp', 'tmp/'); + $this->setEmpty('resource.thumbnails_cache', 'cache'); + $this->setEmpty('resource.page_cache', 'pagecache'); $this->setEmpty('security.ban_after', 4); $this->setEmpty('security.ban_duration', 1800); $this->setEmpty('security.session_protection_disabled', false); + $this->setEmpty('security.open_shaarli', false); - $this->setEmpty('general.check_updates', false); - $this->setEmpty('general.rss_permalinks', true); - $this->setEmpty('general.links_per_page', 20); - $this->setEmpty('general.default_private_links', false); - $this->setEmpty('general.enable_thumbnails', true); - $this->setEmpty('general.enable_localcache', true); - $this->setEmpty('general.check_updates_branch', 'stable'); - $this->setEmpty('general.check_updates_interval', 86400); $this->setEmpty('general.header_link', '?'); + $this->setEmpty('general.links_per_page', 20); $this->setEmpty('general.enabled_plugins', array('qrcode')); - $this->setEmpty('extras.show_atom', false); - $this->setEmpty('extras.hide_public_links', false); - $this->setEmpty('extras.hide_timestamps', false); - $this->setEmpty('extras.open_shaarli', false); - $this->setEmpty('extras.redirector', ''); - $this->setEmpty('extras.redirector_encode_url', true); + $this->setEmpty('updates.check_updates', false); + $this->setEmpty('updates.check_updates_branch', 'stable'); + $this->setEmpty('updates.check_updates_interval', 86400); + + $this->setEmpty('feed.rss_permalinks', true); + $this->setEmpty('feed.show_atom', false); + + $this->setEmpty('privacy.default_private_links', false); + $this->setEmpty('privacy.hide_public_links', false); + $this->setEmpty('privacy.hide_timestamps', false); + + $this->setEmpty('thumbnail.enable_thumbnails', true); + $this->setEmpty('thumbnail.enable_localcache', true); + + $this->setEmpty('redirector.url', ''); + $this->setEmpty('redirector.encode_url', true); $this->setEmpty('plugins', array()); } diff --git a/application/config/ConfigPhp.php b/application/config/ConfigPhp.php index b122f4f1..27187b66 100644 --- a/application/config/ConfigPhp.php +++ b/application/config/ConfigPhp.php @@ -34,38 +34,38 @@ class ConfigPhp implements ConfigIO 'credentials.login' => 'login', 'credentials.hash' => 'hash', 'credentials.salt' => 'salt', - 'path.data_dir' => 'config.DATADIR', - 'path.config' => 'config.CONFIG_FILE', - 'path.datastore' => 'config.DATASTORE', - 'path.updates' => 'config.UPDATES_FILE', - 'path.log' => 'config.LOG_FILE', - 'path.update_check' => 'config.UPDATECHECK_FILENAME', - 'path.raintpl_tpl' => 'config.RAINTPL_TPL', - 'path.raintpl_tmp' => 'config.RAINTPL_TMP', - 'path.thumbnails_cache' => 'config.CACHEDIR', - 'path.page_cache' => 'config.PAGECACHE', - 'path.ban_file' => 'config.IPBANS_FILENAME', + 'resource.data_dir' => 'config.DATADIR', + 'resource.config' => 'config.CONFIG_FILE', + 'resource.datastore' => 'config.DATASTORE', + 'resource.updates' => 'config.UPDATES_FILE', + 'resource.log' => 'config.LOG_FILE', + 'resource.update_check' => 'config.UPDATECHECK_FILENAME', + 'resource.raintpl_tpl' => 'config.RAINTPL_TPL', + 'resource.raintpl_tmp' => 'config.RAINTPL_TMP', + 'resource.thumbnails_cache' => 'config.CACHEDIR', + 'resource.page_cache' => 'config.PAGECACHE', + 'resource.ban_file' => 'config.IPBANS_FILENAME', 'security.session_protection_disabled' => 'disablesessionprotection', 'security.ban_after' => 'config.BAN_AFTER', 'security.ban_duration' => 'config.BAN_DURATION', 'general.title' => 'title', 'general.timezone' => 'timezone', 'general.header_link' => 'titleLink', - 'general.check_updates' => 'config.ENABLE_UPDATECHECK', - 'general.check_updates_branch' => 'config.UPDATECHECK_BRANCH', - 'general.check_updates_interval' => 'config.UPDATECHECK_INTERVAL', - 'general.default_private_links' => 'privateLinkByDefault', - 'general.rss_permalinks' => 'config.ENABLE_RSS_PERMALINKS', + 'updates.check_updates' => 'config.ENABLE_UPDATECHECK', + 'updates.check_updates_branch' => 'config.UPDATECHECK_BRANCH', + 'updates.check_updates_interval' => 'config.UPDATECHECK_INTERVAL', + 'privacy.default_private_links' => 'privateLinkByDefault', + 'feed.rss_permalinks' => 'config.ENABLE_RSS_PERMALINKS', 'general.links_per_page' => 'config.LINKS_PER_PAGE', - 'general.enable_thumbnails' => 'config.ENABLE_THUMBNAILS', - 'general.enable_localcache' => 'config.ENABLE_LOCALCACHE', + 'thumbnail.enable_thumbnails' => 'config.ENABLE_THUMBNAILS', + 'thumbnail.enable_localcache' => 'config.ENABLE_LOCALCACHE', 'general.enabled_plugins' => 'config.ENABLED_PLUGINS', - 'extras.redirector' => 'redirector', - 'extras.redirector_encode_url' => 'config.REDIRECTOR_URLENCODE', - 'extras.show_atom' => 'config.SHOW_ATOM', - 'extras.hide_public_links' => 'config.HIDE_PUBLIC_LINKS', - 'extras.hide_timestamps' => 'config.HIDE_TIMESTAMPS', - 'extras.open_shaarli' => 'config.OPEN_SHAARLI', + 'redirector.url' => 'redirector', + 'redirector.encode_url' => 'config.REDIRECTOR_URLENCODE', + 'feed.show_atom' => 'config.SHOW_ATOM', + 'privacy.hide_public_links' => 'config.HIDE_PUBLIC_LINKS', + 'privacy.hide_timestamps' => 'config.HIDE_TIMESTAMPS', + 'security.open_shaarli' => 'config.OPEN_SHAARLI', ); /** -- cgit v1.2.3 From 5ff23f02b80ec6ddee28dee869171ee8e3656b7c Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Mon, 20 Jun 2016 18:30:37 +0200 Subject: Add closing PHP tags to JSON config files --- application/config/ConfigJson.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'application/config') diff --git a/application/config/ConfigJson.php b/application/config/ConfigJson.php index 94693c86..d07fefee 100644 --- a/application/config/ConfigJson.php +++ b/application/config/ConfigJson.php @@ -17,6 +17,7 @@ class ConfigJson implements ConfigIO } $data = file_get_contents($filepath); $data = str_replace(self::getPhpHeaders(), '', $data); + $data = str_replace(self::getPhpSuffix(), '', $data); $data = json_decode($data, true); if ($data === null) { $error = json_last_error(); @@ -32,7 +33,7 @@ class ConfigJson implements ConfigIO { // JSON_PRETTY_PRINT is available from PHP 5.4. $print = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; - $data = self::getPhpHeaders() . json_encode($conf, $print); + $data = self::getPhpHeaders() . json_encode($conf, $print) . self::getPhpSuffix(); if (!file_put_contents($filepath, $data)) { throw new IOException( $filepath, @@ -62,4 +63,16 @@ class ConfigJson implements ConfigIO { return ''; + } } -- cgit v1.2.3