diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php index 978fc9da..ed9abc39 100644 --- a/application/ApplicationUtils.php +++ b/application/ApplicationUtils.php @@ -132,32 +132,32 @@ public static function checkPHPVersion($minVersion, $curVersion) /** * Checks Shaarli has the proper access permissions to its resources * - * @param array $globalConfig The $GLOBALS['config'] array - * * @return array A list of the detected configuration issues */ - public static function checkResourcePermissions($globalConfig) + public static function checkResourcePermissions() { $errors = array(); + $conf = ConfigManager::getInstance(); // Check script and template directories are readable foreach (array( 'application', 'inc', 'plugins', - $globalConfig['RAINTPL_TPL'] + $conf->get('config.RAINTPL_TPL'), ) as $path) { if (! is_readable(realpath($path))) { $errors[] = '"'.$path.'" directory is not readable'; } } + $datadir = $conf->get('config.DATADIR'); // Check cache and data directories are readable and writeable foreach (array( - $globalConfig['CACHEDIR'], - $globalConfig['DATADIR'], - $globalConfig['PAGECACHE'], - $globalConfig['RAINTPL_TMP'] + $conf->get('config.CACHEDIR'), + $datadir, + $conf->get('config.PAGECACHE'), + $conf->get('config.RAINTPL_TMP'), ) as $path) { if (! is_readable(realpath($path))) { $errors[] = '"'.$path.'" directory is not readable'; @@ -169,11 +169,11 @@ public static function checkResourcePermissions($globalConfig) // Check configuration files are readable and writeable foreach (array( - $globalConfig['CONFIG_FILE'], - $globalConfig['DATASTORE'], - $globalConfig['IPBANS_FILENAME'], - $globalConfig['LOG_FILE'], - $globalConfig['UPDATECHECK_FILENAME'] + $conf->getConfigFile(), + $conf->get('config.DATASTORE'), + $conf->get('config.IPBANS_FILENAME'), + $conf->get('config.LOG_FILE'), + $conf->get('config.UPDATECHECK_FILENAME'), ) as $path) { if (! is_file(realpath($path))) { # the file may not exist yet diff --git a/application/Config.php b/application/Config.php deleted file mode 100644 index 05a59452..00000000 --- a/application/Config.php +++ /dev/null @@ -1,221 +0,0 @@ - $value) { - $configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($config['config'][$key], true).';'. PHP_EOL; - } - - if (isset($config['plugins'])) { - foreach ($config['plugins'] as $key => $value) { - $configStr .= '$GLOBALS[\'plugins\'][\''. $key .'\'] = '.var_export($config['plugins'][$key], true).';'. PHP_EOL; - } - } - - if (!file_put_contents($config['config']['CONFIG_FILE'], $configStr) - || strcmp(file_get_contents($config['config']['CONFIG_FILE']), $configStr) != 0 - ) { - throw new Exception( - 'Shaarli could not create the config file. - Please make sure Shaarli has the right to write in the folder is it installed in.' - ); - } -} - -/** - * Process plugin administration form data and save it in an array. - * - * @param array $formData Data sent by the plugin admin form. - * - * @return array New list of enabled plugin, ordered. - * - * @throws PluginConfigOrderException Plugins can't be sorted because their order is invalid. - */ -function save_plugin_config($formData) -{ - // Make sure there are no duplicates in orders. - if (!validate_plugin_order($formData)) { - throw new PluginConfigOrderException(); - } - - $plugins = array(); - $newEnabledPlugins = array(); - foreach ($formData as $key => $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 $config Plugins configuration. - * - * @return mixed Updated $plugins array. - */ -function load_plugin_parameter_values($plugins, $config) -{ - $out = $plugins; - foreach ($plugins as $name => $plugin) { - if (empty($plugin['parameters'])) { - continue; - } - - foreach ($plugin['parameters'] as $key => $param) { - if (!empty($config[$key])) { - $out[$name]['parameters'][$key] = $config[$key]; - } - } - } - - return $out; -} - -/** - * 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.'; - } -} - -/** - * 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.'; - } -} diff --git a/application/FileUtils.php b/application/FileUtils.php index 6a12ef0e..6cac9825 100644 --- a/application/FileUtils.php +++ b/application/FileUtils.php @@ -9,11 +9,13 @@ class IOException extends Exception /** * Construct a new IOException * - * @param string $path path to the ressource that cannot be accessed + * @param string $path path to the resource that cannot be accessed + * @param string $message Custom exception message. */ - public function __construct($path) + public function __construct($path, $message = '') { $this->path = $path; - $this->message = 'Error accessing '.$this->path; + $this->message = empty($message) ? 'Error accessing' : $message; + $this->message .= PHP_EOL . $this->path; } } diff --git a/application/PageBuilder.php b/application/PageBuilder.php index 82580787..cf13c714 100644 --- a/application/PageBuilder.php +++ b/application/PageBuilder.php @@ -29,21 +29,22 @@ function __construct() private function initialize() { $this->tpl = new RainTPL(); + $conf = ConfigManager::getInstance(); try { $version = ApplicationUtils::checkUpdate( shaarli_version, - $GLOBALS['config']['UPDATECHECK_FILENAME'], - $GLOBALS['config']['UPDATECHECK_INTERVAL'], - $GLOBALS['config']['ENABLE_UPDATECHECK'], + $conf->get('config.UPDATECHECK_FILENAME'), + $conf->get('config.UPDATECHECK_INTERVAL'), + $conf->get('config.ENABLE_UPDATECHECK'), isLoggedIn(), - $GLOBALS['config']['UPDATECHECK_BRANCH'] + $conf->get('config.UPDATECHECK_BRANCH') ); $this->tpl->assign('newVersion', escape($version)); $this->tpl->assign('versionError', ''); } catch (Exception $exc) { - logm($GLOBALS['config']['LOG_FILE'], $_SERVER['REMOTE_ADDR'], $exc->getMessage()); + logm($conf->get('config.LOG_FILE'), $_SERVER['REMOTE_ADDR'], $exc->getMessage()); $this->tpl->assign('newVersion', ''); $this->tpl->assign('versionError', escape($exc->getMessage())); } @@ -62,16 +63,19 @@ private function initialize() $this->tpl->assign('scripturl', index_url($_SERVER)); $this->tpl->assign('pagetitle', 'Shaarli'); $this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links? - if (!empty($GLOBALS['title'])) { - $this->tpl->assign('pagetitle', $GLOBALS['title']); + if ($conf->exists('title')) { + $this->tpl->assign('pagetitle', $conf->get('title')); } - if (!empty($GLOBALS['titleLink'])) { - $this->tpl->assign('titleLink', $GLOBALS['titleLink']); + if ($conf->exists('titleLink')) { + $this->tpl->assign('titleLink', $conf->get('titleLink')); } - if (!empty($GLOBALS['pagetitle'])) { - $this->tpl->assign('pagetitle', $GLOBALS['pagetitle']); + if ($conf->exists('pagetitle')) { + $this->tpl->assign('pagetitle', $conf->get('pagetitle')); } - $this->tpl->assign('shaarlititle', empty($GLOBALS['title']) ? 'Shaarli': $GLOBALS['title']); + $this->tpl->assign('shaarlititle', $conf->get('title', 'Shaarli')); + $this->tpl->assign('openshaarli', $conf->get('config.OPEN_SHAARLI', false)); + $this->tpl->assign('showatom', $conf->get('config.SHOW_ATOM', false)); + // FIXME! Globals if (!empty($GLOBALS['plugin_errors'])) { $this->tpl->assign('plugin_errors', $GLOBALS['plugin_errors']); } diff --git a/application/Updater.php b/application/Updater.php index 58c13c07..6b92af3d 100644 --- a/application/Updater.php +++ b/application/Updater.php @@ -12,11 +12,6 @@ class Updater */ protected $doneUpdates; - /** - * @var array Shaarli's configuration array. - */ - protected $config; - /** * @var LinkDB instance. */ @@ -36,14 +31,12 @@ class Updater * Object constructor. * * @param array $doneUpdates Updates which are already done. - * @param array $config Shaarli's configuration array. * @param LinkDB $linkDB LinkDB instance. * @param boolean $isLoggedIn True if the user is logged in. */ - public function __construct($doneUpdates, $config, $linkDB, $isLoggedIn) + public function __construct($doneUpdates, $linkDB, $isLoggedIn) { $this->doneUpdates = $doneUpdates; - $this->config = $config; $this->linkDB = $linkDB; $this->isLoggedIn = $isLoggedIn; @@ -114,19 +107,21 @@ public function getDoneUpdates() */ public function updateMethodMergeDeprecatedConfigFile() { - $config_file = $this->config['config']['CONFIG_FILE']; + $conf = ConfigManager::getInstance(); - if (is_file($this->config['config']['DATADIR'].'/options.php')) { - include $this->config['config']['DATADIR'].'/options.php'; + if (is_file($conf->get('config.DATADIR') . '/options.php')) { + include $conf->get('config.DATADIR') . '/options.php'; // Load GLOBALS into config + $allowedKeys = array_merge(ConfigPhp::$ROOT_KEYS); + $allowedKeys[] = 'config'; foreach ($GLOBALS as $key => $value) { - $this->config[$key] = $value; + if (in_array($key, $allowedKeys)) { + $conf->set($key, $value); + } } - $this->config['config']['CONFIG_FILE'] = $config_file; - writeConfig($this->config, $this->isLoggedIn); - - unlink($this->config['config']['DATADIR'].'/options.php'); + $conf->write($this->isLoggedIn); + unlink($conf->get('config.DATADIR').'/options.php'); } return true; @@ -137,13 +132,14 @@ public function updateMethodMergeDeprecatedConfigFile() */ public function updateMethodRenameDashTags() { + $conf = ConfigManager::getInstance(); $linklist = $this->linkDB->filterSearch(); foreach ($linklist as $link) { $link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']); $link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true))); $this->linkDB[$link['linkdate']] = $link; } - $this->linkDB->savedb($this->config['config']['PAGECACHE']); + $this->linkDB->savedb($conf->get('config.PAGECACHE')); return true; } } diff --git a/application/Utils.php b/application/Utils.php index da521cce..9a8ca6d1 100644 --- a/application/Utils.php +++ b/application/Utils.php @@ -273,4 +273,4 @@ function autoLocale($headerLocale) } } setlocale(LC_ALL, $attempts); -} \ No newline at end of file +} 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 @@ function read($filepath); * * @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 @@ public static function getInstance() 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 @@ protected function initialize() $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 @@ public function get($setting, $default = '') * @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 @@ public function exists($setting) * * @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 @@ public function write($isLoggedIn) } } - $this->configIO->write(self::$CONFIG_FILE, $this->loadedConfig); + return $this->configIO->write($this->getConfigFile(), $this->loadedConfig); } /** @@ -327,6 +352,22 @@ protected function setEmpty($key, $value) $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 @@ function read($filepath) */ function write($filepath, $conf) { - $filepath .= $this->getExtension(); - $configStr = ' /shaarli/ -define('WEB_PATH', substr($_SERVER["REQUEST_URI"], 0, 1+strrpos($_SERVER["REQUEST_URI"], '/', 0))); +define('WEB_PATH', substr($_SERVER['REQUEST_URI'], 0, 1+strrpos($_SERVER['REQUEST_URI'], '/', 0))); // High execution time in case of problematic imports/exports. ini_set('max_input_time','60'); @@ -144,12 +43,6 @@ // See all errors (for debugging only) //error_reporting(-1); -/* - * User configuration - */ -if (is_file($GLOBALS['config']['CONFIG_FILE'])) { - require_once $GLOBALS['config']['CONFIG_FILE']; -} // Shaarli library require_once 'application/ApplicationUtils.php'; @@ -166,10 +59,12 @@ require_once 'application/TimeZone.php'; require_once 'application/Url.php'; require_once 'application/Utils.php'; -require_once 'application/Config.php'; +require_once 'application/config/ConfigManager.php'; +require_once 'application/config/ConfigPlugin.php'; require_once 'application/PluginManager.php'; require_once 'application/Router.php'; require_once 'application/Updater.php'; +require_once 'inc/rain.tpl.class.php'; // Ensure the PHP version is supported try { @@ -210,16 +105,16 @@ $_COOKIE['shaarli'] = session_id(); } -include "inc/rain.tpl.class.php"; //include Rain TPL -raintpl::$tpl_dir = $GLOBALS['config']['RAINTPL_TPL']; // template directory -raintpl::$cache_dir = $GLOBALS['config']['RAINTPL_TMP']; // cache directory +$conf = ConfigManager::getInstance(); + +RainTPL::$tpl_dir = $conf->get('config.RAINTPL_TPL'); // template directory +RainTPL::$cache_dir = $conf->get('config.RAINTPL_TMP'); // cache directory $pluginManager = PluginManager::getInstance(); -$pluginManager->load($GLOBALS['config']['ENABLED_PLUGINS']); +$pluginManager->load($conf->get('config.ENABLED_PLUGINS')); ob_start(); // Output buffering for the page cache. - // In case stupid admin has left magic_quotes enabled in php.ini: if (get_magic_quotes_gpc()) { @@ -236,17 +131,25 @@ function stripslashes_deep($value) { $value = is_array($value) ? array_map('stri header("Pragma: no-cache"); // Handling of old config file which do not have the new parameters. -if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.escape(index_url($_SERVER)); -if (empty($GLOBALS['timezone'])) $GLOBALS['timezone']=date_default_timezone_get(); -if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']=''; -if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false; -if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=false; -if (empty($GLOBALS['titleLink'])) $GLOBALS['titleLink']='?'; -// I really need to rewrite Shaarli with a proper configuation manager. +if (! $conf->exists('title')) { + $conf->set('title', 'Shared links on '. escape(index_url($_SERVER))); +} +if (! $conf->exists('timezone')) { + $conf->set('timezone', date_default_timezone_get()); +} +if (! $conf->exists('disablesessionprotection')) { + $conf->set('disablesessionprotection', false); +} +if (! $conf->exists('privateLinkByDefault')) { + $conf->set('privateLinkByDefault', false); +} +if (! $conf->exists('titleLink')) { + $conf->set('titleLink', '?'); +} -if (! is_file($GLOBALS['config']['CONFIG_FILE'])) { +if (! is_file($conf->getConfigFile())) { // Ensure Shaarli has proper access to its resources - $errors = ApplicationUtils::checkResourcePermissions($GLOBALS['config']); + $errors = ApplicationUtils::checkResourcePermissions(); if ($errors != array()) { $message = '

Insufficient permissions: