فهرست منبع

New configuration system (not working yet)

- Use only Minz_Configuration
- register() method to load a new configuration file
- get() to get a configuration
- new exceptions related to configuration
- fix a list configuration calls to have FRSS working

Current problems to resolve:

- How to handle configuration param verifications (i.e. check auth_type
  is a value from none, http_auth, persona or form)
- We must use $conf = Minz_Configuration::get('system'); $general_conf = $conf->general;
  to access global system configuration which is quite annoying. How to change that?

See https://github.com/FreshRSS/FreshRSS/issues/730
Marien Fressinaud 11 سال پیش
والد
کامیت
51a71ec4b9

+ 22 - 14
app/Controllers/authController.php

@@ -27,6 +27,8 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 		if (Minz_Request::isPost()) {
 			$ok = true;
 
+			$system_conf = Minz_Configuration::get('system');
+			$general = $system_conf->general;
 			$current_token = FreshRSS_Context::$conf->token;
 			$token = Minz_Request::param('token', $current_token);
 			FreshRSS_Context::$conf->_token($token);
@@ -39,18 +41,21 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 			$auth_type = Minz_Request::param('auth_type', 'none');
 			$unsafe_autologin = Minz_Request::param('unsafe_autologin', false);
 			$api_enabled = Minz_Request::param('api_enabled', false);
-			if ($anon != Minz_Configuration::allowAnonymous() ||
-				$auth_type != Minz_Configuration::authType() ||
-				$anon_refresh != Minz_Configuration::allowAnonymousRefresh() ||
-				$unsafe_autologin != Minz_Configuration::unsafeAutologinEnabled() ||
-				$api_enabled != Minz_Configuration::apiEnabled()) {
-
-				Minz_Configuration::_authType($auth_type);
-				Minz_Configuration::_allowAnonymous($anon);
-				Minz_Configuration::_allowAnonymousRefresh($anon_refresh);
-				Minz_Configuration::_enableAutologin($unsafe_autologin);
-				Minz_Configuration::_enableApi($api_enabled);
-				$ok &= Minz_Configuration::writeFile();
+			if ($anon != $general['allow_anonymous'] ||
+				$auth_type != $general['auth_type'] ||
+				$anon_refresh != $general['allow_anonymous_refresh'] ||
+				$unsafe_autologin != $general['unsafe_autologin_enabled'] ||
+				$api_enabled != $general['api_enabled']) {
+
+				// TODO: test values from form
+				$general['auth_type'] = $auth_type;
+				$general['allow_anonymous'] = $anon;
+				$general['allow_anonymous_refresh'] = $anon_refresh;
+				$general['unsafe_autologin_enabled'] = $unsafe_autologin;
+				$general['api_enabled'] = $api_enabled;
+
+				$system_conf->general = $general;
+				$ok &= $system_conf->save();
 			}
 
 			invalidateHttpCache();
@@ -76,7 +81,8 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 			Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
 		}
 
-		$auth_type = Minz_Configuration::authType();
+		$conf = Minz_Configuration::get('system');
+		$auth_type = $conf->general['auth_type'];
 		switch ($auth_type) {
 		case 'form':
 			Minz_Request::forward(array('c' => 'auth', 'a' => 'formLogin'));
@@ -114,6 +120,8 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 		$file_mtime = @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js');
 		Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . $file_mtime));
 
+		$conf = Minz_Configuration::get('system');
+
 		if (Minz_Request::isPost()) {
 			$nonce = Minz_Session::param('nonce');
 			$username = Minz_Request::param('username', '');
@@ -154,7 +162,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 				Minz_Request::bad(_t('feedback.auth.login.invalid'),
 				                  array('c' => 'auth', 'a' => 'login'));
 			}
-		} elseif (Minz_Configuration::unsafeAutologinEnabled()) {
+		} elseif ($conf->general['unsafe_autologin_enabled']) {
 			$username = Minz_Request::param('u', '');
 			$password = Minz_Request::param('p', '');
 			Minz_Request::_param('p');

+ 7 - 1
app/FreshRSS.php

@@ -6,6 +6,11 @@ class FreshRSS extends Minz_FrontController {
 			Minz_Session::init('FreshRSS');
 		}
 
+		$current_user = Minz_Session::param('currentUser', '_');
+		Minz_Configuration::register('user',
+		                             join_path(USERS_PATH, $current_user, 'config.php'),
+		                             join_path(USERS_PATH, '_', 'config.default.php'));
+
 		// Need to be called just after session init because it initializes
 		// current user.
 		FreshRSS_Auth::init();
@@ -57,7 +62,8 @@ class FreshRSS extends Minz_FrontController {
 		Minz_View::appendScript(Minz_Url::display('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js')));
 		Minz_View::appendScript(Minz_Url::display('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js')));
 
