3 // FIXME! Namespaces...
4 require_once 'ConfigIO.php';
5 require_once 'ConfigPhp.php';
6 #require_once 'ConfigJson.php';
11 * Singleton, manages all Shaarli's settings.
16 * @var ConfigManager instance.
18 protected static $instance = null;
21 * @var string Config folder.
23 public static $CONFIG_FILE = 'data/config';
26 * @var string Flag telling a setting is not found.
28 protected static $NOT_FOUND = 'NOT_FOUND';
31 * @var array Loaded config array.
33 protected $loadedConfig;
36 * @var ConfigIO implementation instance.
41 * Private constructor: new instances not allowed.
43 private function __construct() {}
46 * Cloning isn't allowed either.
48 private function __clone() {}
51 * Return existing instance of PluginManager, or create it.
53 * @return ConfigManager instance.
55 public static function getInstance()
57 if (!(self
::$instance instanceof self
)) {
58 self
::$instance = new self();
59 self
::$instance->initialize();
62 return self
::$instance;
66 * Reset the ConfigManager instance.
68 public static function reset()
70 self
::$instance = null;
71 return self
::getInstance();
75 * Rebuild the loaded config array from config files.
77 public function reload()
83 * Initialize the ConfigIO and loaded the conf.
85 protected function initialize()
87 /*if (! file_exists(self::$CONFIG_FILE .'.php')) {
88 $this->configIO = new ConfigJson();
90 $this->configIO = new ConfigPhp();
92 $this->configIO
= new ConfigPhp();
97 * Load configuration in the ConfigurationManager.
99 protected function load()
101 $this->loadedConfig
= $this->configIO
->read($this->getConfigFile());
102 $this->setDefaultValues();
108 * Supports nested settings with dot separated keys.
109 * Eg. 'config.stuff.option' will find $conf[config][stuff][option],
111 * { "config": { "stuff": {"option": "mysetting" } } } }
113 * @param string $setting Asked setting, keys separated with dots.
114 * @param string $default Default value if not found.
116 * @return mixed Found setting, or the default value.
118 public function get($setting, $default = '')
120 $settings = explode('.', $setting);
121 $value = self
::getConfig($settings, $this->loadedConfig
);
122 if ($value === self
::$NOT_FOUND) {
129 * Set a setting, and eventually write it.
131 * Supports nested settings with dot separated keys.
133 * @param string $setting Asked setting, keys separated with dots.
134 * @param string $value Value to set.
135 * @param bool $write Write the new setting in the config file, default false.
136 * @param bool $isLoggedIn User login state, default false.
138 * @throws Exception Invalid
140 public function set($setting, $value, $write = false, $isLoggedIn = false)
142 if (empty($setting) || ! is_string($setting)) {
143 throw new Exception('Invalid setting key parameter. String expected, got: '. gettype($setting));
146 $settings = explode('.', $setting);
147 self
::setConfig($settings, $value, $this->loadedConfig
);
149 $this->write($isLoggedIn);
154 * Check if a settings exists.
156 * Supports nested settings with dot separated keys.
158 * @param string $setting Asked setting, keys separated with dots.
160 * @return bool true if the setting exists, false otherwise.
162 public function exists($setting)
164 $settings = explode('.', $setting);
165 $value = self
::getConfig($settings, $this->loadedConfig
);
166 if ($value === self
::$NOT_FOUND) {
173 * Call the config writer.
175 * @param bool $isLoggedIn User login state.
177 * @return bool True if the configuration has been successfully written, false otherwise.
179 * @throws MissingFieldConfigException: a mandatory field has not been provided in $conf.
180 * @throws UnauthorizedConfigException: user is not authorize to change configuration.
181 * @throws IOException: an error occurred while writing the new config file.
183 public function write($isLoggedIn)
185 // These fields are required in configuration.
186 $mandatoryFields = array(
187 'login', 'hash', 'salt', 'timezone', 'title', 'titleLink',
188 'redirector', 'disablesessionprotection', 'privateLinkByDefault'
191 // Only logged in user can alter config.
192 if (is_file(self
::$CONFIG_FILE) && !$isLoggedIn) {
193 throw new UnauthorizedConfigException();
196 // Check that all mandatory fields are provided in $conf.
197 foreach ($mandatoryFields as $field) {
198 if (! $this->exists($field)) {
199 throw new MissingFieldConfigException($field);
203 return $this->configIO
->write($this->getConfigFile(), $this->loadedConfig
);
207 * Get the configuration file path.
209 * @return string Config file path.
211 public function getConfigFile()
213 return self
::$CONFIG_FILE . $this->configIO
->getExtension();
217 * Recursive function which find asked setting in the loaded config.
219 * @param array $settings Ordered array which contains keys to find.
220 * @param array $conf Loaded settings, then sub-array.
222 * @return mixed Found setting or NOT_FOUND flag.
224 protected static function getConfig($settings, $conf)
226 if (!is_array($settings) || count($settings) == 0) {
227 return self
::$NOT_FOUND;
230 $setting = array_shift($settings);
231 if (!isset($conf[$setting])) {
232 return self
::$NOT_FOUND;
235 if (count($settings) > 0) {
236 return self
::getConfig($settings, $conf[$setting]);
238 return $conf[$setting];
242 * Recursive function which find asked setting in the loaded config.
244 * @param array $settings Ordered array which contains keys to find.
245 * @param mixed $value
246 * @param array $conf Loaded settings, then sub-array.
248 * @return mixed Found setting or NOT_FOUND flag.
250 protected static function setConfig($settings, $value, &$conf)
252 if (!is_array($settings) || count($settings) == 0) {
253 return self
::$NOT_FOUND;
256 $setting = array_shift($settings);
257 if (count($settings) > 0) {
258 return self
::setConfig($settings, $value, $conf[$setting]);
260 $conf[$setting] = $value;
264 * Set a bunch of default values allowing Shaarli to start without a config file.
266 protected function setDefaultValues()
269 $this->setEmpty('config.DATADIR', 'data');
271 // Main configuration file
272 $this->setEmpty('config.CONFIG_FILE', 'data/config.php');
275 $this->setEmpty('config.DATASTORE', 'data/datastore.php');
278 $this->setEmpty('config.IPBANS_FILENAME', 'data/ipbans.php');
280 // Processed updates file.
281 $this->setEmpty('config.UPDATES_FILE', 'data/updates.txt');
284 $this->setEmpty('config.LOG_FILE', 'data/log.txt');
286 // For updates check of Shaarli
287 $this->setEmpty('config.UPDATECHECK_FILENAME', 'data/lastupdatecheck.txt');
289 // Set ENABLE_UPDATECHECK to disabled by default.
290 $this->setEmpty('config.ENABLE_UPDATECHECK', false);
292 // RainTPL cache directory (keep the trailing slash!)
293 $this->setEmpty('config.RAINTPL_TMP', 'tmp/');
294 // Raintpl template directory (keep the trailing slash!)
295 $this->setEmpty('config.RAINTPL_TPL', 'tpl/');
297 // Thumbnail cache directory
298 $this->setEmpty('config.CACHEDIR', 'cache');
300 // Atom & RSS feed cache directory
301 $this->setEmpty('config.PAGECACHE', 'pagecache');
303 // Ban IP after this many failures
304 $this->setEmpty('config.BAN_AFTER', 4);
305 // Ban duration for IP address after login failures (in seconds)
306 $this->setEmpty('config.BAN_DURATION', 1800);
309 // Enable RSS permalinks by default.
310 // This corresponds to the default behavior of shaarli before this was added as an option.
311 $this->setEmpty('config.ENABLE_RSS_PERMALINKS', true);
312 // If true, an extra "ATOM feed" button will be displayed in the toolbar
313 $this->setEmpty('config.SHOW_ATOM', false);
315 // Link display options
316 $this->setEmpty('config.HIDE_PUBLIC_LINKS', false);
317 $this->setEmpty('config.HIDE_TIMESTAMPS', false);
318 $this->setEmpty('config.LINKS_PER_PAGE', 20);
320 // Open Shaarli (true): anyone can add/edit/delete links without having to login
321 $this->setEmpty('config.OPEN_SHAARLI', false);
324 // Display thumbnails in links
325 $this->setEmpty('config.ENABLE_THUMBNAILS', true);
326 // Store thumbnails in a local cache
327 $this->setEmpty('config.ENABLE_LOCALCACHE', true);
329 // Update check frequency for Shaarli. 86400 seconds=24 hours
330 $this->setEmpty('config.UPDATECHECK_BRANCH', 'stable');
331 $this->setEmpty('config.UPDATECHECK_INTERVAL', 86400);
333 $this->setEmpty('redirector', '');
334 $this->setEmpty('config.REDIRECTOR_URLENCODE', true);
337 $this->setEmpty('config.ENABLED_PLUGINS', array('qrcode'));
339 // Initialize plugin parameters array.
340 $this->setEmpty('plugins', array());
344 * Set only if the setting does not exists.
346 * @param string $key Setting key.
347 * @param mixed $value Setting value.
349 protected function setEmpty($key, $value)
351 if (! $this->exists($key)) {
352 $this->set($key, $value);
359 public function getConfigIO()
361 return $this->configIO
;
365 * @param ConfigIO $configIO
367 public function setConfigIO($configIO)
369 $this->configIO
= $configIO;
374 * Exception used if a mandatory field is missing in given configuration.
376 class MissingFieldConfigException
extends Exception
381 * Construct exception.
383 * @param string $field field name missing.
385 public function __construct($field)
387 $this->field
= $field;
388 $this->message
= 'Configuration value is required for '. $this->field
;
393 * Exception used if an unauthorized attempt to edit configuration has been made.
395 class UnauthorizedConfigException
extends Exception
398 * Construct exception.
400 public function __construct()
402 $this->message
= 'You are not authorized to alter config.';