-		if (Minz_Configuration::authType() === 'persona') {
+		$conf = Minz_Configuration::get('system');
+		if ($conf->general['auth_type'] === 'persona') {
 			// TODO move it in a plugin
 			// Needed for login AND logout with Persona.
 			Minz_View::appendScript('https://login.persona.org/include.js');

+ 26 - 14
app/Models/Auth.php

@@ -16,7 +16,8 @@ class FreshRSS_Auth {
 		self::$login_ok = Minz_Session::param('loginOk', false);
 		$current_user = Minz_Session::param('currentUser', '');
 		if ($current_user === '') {
-			$current_user = Minz_Configuration::defaultUser();
+			$conf = Minz_Configuration::get('system');
+			$current_user = $conf->general['default_user'];
 			Minz_Session::_param('currentUser', $current_user);
 		}
 
@@ -40,7 +41,9 @@ class FreshRSS_Auth {
 	 * @return boolean true if user can be connected, false else.
 	 */
 	private static function accessControl() {
-		switch (Minz_Configuration::authType()) {
+		$conf = Minz_Configuration::get('system');
+		$auth_type = $conf->general['auth_type'];
+		switch ($auth_type) {
 		case 'form':
 			$credentials = FreshRSS_FormAuth::getCredentialsFromCookie();
 			$current_user = '';
@@ -79,22 +82,19 @@ class FreshRSS_Auth {
 	 * Gives access to the current user.
 	 */
 	public static function giveAccess() {
-		$current_user = Minz_Session::param('currentUser');
-		try {
-			$conf = new FreshRSS_Configuration($current_user);
-		} catch(Minz_Exception $e) {
-			die($e->getMessage());
-		}
+		$user_conf = Minz_Configuration::get('user');
+		$system_conf = Minz_Configuration::get('system');
+		$auth_type = $system_conf->general['auth_type'];
 
-		switch (Minz_Configuration::authType()) {
+		switch ($auth_type) {
 		case 'form':
-			self::$login_ok = Minz_Session::param('passwordHash') === $conf->passwordHash;
+			self::$login_ok = Minz_Session::param('passwordHash') === $user_conf->passwordHash;
 			break;
 		case 'http_auth':
 			self::$login_ok = strcasecmp($current_user, httpAuthUser()) === 0;
 			break;
 		case 'persona':
-			self::$login_ok = strcasecmp(Minz_Session::param('mail'), $conf->mail_login) === 0;
+			self::$login_ok = strcasecmp(Minz_Session::param('mail'), $user_conf->mail_login) === 0;
 			break;
 		case 'none':
 			self::$login_ok = true;
@@ -114,12 +114,14 @@ class FreshRSS_Auth {
 	 * @return boolean true if user has corresponding access, false else.
 	 */
 	public static function hasAccess($scope = 'general') {
+		$conf = Minz_Configuration::get('system');
+		$default_user = $conf->general['default_user'];
 		$ok = self::$login_ok;
 		switch ($scope) {
 		case 'general':
 			break;
 		case 'admin':
-			$ok &= Minz_Session::param('currentUser') === Minz_Configuration::defaultUser();
+			$ok &= Minz_Session::param('currentUser') === $default_user;
 			break;
 		default:
 			$ok = false;
@@ -133,9 +135,10 @@ class FreshRSS_Auth {
 	public static function removeAccess() {
 		Minz_Session::_param('loginOk');
 		self::$login_ok = false;
-		Minz_Session::_param('currentUser', Minz_Configuration::defaultUser());
+		$conf = Minz_Configuration::get('system');
+		Minz_Session::_param('currentUser', $conf->general['default_user']);
 
-		switch (Minz_Configuration::authType()) {
+		switch ($conf->general['auth_type']) {
 		case 'form':
 			Minz_Session::_param('passwordHash');
 			FreshRSS_FormAuth::deleteCookie();
@@ -151,6 +154,15 @@ class FreshRSS_Auth {
 			// TODO: extensions
 		}
 	}
+
+	/**
+	 * Return if authentication is enabled on this instance of FRSS.
+	 */
+	public static function accessNeedLogin() {
+		$conf = Minz_Configuration::get('system');
+		$auth_type = $conf->general['auth_type'];
+		return $auth_type === 'form' || $auth_type === 'persona';
+	}
 }
 
 

+ 2 - 2
app/Models/Context.php

@@ -41,10 +41,10 @@ class FreshRSS_Context {
 	 */
 	public static function init() {
 		// Init configuration.
-		$current_user = Minz_Session::param('currentUser');
 		try {
-			self::$conf = new FreshRSS_Configuration($current_user);
+			self::$conf = Minz_Configuration::get('user');
 		} catch(Minz_Exception $e) {
+			$current_user = Minz_Session::param('currentUser', '_');
 			Minz_Log::error('Cannot load configuration file of user `' . $current_user . '`');
 			die($e->getMessage());
 		}

+ 8 - 8
app/Models/Factory.php

@@ -3,8 +3,8 @@
 class FreshRSS_Factory {
 
 	public static function createFeedDao($username = null) {
-		$db = Minz_Configuration::dataBase();
-		if ($db['type'] === 'sqlite') {
+		$conf = Minz_Configuration::get('system');
+		if ($conf->db['type'] === 'sqlite') {
 			return new FreshRSS_FeedDAOSQLite($username);
 		} else {
 			return new FreshRSS_FeedDAO($username);
@@ -12,8 +12,8 @@ class FreshRSS_Factory {
 	}
 
 	public static function createEntryDao($username = null) {
-		$db = Minz_Configuration::dataBase();
-		if ($db['type'] === 'sqlite') {
+		$conf = Minz_Configuration::get('system');
+		if ($conf->db['type'] === 'sqlite') {
 			return new FreshRSS_EntryDAOSQLite($username);
 		} else {
 			return new FreshRSS_EntryDAO($username);
@@ -21,8 +21,8 @@ class FreshRSS_Factory {
 	}
 
 	public static function createStatsDAO($username = null) {
-		$db = Minz_Configuration::dataBase();
-		if ($db['type'] === 'sqlite') {
+		$conf = Minz_Configuration::get('system');
+		if ($conf->db['type'] === 'sqlite') {
 			return new FreshRSS_StatsDAOSQLite($username);
 		} else {
 			return new FreshRSS_StatsDAO($username);
@@ -30,8 +30,8 @@ class FreshRSS_Factory {
 	}
 
 	public static function createDatabaseDAO($username = null) {
-		$db = Minz_Configuration::dataBase();
-		if ($db['type'] === 'sqlite') {
+		$conf = Minz_Configuration::get('system');
+		if ($conf->db['type'] === 'sqlite') {
 			return new FreshRSS_DatabaseDAOSQLite($username);
 		} else {
 			return new FreshRSS_DatabaseDAO($username);

+ 2 - 1
app/Models/Feed.php

@@ -39,8 +39,9 @@ class FreshRSS_Feed extends Minz_Model {
 	}
 
 	public function hash() {
+		$conf = Minz_Configuration::get('system');
 		if ($this->hash === null) {
-			$this->hash = hash('crc32b', Minz_Configuration::salt() . $this->url);
+			$this->hash = hash('crc32b', $conf->general['salt'] . $this->url);
 		}
 		return $this->hash;
 	}

+ 8 - 5
app/layout/header.phtml

@@ -1,5 +1,8 @@
 <?php
-if (Minz_Configuration::canLogIn()) {
+
+$conf = Minz_Configuration::get('system');
+
+if (FreshRSS_Auth::accessNeedLogin()) {
 	?><ul class="nav nav-head nav-login"><?php
 		if (FreshRSS_Auth::hasAccess()) {
 			?><li class="item"><?php echo _i('logout'); ?> <a class="signout" href="<?php echo _url('auth', 'logout'); ?>"><?php echo _t('gen.auth.logout'); ?></a></li><?php
@@ -15,13 +18,13 @@ if (Minz_Configuration::canLogIn()) {
 		<h1>
 			<a href="<?php echo _url('index', 'index'); ?>">
 				<img class="logo" src="<?php echo _i('icon', true); ?>" alt="⊚" />
-				<?php echo Minz_Configuration::title(); ?>
+				<?php echo $conf->general['title']; ?>
 			</a>
 		</h1>
 	</div>
 
 	<div class="item search">
-		<?php if (FreshRSS_Auth::hasAccess() || Minz_Configuration::allowAnonymous()) { ?>
+		<?php if (FreshRSS_Auth::hasAccess() || $conf->general['allow_anonymous']) { ?>
 		<form action="<?php echo _url('index', 'index'); ?>" method="get">
 			<div class="stick">
 				<?php $search = Minz_Request::param('search', ''); ?>
@@ -76,14 +79,14 @@ if (Minz_Configuration::canLogIn()) {
 				<li class="item"><a href="<?php echo _url('index', 'logs'); ?>"><?php echo _t('gen.menu.logs'); ?></a></li>
 				<li class="item"><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('gen.menu.about'); ?></a></li>
 				<?php
-				if (Minz_Configuration::canLogIn()) {
+				if (FreshRSS_Auth::accessNeedLogin()) {
 					?><li class="separator"></li>
 				<li class="item"><a class="signout" href="<?php echo _url('auth', 'logout'); ?>"><?php echo _i('logout'), ' ', _t('gen.auth.logout'); ?></a></li><?php
 				} ?>
 			</ul>
 		</div>
 	</div>
-	<?php } elseif (Minz_Configuration::canLogIn()) { ?>
+	<?php } elseif (FreshRSS_Auth::accessNeedLogin()) { ?>
 	<div class="item configure">
 		<?php echo _i('login'); ?><a class="signin" href="<?php echo _url('auth', 'login'); ?>"><?php echo _t('gen.auth.login'); ?></a>
 	</div>

+ 2 - 1
app/layout/layout.phtml

@@ -10,6 +10,7 @@
 <?php $this->renderHelper('javascript_vars'); ?>
 		//]]></script>
 <?php
+	$conf = Minz_Configuration::get('system');
 	$url_base = Minz_Request::currentRequest();
 	if (FreshRSS_Context::$next_id !== '') {
 		$url_next = $url_base;
@@ -34,7 +35,7 @@
 		<link rel="apple-touch-icon" href="<?php echo Minz_Url::display('/themes/icons/apple-touch-icon.png'); ?>">
 		<meta name="apple-mobile-web-app-capable" content="yes" />
 		<meta name="apple-mobile-web-app-status-bar-style" content="black" />
-		<meta name="apple-mobile-web-app-title" content="<?php echo Minz_Configuration::title(); ?>">
+		<meta name="apple-mobile-web-app-title" content="<?php echo $conf->general['title'] ?>">
 		<meta name="msapplication-TileColor" content="#FFF" />
 		<meta name="robots" content="noindex,nofollow" />
 	</head>

+ 2 - 1
app/views/helpers/javascript_vars.phtml

@@ -1,6 +1,7 @@
 "use strict";
 <?php
 
+$conf = Minz_Configuration::get('system');
 $mark = FreshRSS_Context::$conf->mark_when;
 $mail = Minz_Session::param('mail', false);
 $auto_actualize = Minz_Session::param('actualize_feeds', false);
@@ -29,7 +30,7 @@ echo 'var context={',
 	'does_lazyload:', FreshRSS_Context::$conf->lazyload ? 'true' : 'false', ',',
 	'sticky_post:', FreshRSS_Context::isStickyPostEnabled() ? 'true' : 'false', ',',
 	'html5_notif_timeout:', FreshRSS_Context::$conf->html5_notif_timeout, ',',
-	'auth_type:"', Minz_Configuration::authType(), '",',
+	'auth_type:"', $conf->general['auth_type'], '",',
 	'current_user_mail:', $mail ? ('"' . $mail . '"') : 'null', ',',
 	'current_view:"', Minz_Request::param('output', 'normal'), '"',
 "},\n";

+ 32 - 0
data/config.default.php

@@ -0,0 +1,32 @@
+<?php
+
+return array(
+	'general' => array(
+		'environment' => 'production',
+		'salt' => '',
+		'base_url' => '',
+		'language' => 'en',
+		'title' => 'FreshRSS',
+		'default_user' => '_',
+		'allow_anonymous' => false,
+		'allow_anonymous_refresh' => false,
+		'auth_type' => 'none',
+		'api_enabled' => false,
+		'unsafe_autologin_enabled' => false,
+	),
+	'limits' => array(
+		'cache_duration' => 800,
+		'timeout' => 10,
+		'max_inactivity' => PHP_INT_MAX,
+		'max_feeds' => 16384,
+		'max_categories' => 16384,
+	),
+	'db' => array(
+		'type' => 'sqlite',
+		'host' => '',
+		'user' => '',
+		'password' => '',
+		'base' => '',
+		'prefix' => '',
+	),
+);

+ 66 - 0
data/users/_/config.default.php

@@ -0,0 +1,66 @@
+<?php
+
+return array (
+	'language' => 'en',
+	'old_entries' => 3,
+	'keep_history_default' => 0,
+	'ttl_default' => 3600,
+	'mail_login' => '',
+	'token' => '',
+	'passwordHash' => '',
+	'apiPasswordHash' => '',
+	'posts_per_page' => 20,
+	'view_mode' => 'normal',
+	'default_view' => 'adaptive',
+	'default_state' => FreshRSS_Entry::STATE_NOT_READ,
+	'auto_load_more' => true,
+	'display_posts' => false,
+	'display_categories' => false,
+	'hide_read_feeds' => true,
+	'onread_jump_next' => true,
+	'lazyload' => true,
+	'sticky_post' => true,
+	'reading_confirm' => false,
+	'auto_remove_article' => false,
+	'sort_order' => 'DESC',
+	'anon_access' => false,
+	'mark_when' => array (
+		'article' => true,
+		'site' => true,
+		'scroll' => false,
+		'reception' => false,
+	),
+	'theme' => 'Origine',
+	'content_width' => 'thin',
+	'shortcuts' => array (
+		'mark_read' => 'r',
+		'mark_favorite' => 'f',
+		'go_website' => 'space',
+		'next_entry' => 'j',
+		'prev_entry' => 'k',
+		'first_entry' => 'home',
+		'last_entry' => 'end',
+		'collapse_entry' => 'c',
+		'load_more' => 'm',
+		'auto_share' => 's',
+		'focus_search' => 'a',
+		'user_filter' => 'u',
+		'help' => 'f1',
+		'close_dropdown' => 'escape',
+	),
+	'topline_read' => true,
+	'topline_favorite' => true,
+	'topline_date' => true,
+	'topline_link' => true,
+	'bottomline_read' => true,
+	'bottomline_favorite' => true,
+	'bottomline_sharing' => true,
+	'bottomline_tags' => true,
+	'bottomline_date' => true,
+	'bottomline_link' => true,
+	'sharing' => array (
+	),
+	'queries' => array (
+	),
+	'html5_notif_timeout' => 0,
+);

+ 0 - 9
lib/Minz/BadConfigurationException.php

@@ -1,9 +0,0 @@
-<?php
-class Minz_BadConfigurationException extends Minz_Exception {
-	public function __construct ($part_missing, $code = self::ERROR) {
-		$message = '`' . $part_missing
-		         . '` in the configuration file is missing or is misconfigured';
-		
-		parent::__construct ($message, $code);
-	}
-}

+ 145 - 372
lib/Minz/Configuration.php

@@ -1,419 +1,192 @@
 <?php
-/**
- * MINZ - Copyright 2011 Marien Fressinaud
- * Sous licence AGPL3 <http://www.gnu.org/licenses/>
-*/
 
 /**
- * La classe Configuration permet de gérer la configuration de l'application
+ * Manage configuration for the application.
  */
 class Minz_Configuration {
-	const CONF_PATH_NAME = '/config.php';
-
 	/**
-	 * VERSION est la version actuelle de MINZ
+	 * The list of configurations.
 	 */
-	const VERSION = '1.3.1.freshrss';  // version spéciale FreshRSS
+	private static $config_list = array();
 
 	/**
-	 * valeurs possibles pour l'"environment"
-	 * SILENT rend l'application muette (pas de log)
-	 * PRODUCTION est recommandée pour une appli en production
-	 *			(log les erreurs critiques)
-	 * DEVELOPMENT log toutes les erreurs
+	 * Add a new configuration to the list of configuration.
+	 *
+	 * @param $namespace the name of the current configuration
+	 * @param $config_filename the filename of the configuration
+	 * @param $default_filename a filename containing default values for the configuration
+	 * @throws Minz_ConfigurationNamespaceException if the namespace already exists.
 	 */
-	const SILENT = 0;
-	const PRODUCTION = 1;
-	const DEVELOPMENT = 2;
+	public static function register($namespace, $config_filename, $default_filename = null) {
+		if (isset(self::$config_list[$namespace])) {
+			throw new Minz_ConfigurationNamespaceException(
+				$namespace . ' namespace already exists'
+			);
+		}
+
+		self::$config_list[$namespace] = new Minz_Configuration(
+			$namespace, $config_filename, $default_filename
+		);
+	}
 
 	/**
-	 * définition des variables de configuration
-	 * $salt une chaîne de caractères aléatoires (obligatoire)
-	 * $environment gère le niveau d'affichage pour log et erreurs
-	 * $base_url le chemin de base pour accéder à l'application
-	 * $title le nom de l'application
-	 * $language la langue par défaut de l'application
-	 * $db paramètres pour la base de données (tableau)
-	 *     - host le serveur de la base
-	 *     - user nom d'utilisateur
-	 *     - password mot de passe de l'utilisateur
-	 *     - base le nom de la base de données
+	 * Parse a file and return its data.
+	 *
+	 * If the file does not contain a valid PHP code returning an array, an
+	 * empty array is returned anyway.
+	 *
+	 * @param $filename the name of the file to parse.
+	 * @return an array of values
+	 * @throws Minz_FileNotExistException if the file does not exist.
 	 */
-	private static $salt = '';
-	private static $environment = Minz_Configuration::PRODUCTION;
-	private static $base_url = '';
-	private static $title = '';
-	private static $language = 'en';
-	private static $default_user = '';
-	private static $allow_anonymous = false;
-	private static $allow_anonymous_refresh = false;
-	private static $auth_type = 'none';
-	private static $api_enabled = false;
-	private static $unsafe_autologin_enabled = false;
-
-	private static $db = array (
-		'type' => 'mysql',
-		'host' => '',
-		'user' => '',
-		'password' => '',
-		'base' => '',
-		'prefix' => '',
-	);
-
-	const MAX_SMALL_INT = 16384;
-	private static $limits = array(
-		'cache_duration' => 800,	//SimplePie cache duration in seconds
-		'timeout' => 10,	//SimplePie timeout in seconds
-		'max_inactivity' => PHP_INT_MAX,	//Time in seconds after which a user who has not used the account is considered inactive (no auto-refresh of feeds).
-		'max_feeds' => Minz_Configuration::MAX_SMALL_INT,
-		'max_categories' => Minz_Configuration::MAX_SMALL_INT,
-	);
+	public static function parseFile($filename) {
+		if (!file_exists($filename)) {
+			throw new Minz_FileNotExistException($filename);
+		}
 
-	/*
-	 * Getteurs
-	 */
-	public static function salt () {
-		return self::$salt;
+		$data = @include($filename);
+		if (is_array($data)) {
+			return $data;
+		} else {
+			return array();
+		}
 	}
-	public static function environment ($str = false) {
-		$env = self::$environment;
 
-		if ($str) {
-			switch (self::$environment) {
-			case self::SILENT:
-				$env = 'silent';
-				break;
-			case self::DEVELOPMENT:
-				$env = 'development';
-				break;
-			case self::PRODUCTION:
-			default:
-				$env = 'production';
-			}
+	/**
+	 * Return the configuration related to a given namespace.
+	 *
+	 * @param $namespace the name of the configuration to get.
+	 * @return a Minz_Configuration object
+	 * @throws Minz_ConfigurationNamespaceException if the namespace does not exist.
+	 */
+	public static function get($namespace) {
+		if (!isset(self::$config_list[$namespace])) {
+			throw new Minz_ConfigurationNamespaceException(
+				$namespace . ' namespace does not exist'
+			);
 		}
 
-		return $env;
-	}
-	public static function baseUrl () {
-		return self::$base_url;
-	}
-	public static function title () {
-		return self::$title;
-	}
-	public static function language () {
-		return self::$language;
-	}
-	public static function dataBase () {
-		return self::$db;
-	}
-	public static function limits() {
-		return self::$limits;
-	}
-	public static function defaultUser () {
-		return self::$default_user;
-	}
-	public static function allowAnonymous() {
-		return self::$allow_anonymous;
-	}
-	public static function allowAnonymousRefresh() {
-		return self::$allow_anonymous_refresh;
-	}
-	public static function authType() {
-		return self::$auth_type;
-	}
-	public static function needsLogin() {
-		return self::$auth_type !== 'none';
-	}
-	public static function canLogIn() {
-		return self::$auth_type === 'form' || self::$auth_type === 'persona';
-	}
-	public static function apiEnabled() {
-		return self::$api_enabled;
-	}
-	public static function unsafeAutologinEnabled() {
-		return self::$unsafe_autologin_enabled;
+		return self::$config_list[$namespace];
 	}
 
-	public static function _allowAnonymous($allow = false) {
-		self::$allow_anonymous = ((bool)$allow) && self::canLogIn();
-	}
-	public static function _allowAnonymousRefresh($allow = false) {
-		self::$allow_anonymous_refresh = ((bool)$allow) && self::allowAnonymous();
-	}
-	public static function _authType($value) {
-		$value = strtolower($value);
-		switch ($value) {
-			case 'form':
-			case 'http_auth':
-			case 'persona':
-			case 'none':
-				self::$auth_type = $value;
-				break;
-		}
-		self::_allowAnonymous(self::$allow_anonymous);
-	}
+	/**
+	 * The namespace of the current configuration.
+	 */
+	private $namespace = '';
 
-	public static function _enableApi($value = false) {
-		self::$api_enabled = (bool)$value;
-	}
-	public static function _enableAutologin($value = false) {
-		self::$unsafe_autologin_enabled = (bool)$value;
-	}
+	/**
+	 * The filename for the current configuration.
+	 */
+	private $config_filename = '';
+
+	/**
+	 * The filename for the current default values, null by default.
+	 */
+	private $default_filename = null;
+
+	/**
+	 * The configuration values, an empty array by default.
+	 */
+	private $data = array();
+
+	/**
+	 * The default values, an empty array by default.
+	 */
+	private $data_default = array();
 
 	/**
-	 * Initialise les variables de configuration
-	 * @exception Minz_FileNotExistException si le CONF_PATH_NAME n'existe pas
-	 * @exception Minz_BadConfigurationException si CONF_PATH_NAME mal formaté
+	 * Create a new Minz_Configuration object.
+	 * 
+	 * @param $namespace the name of the current configuration.
+	 * @param $config_filename the file containing configuration values.
+	 * @param $default_filename the file containing default values, null by default.
 	 */
-	public static function init () {
+	private function __construct($namespace, $config_filename, $default_filename = null) {
+		$this->namespace = $namespace;
+		$this->config_filename = $config_filename;
+
 		try {
-			self::parseFile ();
-			self::setReporting ();
+			$this->data = self::parseFile($this->config_filename);
 		} catch (Minz_FileNotExistException $e) {
-			throw $e;
-		} catch (Minz_BadConfigurationException $e) {
-			throw $e;
+			if (is_null($default_filename)) {
+				throw $e;
+			}
 		}
-	}
 
-	public static function writeFile() {
-		$ini_array = array(
-			'general' => array(
-				'environment' => self::environment(true),
-				'salt' => self::$salt,
-				'base_url' => self::$base_url,
-				'title' => self::$title,
-				'default_user' => self::$default_user,
-				'allow_anonymous' => self::$allow_anonymous,
-				'allow_anonymous_refresh' => self::$allow_anonymous_refresh,
-				'auth_type' => self::$auth_type,
-				'api_enabled' => self::$api_enabled,
-				'unsafe_autologin_enabled' => self::$unsafe_autologin_enabled,
-			),
-			'limits' => self::$limits,
-			'db' => self::$db,
-		);
-		@rename(DATA_PATH . self::CONF_PATH_NAME, DATA_PATH . self::CONF_PATH_NAME . '.bak.php');
-		$result = file_put_contents(DATA_PATH . self::CONF_PATH_NAME, "<?php\n return " . var_export($ini_array, true) . ';');
-		if (function_exists('opcache_invalidate')) {
-			opcache_invalidate(DATA_PATH . self::CONF_PATH_NAME);	//Clear PHP 5.5+ cache for include
+		$this->default_filename = $default_filename;
+		if (!is_null($this->default_filename)) {
+			$this->data_default = self::parseFile($this->default_filename);
 		}
-		return (bool)$result;
 	}
 
 	/**
-	 * Parse un fichier de configuration
-	 * @exception Minz_PermissionDeniedException si le CONF_PATH_NAME n'est pas accessible
-	 * @exception Minz_BadConfigurationException si CONF_PATH_NAME mal formaté
+	 * Return the value of the given param.
+	 * 
+	 * @param $key the name of the param.
+	 * @param $default default value to return if key does not exist.
+	 * @return the value corresponding to the key.
+	 * @throws Minz_ConfigurationParamException if the param does not exist
 	 */
-	private static function parseFile () {
-		$ini_array = include(DATA_PATH . self::CONF_PATH_NAME);
-
-		if (!is_array($ini_array)) {
-			throw new Minz_PermissionDeniedException (
-				DATA_PATH . self::CONF_PATH_NAME,
-				Minz_Exception::ERROR
+	public function param($key, $default = null) {
+		if (isset($this->data[$key])) {
+			return $this->data[$key];
+		} elseif (!is_null($default)) {
+			return $default;
+		} elseif (isset($this->data_default[$key])) {
+			return $this->data_default[$key];
+		} else {
+			throw new Minz_ConfigurationParamException(
+				$key . ' param does not exist'
 			);
 		}
+	}
 
-		// [general] est obligatoire
-		if (!isset ($ini_array['general'])) {
-			throw new Minz_BadConfigurationException (
-				'[general]',
-				Minz_Exception::ERROR
-			);
-		}
-		$general = $ini_array['general'];
+	/**
+	 * A wrapper for param().
+	 */
+	public function __get($key) {
+		return $this->param($key);
+	}
 
-		// salt est obligatoire
-		if (!isset ($general['salt'])) {
-			if (isset($general['sel_application'])) {	//v0.6
-				$general['salt'] = $general['sel_application'];
-			} else {
-				throw new Minz_BadConfigurationException (
-					'salt',
-					Minz_Exception::ERROR
-				);
-			}
+	/**
+	 * Set or remove a param.
+	 *
+	 * @param $key the param name to set.
+	 * @param $value the value to set. If null, the key is removed from the configuration.
+	 */
+	public function _param($key, $value = null) {
+		if (isset($this->data[$key]) && is_null($value)) {
+			unset($this->data[$key]);
+		} else {
+			$this->data[$key] = $value;
 		}
-		self::$salt = $general['salt'];
-
-		if (isset ($general['environment'])) {
-			switch ($general['environment']) {
-			case 'silent':
-				self::$environment = Minz_Configuration::SILENT;
-				break;
-			case 'development':
-				self::$environment = Minz_Configuration::DEVELOPMENT;
-				break;
-			case 'production':
-				self::$environment = Minz_Configuration::PRODUCTION;
-				break;
-			default:
-				if ($general['environment'] >= 0 &&
-					$general['environment'] <= 2) {
-					// fallback 0.7-beta
-					self::$environment = $general['environment'];
-				} else {
-					throw new Minz_BadConfigurationException (
-						'environment',
-						Minz_Exception::ERROR
-					);
-				}
-			}
+	}
 
-		}
-		if (isset ($general['base_url'])) {
-			self::$base_url = $general['base_url'];
-		}
+	/**
+	 * A wrapper for _param().
+	 */
+	public function __set($key, $value) {
+		$this->_param($key, $value);
+	}
 
-		if (isset ($general['title'])) {
-			self::$title = $general['title'];
-		}
-		if (isset ($general['language'])) {
-			self::$language = $general['language'];
-		}
-		if (isset ($general['default_user'])) {
-			self::$default_user = $general['default_user'];
-		}
-		if (isset ($general['auth_type'])) {
-			self::_authType($general['auth_type']);
-		}
-		if (isset ($general['allow_anonymous'])) {
-			self::$allow_anonymous = (
-				((bool)($general['allow_anonymous'])) &&
-				($general['allow_anonymous'] !== 'no')
-			);
-		}
-		if (isset ($general['allow_anonymous_refresh'])) {
-			self::$allow_anonymous_refresh = (
-				((bool)($general['allow_anonymous_refresh'])) &&
-				($general['allow_anonymous_refresh'] !== 'no')
-			);
-		}
-		if (isset ($general['api_enabled'])) {
-			self::$api_enabled = (
-				((bool)($general['api_enabled'])) &&
-				($general['api_enabled'] !== 'no')
-			);
-		}
-		if (isset ($general['unsafe_autologin_enabled'])) {
-			self::$unsafe_autologin_enabled = (
-				((bool)($general['unsafe_autologin_enabled'])) &&
-				($general['unsafe_autologin_enabled'] !== 'no')
-			);
-		}
+	/**
+	 * Save the current configuration in the configuration file.
+	 */
+	public function save() {
+		$back_filename = $this->config_filename . '.bak.php';
+		@rename($this->config_filename, $back_filename);
 
-		if (isset($ini_array['limits'])) {
-			$limits = $ini_array['limits'];
-			if (isset($limits['cache_duration'])) {
-				$v = intval($limits['cache_duration']);
-				if ($v > 0) {
-					self::$limits['cache_duration'] = $v;
-				}
-			}
-			if (isset($limits['timeout'])) {
-				$v = intval($limits['timeout']);
-				if ($v > 0) {
-					self::$limits['timeout'] = $v;
-				}
-			}
-			if (isset($limits['max_inactivity'])) {
-				$v = intval($limits['max_inactivity']);
-				if ($v > 0) {
-					self::$limits['max_inactivity'] = $v;
-				}
-			}
-			if (isset($limits['max_feeds'])) {
-				$v = intval($limits['max_feeds']);
-				if ($v > 0 && $v < Minz_Configuration::MAX_SMALL_INT) {
-					self::$limits['max_feeds'] = $v;
-				}
-			}
-			if (isset($limits['max_categories'])) {
-				$v = intval($limits['max_categories']);
-				if ($v > 0 && $v < Minz_Configuration::MAX_SMALL_INT) {
-					self::$limits['max_categories'] = $v;
-				}
-			}
+		if (file_put_contents($this->config_filename,
+		                      "<?php\nreturn " . var_export($this->data, true) . ';',
+		                      LOCK_EX) === false) {
+			return false;
 		}
 
-		// Base de données
-		if (isset ($ini_array['db'])) {
-			$db = $ini_array['db'];
-			if (empty($db['type'])) {
-				throw new Minz_BadConfigurationException (
-					'type',
-					Minz_Exception::ERROR
-				);
-			}
-			switch ($db['type']) {
-				case 'mysql':
-					if (empty($db['host'])) {
-						throw new Minz_BadConfigurationException (
-							'host',
-							Minz_Exception::ERROR
-						);
-					}
-					if (empty($db['user'])) {
-						throw new Minz_BadConfigurationException (
-							'user',
-							Minz_Exception::ERROR
-						);
-					}
-					if (!isset($db['password'])) {
-						throw new Minz_BadConfigurationException (
-							'password',
-							Minz_Exception::ERROR
-						);
-					}
-					if (empty($db['base'])) {
-						throw new Minz_BadConfigurationException (
-							'base',
-							Minz_Exception::ERROR
-						);
-					}
-					self::$db['host'] = $db['host'];
-					self::$db['user'] = $db['user'];
-					self::$db['password'] = $db['password'];
-					self::$db['base'] = $db['base'];
-					if (isset($db['prefix'])) {
-						self::$db['prefix'] = $db['prefix'];
-					}
-					break;
-				case 'sqlite':
-					self::$db['host'] = '';
-					self::$db['user'] = '';
-					self::$db['password'] = '';
-					self::$db['base'] = '';
-					self::$db['prefix'] = '';
-					break;
-				default:
-					throw new Minz_BadConfigurationException (
-						'type',
-						Minz_Exception::ERROR
-					);
-					break;
-			}
-			self::$db['type'] = $db['type'];
+		// Clear PHP 5.5+ cache for include
+		if (function_exists('opcache_invalidate')) {
+			opcache_invalidate($this->config_filename);
 		}
-	}
 
-	private static function setReporting() {
-		switch (self::$environment) {
-			case self::PRODUCTION:
-				error_reporting(E_ALL);
-				ini_set('display_errors','Off');
-				ini_set('log_errors', 'On');
-				break;
-			case self::DEVELOPMENT:
-				error_reporting(E_ALL);
-				ini_set('display_errors','On');
-				ini_set('log_errors', 'On');
-				break;
-			case self::SILENT:
-				error_reporting(0);
-				break;
-		}
+		return true;
 	}
 }

+ 8 - 0
lib/Minz/ConfigurationException.php

@@ -0,0 +1,8 @@
+<?php
+
+class Minz_ConfigurationException extends Minz_Exception {
+	public function __construct($error, $code = self::ERROR) {
+		$message = 'Configuration error: ' . $error;
+		parent::__construct($message, $code);
+	}
+}

+ 4 - 0
lib/Minz/ConfigurationNamespaceException.php

@@ -0,0 +1,4 @@
+<?php
+
+class Minz_ConfigurationNamespaceException extends Minz_ConfigurationException {
+}

+ 4 - 0
lib/Minz/ConfigurationParamException.php

@@ -0,0 +1,4 @@
+<?php
+
+class Minz_ConfigurationParamException extends Minz_ConfigurationException {
+}

+ 4 - 3
lib/Minz/Error.php

@@ -82,7 +82,8 @@ class Minz_Error {
 	 *       > en fonction de l'environment
 	 */
 	private static function processLogs ($logs) {
-		$env = Minz_Configuration::environment ();
+		$conf = Minz_Configuration::get('system');
+		$env = $conf->general['environment'];
 		$logs_ok = array ();
 		$error = array ();
 		$warning = array ();
@@ -98,10 +99,10 @@ class Minz_Error {
 			$notice = $logs['notice'];
 		}
 
-		if ($env == Minz_Configuration::PRODUCTION) {
+		if ($env == 'production') {
 			$logs_ok = $error;
 		}
-		if ($env == Minz_Configuration::DEVELOPMENT) {
+		if ($env == 'development') {
 			$logs_ok = array_merge ($error, $warning, $notice);
 		}
 

+ 24 - 2
lib/Minz/FrontController.php

@@ -31,9 +31,12 @@ class Minz_FrontController {
 	 */
 	public function __construct () {
 		try {
-			Minz_Configuration::init ();
+			Minz_Configuration::register('system',
+			                             DATA_PATH . '/config.php',
+			                             DATA_PATH . '/config.default.php');
+			$this->setReporting();
 
-			Minz_Request::init ();
+			Minz_Request::init();
 
 			$url = $this->buildUrl();
 			$url['params'] = array_merge (
@@ -110,4 +113,23 @@ class Minz_FrontController {
 		}
 		exit ('### Application problem ###<br />'."\n".$txt);
 	}
+
+	private function setReporting() {
+		$conf = Minz_Configuration::get('system');
+		switch($conf->general['environment']) {
+		case 'production':
+			error_reporting(E_ALL);
+			ini_set('display_errors','Off');
+			ini_set('log_errors', 'On');
+			break;
+		case 'development':
+			error_reporting(E_ALL);
+			ini_set('display_errors','On');
+			ini_set('log_errors', 'On');
+			break;
+		case 'silent':
+			error_reporting(0);
+			break;
+		}
+	}
 }

+ 8 - 3
lib/Minz/Log.php

@@ -31,10 +31,15 @@ class Minz_Log {
 	 * @param $file_name fichier de log
 	 */
 	public static function record ($information, $level, $file_name = null) {
-		$env = Minz_Configuration::environment ();
+		try {
+			$conf = Minz_Configuration::get('system');
+			$env = $conf->general['environment'];
+		} catch (Minz_ConfigurationException $e) {
+			$env = 'production';
+		}
 
-		if (! ($env === Minz_Configuration::SILENT
-		       || ($env === Minz_Configuration::PRODUCTION
+		if (! ($env === 'silent'
+		       || ($env === 'production'
 		       && ($level >= Minz_Log::NOTICE)))) {
 			if ($file_name === null) {
 				$file_name = join_path(USERS_PATH, Minz_Session::param('currentUser', '_'), 'log.txt');

+ 2 - 1
lib/Minz/ModelPdo.php

@@ -44,7 +44,8 @@ class Minz_ModelPdo {
 			return;
 		}
 
-		$db = Minz_Configuration::dataBase();
+		$conf = Minz_Configuration::get('system');
+		$db = $conf->db;
 
 		if ($currentUser === null) {
 			$currentUser = Minz_Session::param('currentUser', '_');

+ 2 - 1
lib/Minz/Request.php

@@ -96,7 +96,8 @@ class Minz_Request {
 	 * @return la base de l'url
 	 */
 	public static function getBaseUrl() {
-		$defaultBaseUrl = Minz_Configuration::baseUrl();
+		$conf = Minz_Configuration::get('system');
+		$defaultBaseUrl = $conf->general['base_url'];
 		if (!empty($defaultBaseUrl)) {
 			return $defaultBaseUrl;
 		} elseif (isset($_SERVER['REQUEST_URI'])) {

+ 2 - 1
lib/Minz/Translate.php

@@ -28,7 +28,8 @@ class Minz_Translate {
 	 * Load $lang_name and $lang_path based on configuration and selected language.
 	 */
 	public static function init() {
-		$l = Minz_Configuration::language();
+		$conf = Minz_Configuration::get('system');
+		$l = $conf->general['language'];
 		self::$lang_name = Minz_Session::param('language', $l);
 		self::$lang_path = APP_PATH . '/i18n/' . self::$lang_name . '/';
 	}

+ 3 - 1
lib/Minz/View.php

@@ -28,7 +28,9 @@ class Minz_View {
 	public function __construct () {
 		$this->change_view(Minz_Request::controllerName(),
 		                   Minz_Request::actionName());
-		self::$title = Minz_Configuration::title ();
+
+		$conf = Minz_Configuration::get('system');
+		self::$title = $conf->general['title'];
 	}
 
 	/**

+ 1 - 1
p/i/index.php

@@ -33,7 +33,7 @@ if (file_exists(DATA_PATH . '/do-install.txt')) {
 		$currentUser = Minz_Session::param('currentUser', '');
 		$dateLastModification = $currentUser === '' ? time() : max(
 			@filemtime(join_path(USERS_PATH, $currentUser, 'log.txt')),
-			@filemtime(join_path(DATA_PATH . 'config.php'))
+			@filemtime(join_path(DATA_PATH, 'config.php'))
 		);
 		if (httpConditional($dateLastModification, 0, 0, false, PHP_COMPRESSION, true)) {
 			exit();	//No need to send anything