Jelajahi Sumber

Début de mode multi-utilisateur avec http_auth

+ Légère optimisation de Minz_View.
+ Encore plus de tests de bibliothèques dans install.php
Contribue à https://github.com/marienfressinaud/FreshRSS/issues/126 et
https://github.com/marienfressinaud/FreshRSS/issues/303
Alexandre Alapetite 12 tahun lalu
induk
melakukan
92efd68a3a

+ 1 - 1
README.md

@@ -21,7 +21,7 @@ Privilégiez pour cela des demandes sur GitHub
 # Pré-requis
 * Serveur Apache2 ou Nginx (non testé sur les autres)
 * PHP 5.2+ (PHP 5.3.3+ recommandé)
- * Requis : [LibXML](http://php.net/xml), [PCRE](http://php.net/pcre), [cURL](http://php.net/curl), [PDO_MySQL](http://php.net/pdo-mysql)
+ * Requis : [PDO_MySQL](http://php.net/pdo-mysql), [cURL](http://php.net/curl), [LibXML](http://php.net/xml), [PCRE](http://php.net/pcre), [ctype](http://php.net/ctype)
  * Recommandés : [JSON](http://php.net/json), [zlib](http://php.net/zlib), [mbstring](http://php.net/mbstring), [iconv](http://php.net/iconv)
 * MySQL 5.0.3+ (ou SQLite 3.7.4+ à venir)
 * Un navigateur Web récent tel Firefox, Chrome, Opera, Safari, Internet Explorer 9+

+ 1 - 4
app/Controllers/configureController.php

@@ -2,7 +2,7 @@
 
 class FreshRSS_configure_Controller extends Minz_ActionController {
 	public function firstAction () {
-		if (login_is_conf ($this->view->conf) && !is_logged ()) {
+		if (!$this->view->loginOk) {
 			Minz_Error::error (
 				403,
 				array ('error' => array (Minz_Translate::t ('access_denied')))
@@ -16,7 +16,6 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 	public function categorizeAction () {
 		$feedDAO = new FreshRSS_FeedDAO ();
 		$catDAO = new FreshRSS_CategoryDAO ();
-		$catDAO->checkDefault ();
 		$defaultCategory = $catDAO->getDefault ();
 		$defaultId = $defaultCategory->id ();
 
@@ -167,8 +166,6 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			$this->view->conf->_bottomline_link(Minz_Request::param('bottomline_link', false));
 			$this->view->conf->save();
 
-			Minz_Session::_param ('mail', $this->view->conf->mail_login);
-
 			Minz_Session::_param ('language', $this->view->conf->language);
 			Minz_Translate::reset ();
 

+ 2 - 2
app/Controllers/entryController.php

@@ -2,7 +2,7 @@
 
 class FreshRSS_entry_Controller extends Minz_ActionController {
 	public function firstAction () {
-		if (login_is_conf ($this->view->conf) && !is_logged ()) {
+		if (!$this->view->loginOk) {
 			Minz_Error::error (
 				403,
 				array ('error' => array (Minz_Translate::t ('access_denied')))
@@ -38,7 +38,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
 		$nextGet = Minz_Request::param ('nextGet', $get); 
 		$idMax = Minz_Request::param ('idMax', 0);
 
-		$is_read = !!$is_read;
+		$is_read = (bool)$is_read;
 
 		$entryDAO = new FreshRSS_EntryDAO ();
 		if ($id == false) {

+ 12 - 15
app/Controllers/feedController.php

@@ -2,18 +2,17 @@
 
 class FreshRSS_feed_Controller extends Minz_ActionController {
 	public function firstAction () {
-		$token = $this->view->conf->token;
-		$token_param = Minz_Request::param ('token', '');
-		$token_is_ok = ($token != '' && $token == $token_param);
-		$action = Minz_Request::actionName ();
-
-		if (login_is_conf ($this->view->conf) &&
-				!is_logged () &&
-				!($token_is_ok && $action == 'actualize')) {
-			Minz_Error::error (
-				403,
-				array ('error' => array (Minz_Translate::t ('access_denied')))
-			);
+		if (!$this->view->loginOk) {
+			$token = $this->view->conf->token;	//TODO: check the token logic again, and if it is still needed
+			$token_param = Minz_Request::param ('token', '');
+			$token_is_ok = ($token != '' && $token == $token_param);
+			$action = Minz_Request::actionName ();
+			if (!($token_is_ok && $action === 'actualize')) {
+				Minz_Error::error (
+					403,
+					array ('error' => array (Minz_Translate::t ('access_denied')))
+				);
+			}
 		}
 
 		$this->catDAO = new FreshRSS_CategoryDAO ();
@@ -411,10 +410,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 	}
 
 	private function addCategories ($categories) {
-		$catDAO = new FreshRSS_CategoryDAO ();
-
 		foreach ($categories as $cat) {
-			if (!$catDAO->searchByName ($cat->name ())) {
+			if (!$this->catDAO->searchByName ($cat->name ())) {
 				$values = array (
 					'id' => $cat->id (),
 					'name' => $cat->name (),

+ 14 - 17
app/Controllers/indexController.php

@@ -16,17 +16,18 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 
 	public function indexAction () {
 		$output = Minz_Request::param ('output');
-
-		$token = $this->view->conf->token;
-		$token_param = Minz_Request::param ('token', '');
-		$token_is_ok = ($token != '' && $token === $token_param);
-
-		// check if user is log in
-		if(login_is_conf ($this->view->conf) &&
-				!is_logged() &&
-				!Minz_Configuration::allowAnonymous() &&
-				!($output === 'rss' && $token_is_ok)) {
-			return;
+		$token = '';
+
+		// check if user is logged in
+		if (!$this->view->loginOk && !Minz_Configuration::allowAnonymous())
+		{
+			$token = $this->view->conf->token;
+			$token_param = Minz_Request::param ('token', '');
+			$token_is_ok = ($token != '' && $token === $token_param);
+			if (!($output === 'rss' && $token_is_ok)) {
+				return;
+			}
+			$params['token'] = $token;
 		}
 
 		// construction of RSS url of this feed
@@ -35,11 +36,6 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 		if (isset ($params['search'])) {
 			$params['search'] = urlencode ($params['search']);
 		}
-		if (login_is_conf($this->view->conf) &&
-				!Minz_Configuration::allowAnonymous() &&
-				$token !== '') {
-			$params['token'] = $token;
-		}
 		$this->view->rss_url = array (
 			'c' => 'index',
 			'a' => 'index',
@@ -212,7 +208,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 	}
 
 	public function logsAction () {
-		if (login_is_conf ($this->view->conf) && !is_logged ()) {
+		if (!$this->view->loginOk) {
 			Minz_Error::error (
 				403,
 				array ('error' => array (Minz_Translate::t ('access_denied')))
@@ -255,6 +251,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 		$res = json_decode ($result, true);
 		if ($res['status'] === 'okay' && $res['email'] === $this->view->conf->mail_login) {
 			Minz_Session::_param ('mail', $res['email']);
+			$this->view->loginOk = true;
 			invalidateHttpCache();
 		} else {
 			$res = array ();

+ 43 - 13
app/FreshRSS.php

@@ -1,26 +1,56 @@
 <?php
 class FreshRSS extends Minz_FrontController {
-	public function init () {
-		Minz_Session::init ('FreshRSS');
-
-		$this->loadParamsView ();
-		$this->loadStylesAndScripts ();
-		$this->loadNotifications ();
+	public function init($currentUser = null) {
+		Minz_Session::init('FreshRSS');
+		$this->accessControl($currentUser);
+		$this->loadParamsView();
+		$this->loadStylesAndScripts();
+		$this->loadNotifications();
 	}
 
-	private function loadParamsView () {
+	private function accessControl($currentUser) {
+		if ($currentUser === null) {
+			switch (Minz_Configuration::authType()) {
+				case 'http_auth':
+					$currentUser = httpAuthUser();
+					$loginOk = $currentUser != '';
+					break;
+				case 'persona':
+					$currentUser = Minz_Configuration::defaultUser();
+					$loginOk = Minz_Session::param('mail') != '';
+					break;
+				case 'none':
+					$currentUser = Minz_Configuration::defaultUser();
+					$loginOk = true;
+					break;
+				default:
+					$loginOk = false;
+					break;
+			}
+		} elseif ((PHP_SAPI === 'cli') && (Minz_Request::actionName() === 'actualize')) {	//Command line
+			Minz_Configuration::_authType('none');
+			$loginOk = true;
+		}
+
+		if (!$loginOk || !isValidUser($currentUser)) {
+			$currentUser = Minz_Configuration::defaultUser();
+			$loginOk = false;
+		}
+		Minz_Configuration::_currentUser($currentUser);
+		Minz_View::_param ('loginOk', $loginOk);
+
 		try {
-			$this->conf = new FreshRSS_Configuration();
+			$this->conf = new FreshRSS_Configuration($currentUser);
 		} catch (Minz_Exception $e) {
 			// Permission denied or conf file does not exist
-			// it's critical!
 			die($e->getMessage());
 		}
-
 		Minz_View::_param ('conf', $this->conf);
+	}
+
+	private function loadParamsView () {
 		Minz_Session::_param ('language', $this->conf->language);
 		Minz_Translate::init();
-
 		$output = Minz_Request::param ('output');
 		if (!$output) {
 			$output = $this->conf->view_mode;
@@ -31,12 +61,12 @@ class FreshRSS extends Minz_FrontController {
 	private function loadStylesAndScripts () {
 		$theme = FreshRSS_Themes::get_infos($this->conf->theme);
 		if ($theme) {
-			foreach($theme["files"] as $file) {
+			foreach($theme['files'] as $file) {
 				Minz_View::appendStyle (Minz_Url::display ('/themes/' . $theme['path'] . '/' . $file . '?' . @filemtime(PUBLIC_PATH . '/themes/' . $theme['path'] . '/' . $file)));
 			}
 		}
 
-		if (login_is_conf ($this->conf)) {
+		if (Minz_Configuration::authType() === 'persona') {
 			Minz_View::appendScript ('https://login.persona.org/include.js');
 		}
 		$includeLazyLoad = $this->conf->lazyload && ($this->conf->display_posts || Minz_Request::param ('output') === 'reader');

+ 8 - 7
app/Models/Configuration.php

@@ -59,10 +59,9 @@ class FreshRSS_Configuration extends Minz_ModelArray {
 		'fr' => 'Français',
 	);
 
-	public function __construct ($filename = '') {
-		if (empty($filename)) {
-			$filename = DATA_PATH . '/' . Minz_Configuration::currentUser () . '_user.php';
-		}
+	public function __construct ($user) {
+		$filename = DATA_PATH . '/' . $user . '_user.php';
+
 		parent::__construct($filename);
 		$data = parent::loadArray();
 
@@ -72,6 +71,7 @@ class FreshRSS_Configuration extends Minz_ModelArray {
 				$this->$function($value);
 			}
 		}
+		$this->data['user'] = $user;
 	}
 
 	public function save() {
@@ -151,10 +151,11 @@ class FreshRSS_Configuration extends Minz_ModelArray {
 		}
 	}
 	public function _mail_login ($value) {
-		if (filter_var($value, FILTER_VALIDATE_EMAIL)) {
-			$this->mail_login = $value;
+		$value = filter_var($value, FILTER_VALIDATE_EMAIL);
+		if ($value) {
+			$this->data['mail_login'] = $value;
 		} else {
-			$this->mail_login = '';
+			$this->data['mail_login'] = '';
 		}
 	}
 	public function _anon_access ($value) {

+ 11 - 4
app/actualize_script.php

@@ -1,6 +1,8 @@
 <?php
 require(dirname(__FILE__) . '/../constants.php');
 
+//TODO: check if already running
+
 $_GET['c'] = 'feed';
 $_GET['a'] = 'actualize';
 $_GET['force'] = true;
@@ -9,7 +11,12 @@ $_SERVER['HTTP_HOST'] = '';
 require(LIB_PATH . '/lib_rss.php');	//Includes class autoloader
 
 $front_controller = new FreshRSS ();
-$front_controller->init ();
-Minz_Session::_param('mail', true); // permet de se passer de la phase de connexion
-$front_controller->run ();
-invalidateHttpCache();
+
+$users = listUsers();
+shuffle($users);
+
+foreach ($users as $user) {
+	$front_controller->init($user);
+	$front_controller->run();
+	invalidateHttpCache($user);
+}

+ 3 - 2
app/i18n/en.php

@@ -158,13 +158,14 @@ return array (
 
 	'current_user'			=> 'Current user',
 	'default_user'			=> 'Username of the default user (maximum 16 alphanumeric characters)',
-	'persona_connection_email'	=> 'Login mail address (use <a href="https://persona.org/">Mozilla Persona</a>)',
+	'persona_connection_email'	=> 'Login mail address (for <a href="https://persona.org/" rel="external">Mozilla Persona</a>)',
 	'allow_anonymous'		=> 'Allow anonymous reading for the default user (%s)',
 	'auth_token'			=> 'Authentication token',
-	'explain_token'			=> 'Allows to access RSS output without authentication.<br />%s?token=%s',
+	'explain_token'			=> 'Allows to access RSS output of the default user without authentication.<br /><kbd>%s?token=%s</kbd>',
 	'login_configuration'		=> 'Login',
 	'is_admin'			=> 'is administrator',
 	'auth_type'			=> 'Authentication method',
+	'auth_none'			=> 'None (dangerous)',
 	'users_list'			=> 'List of users',
 
 	'language'			=> 'Language',

+ 3 - 2
app/i18n/fr.php

@@ -158,13 +158,14 @@ return array (
 
 	'current_user'			=> 'Utilisateur actuel',
 	'default_user'			=> 'Nom de l’utilisateur par défaut (16 caractères alphanumériques maximum)',
-	'persona_connection_email'	=> 'Adresse courriel de connexion (utilise <a href="https://persona.org/">Mozilla Persona</a>)',
+	'persona_connection_email'	=> 'Adresse courriel de connexion (pour <a href="https://persona.org/" rel="external">Mozilla Persona</a>)',
 	'allow_anonymous'		=> 'Autoriser la lecture anonyme pour l’utilisateur par défaut (%s)',
 	'auth_token'			=> 'Jeton d’identification',
-	'explain_token'			=> 'Permet d’accéder à la sortie RSS sans besoin de s’authentifier.<br />%s?output=rss&token=%s',
+	'explain_token'			=> 'Permet d’accéder à la sortie RSS de l’utilisateur par défaut sans besoin de s’authentifier.<br /><kbd>%s?output=rss&token=%s</kbd>',
 	'login_configuration'		=> 'Identification',
 	'is_admin'			=> 'est administrateur',
 	'auth_type'			=> 'Méthode d’authentification',
+	'auth_none'			=> 'Aucune (dangereux)',
 	'users_list'			=> 'Liste des utilisateurs',
 
 	'language'			=> 'Langue',

+ 3 - 3
app/layout/aside_flux.phtml

@@ -2,14 +2,14 @@
 	<a class="toggle_aside" href="#close"><?php echo FreshRSS_Themes::icon('close'); ?></a>
 
 	<ul class="categories">
-		<?php if (!login_is_conf ($this->conf) || is_logged ()) { ?>
+		<?php if ($this->loginOk) { ?>
 		<li>
 			<div class="stick">
 				<a class="btn btn-important" href="<?php echo _url ('configure', 'feed'); ?>"><?php echo Minz_Translate::t ('subscription_management'); ?></a>
 				<a class="btn btn-important" href="<?php echo _url ('configure', 'categorize'); ?>" title="<?php echo Minz_Translate::t ('categories_management'); ?>"><?php echo FreshRSS_Themes::icon('category-white'); ?></a>
 			</div>
 		</li>
-		<?php } elseif (login_is_conf ($this->conf)) { ?>
+		<?php } elseif (Minz_Configuration::needsLogin()) { ?>
 		<li><a href="<?php echo _url ('index', 'about'); ?>"><?php echo Minz_Translate::t ('about_freshrss'); ?></a></li>
 		<?php } ?>
 
@@ -69,7 +69,7 @@
 		<li class="dropdown-close"><a href="#close">❌</a></li>
 		<li class="item"><a href="<?php echo _url ('index', 'index', 'get', 'f_!!!!!!'); ?>"><?php echo Minz_Translate::t ('filter'); ?></a></li>
 		<li class="item"><a target="_blank" href="http://example.net/"><?php echo Minz_Translate::t ('see_website'); ?></a></li>
-		<?php if (!login_is_conf ($this->conf) || is_logged ()) { ?>
+		<?php if ($this->loginOk) { ?>
 		<li class="separator"></li>
 		<li class="item"><a href="<?php echo _url ('configure', 'feed', 'id', '!!!!!!'); ?>"><?php echo Minz_Translate::t ('administration'); ?></a></li>
 		<li class="item"><a href="<?php echo _url ('feed', 'actualize', 'id', '!!!!!!'); ?>"><?php echo Minz_Translate::t ('actualize'); ?></a></li>

+ 8 - 13
app/layout/header.phtml

@@ -1,9 +1,9 @@
-<?php if (login_is_conf ($this->conf)) { ?>
+<?php if (Minz_Configuration::canLogIn()) { ?>
 <ul class="nav nav-head nav-login">
-	<?php if (!is_logged ()) { ?>
-	<li class="item"><?php echo FreshRSS_Themes::icon('login'); ?> <a class="signin" href="#"><?php echo Minz_Translate::t ('login'); ?></a></li>
-	<?php } else { ?>
+	<?php if ($this->loginOk) { ?>
 	<li class="item"><?php echo FreshRSS_Themes::icon('logout'); ?> <a class="signout" href="#"><?php echo Minz_Translate::t ('logout'); ?></a></li>
+	<?php } else { ?>
+	<li class="item"><?php echo FreshRSS_Themes::icon('login'); ?> <a class="signin" href="#"><?php echo Minz_Translate::t ('login'); ?></a></li>
 	<?php } ?>
 </ul>
 <?php } ?>
@@ -19,9 +19,7 @@
 	</div>
 
 	<div class="item search">
-		<?php if(!login_is_conf ($this->conf) ||
-		         is_logged() ||
-		         Minz_Configuration::allowAnonymous()) { ?>
+		<?php if ($this->loginOk || Minz_Configuration::allowAnonymous()) { ?>
 		<form action="<?php echo _url ('index', 'index'); ?>" method="get">
 			<div class="stick">
 				<?php $search = Minz_Request::param ('search', ''); ?>
@@ -48,11 +46,10 @@
 		<?php } ?>
 	</div>
 
-	<?php if (!login_is_conf ($this->conf) || is_logged ()) { ?>
+	<?php if ($this->loginOk) { ?>
 	<div class="item configure">
 		<div class="dropdown">
 			<div id="dropdown-configure" class="dropdown-target"></div>
-
 			<a class="btn dropdown-toggle" href="#dropdown-configure"><?php echo FreshRSS_Themes::icon('configure'); ?></a>
 			<ul class="dropdown-menu">
 				<li class="dropdown-close"><a href="#close">❌</a></li>
@@ -65,16 +62,14 @@
 				<li class="separator"></li>
 				<li class="item"><a href="<?php echo _url ('index', 'about'); ?>"><?php echo Minz_Translate::t ('about'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('index', 'logs'); ?>"><?php echo Minz_Translate::t ('logs'); ?></a></li>
-				<?php if (login_is_conf ($this->conf) && is_logged ()) { ?>
+				<?php if (Minz_Configuration::canLogIn()) { ?>
 				<li class="separator"></li>
 				<li class="item"><a class="signout" href="#"><?php echo FreshRSS_Themes::icon('logout'); ?> <?php echo Minz_Translate::t ('logout'); ?></a></li>
 				<?php } ?>
 			</ul>
 		</div>
 	</div>
-	<?php }
-
-	if (login_is_conf ($this->conf) && !is_logged ()) { ?>
+	<?php } elseif (Minz_Configuration::canLogIn()) { ?>
 	<div class="item configure">
 		<?php echo FreshRSS_Themes::icon('login'); ?> <a class="signin" href="#"><?php echo Minz_Translate::t ('login'); ?></a>
 	</div>

+ 1 - 1
app/layout/nav_menu.phtml

@@ -1,7 +1,7 @@
 <div class="nav_menu">
 	<a class="btn toggle_aside" href="#aside_flux"><?php echo FreshRSS_Themes::icon('category'); ?></a>
 
-	<?php if (!login_is_conf ($this->conf) || is_logged ()) { ?>
+	<?php if ($this->loginOk) { ?>
 	<a id="actualize" class="btn" href="<?php echo _url ('feed', 'actualize'); ?>"><?php echo FreshRSS_Themes::icon('refresh'); ?></a>
 
 	<?php

+ 29 - 22
app/views/configure/users.phtml

@@ -22,20 +22,11 @@
 			<label class="group-name" for="mail_login"><?php echo Minz_Translate::t('persona_connection_email'); ?></label>
 			<?php $mail = $this->conf->mail_login; ?>
 			<div class="group-controls">
-				<input type="email" id="mail_login" name="mail_login" value="<?php echo $mail ? $mail : ''; ?>" placeholder="<?php echo Minz_Translate::t('blank_to_disable'); ?>" />
+				<input type="email" id="mail_login" name="mail_login" value="<?php echo $mail ? $mail : ''; ?>" placeholder="alice@example.net" />
 				<noscript><b><?php echo Minz_Translate::t('javascript_should_be_activated'); ?></b></noscript>
 			</div>
 		</div>
 
-		<div class="form-group">
-			<label class="group-name" for="token"><?php echo Minz_Translate::t('auth_token'); ?></label>
-			<?php $token = $this->conf->token; ?>
-			<div class="group-controls">
-				<input type="text" id="token" name="token" value="<?php echo $token; ?>"  placeholder="<?php echo Minz_Translate::t('blank_to_disable'); ?>"/>
-				<?php echo FreshRSS_Themes::icon('help'); ?> <?php echo Minz_Translate::t('explain_token', Minz_Url::display(null, 'html', true), $token); ?>
-			</div>
-		</div>
-
 		<div class="form-group form-actions">
 			<div class="group-controls">
 				<button type="submit" class="btn btn-important"><?php echo Minz_Translate::t('save'); ?></button>
@@ -47,21 +38,12 @@
 
 		<legend><?php echo Minz_Translate::t('users'); ?></legend>
 
-		<div class="form-group">
-			<div class="group-controls">
-				<label class="checkbox" for="anon_access">
-					<input type="checkbox" name="anon_access" id="anon_access" value="1"<?php echo Minz_Configuration::allowAnonymous() ? ' checked="checked"' : ''; ?> />
-					<?php echo Minz_Translate::t('allow_anonymous', Minz_Configuration::defaultUser()); ?>
-				</label>
-			</div>
-		</div>
-
 		<div class="form-group">
 			<label class="group-name" for="auth_type"><?php echo Minz_Translate::t('auth_type'); ?></label>
 			<div class="group-controls">
 				<select id="auth_type" name="auth_type">
-					<option value="none"<?php echo Minz_Configuration::authType() === 'none' ? ' selected="selected"' : ''; ?>>None (dangerous)</option>
-					<option value="http_auth"<?php echo Minz_Configuration::authType() === 'http_auth' ? ' selected="selected"' : ''; ?>>HTTP Auth</option>
+					<option value="none"<?php echo Minz_Configuration::authType() === 'none' ? ' selected="selected"' : ''; ?>><?php echo Minz_Translate::t('auth_none'); ?></option>
+					<option value="http_auth"<?php echo Minz_Configuration::authType() === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : ''; ?>>HTTP Auth</option>
 					<option value="persona"<?php echo Minz_Configuration::authType() === 'persona' ? ' selected="selected"' : ''; ?>>Mozilla Persona</option>
 				</select>
 			</div>
@@ -75,7 +57,32 @@
 						echo '<option>', $user, '</option>';
 					}
 				?></select>
-				(not implemented yet)
+			</div>
+		</div>
+
+		<div class="form-group form-actions">
+			<div class="group-controls">
+				<button type="submit" class="btn btn-important"><?php echo Minz_Translate::t('save'); ?></button>
+				<button type="reset" class="btn"><?php echo Minz_Translate::t('cancel'); ?></button>
+			</div>
+		</div>
+
+		<legend>Mozilla Persona</legend>
+		<div class="form-group">
+			<div class="group-controls">
+				<label class="checkbox" for="anon_access">
+					<input type="checkbox" name="anon_access" id="anon_access" value="1"<?php echo Minz_Configuration::allowAnonymous() ? ' checked="checked"' : ''; ?> />
+					<?php echo Minz_Translate::t('allow_anonymous', Minz_Configuration::defaultUser()); ?>
+				</label>
+			</div>
+		</div>
+
+		<div class="form-group">
+			<label class="group-name" for="token"><?php echo Minz_Translate::t('auth_token'); ?></label>
+			<?php $token = $this->conf->token; ?>
+			<div class="group-controls">
+				<input type="text" id="token" name="token" value="<?php echo $token; ?>"  placeholder="<?php echo Minz_Translate::t('blank_to_disable'); ?>"/>
+				<?php echo FreshRSS_Themes::icon('help'); ?> <?php echo Minz_Translate::t('explain_token', Minz_Url::display(null, 'html', true), $token); ?>
 			</div>
 		</div>
 

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

@@ -29,7 +29,7 @@
 	if ($mail != 'null') {
 		$mail = '"' . $mail . '"';
 	}
-	echo 'use_persona=', login_is_conf ($this->conf) ? 'true' : 'false',
+	echo 'use_persona=', Minz_Configuration::authType() === 'persona' ? 'true' : 'false',
 		',url_freshrss="', _url ('index', 'index'), '",',
 		'url_login="', _url ('index', 'login'), '",',
 		'url_logout="', _url ('index', 'logout'), '",',

+ 33 - 27
app/views/helpers/view/normal_view.phtml

@@ -7,39 +7,48 @@ if (!empty($this->entries)) {
 	$display_today = true;
 	$display_yesterday = true;
 	$display_others = true;
-
-	$logged = !login_is_conf ($this->conf) || is_logged ();
-	$shaarli = $this->conf->sharing ('shaarli');
-	$poche = $this->conf->sharing ('poche');
-	$diaspora = $this->conf->sharing ('diaspora');
+	$shaarli = $this->loginOk && $this->conf->sharing ('shaarli');
+	$poche = $this->loginOk && $this->conf->sharing ('poche');
+	$diaspora = $this->loginOk && $this->conf->sharing ('diaspora');
 	$twitter = $this->conf->sharing ('twitter');
 	$google_plus = $this->conf->sharing ('g+');
 	$facebook = $this->conf->sharing ('facebook');
 	$email = $this->conf->sharing ('email');
 	$print = $this->conf->sharing ('print');
-	$today = $this->today;
 	$hidePosts = !$this->conf->display_posts;
 	$lazyload = $this->conf->lazyload;
+	$topline_read = $this->conf->topline_read;
+	$topline_favorite = $this->conf->topline_favorite;
+	$topline_date = $this->conf->topline_date;
+	$topline_link = $this->conf->topline_link;
+	$bottomline_read = $this->conf->bottomline_read;
+	$bottomline_favorite = $this->conf->bottomline_favorite;
+	$bottomline_sharing = $this->conf->bottomline_sharing && (
+		$shaarli || $poche || $diaspora || $twitter ||
+		$google_plus || $facebook || $email);
+	$bottomline_tags = $this->conf->bottomline_tags;
+	$bottomline_date = $this->conf->bottomline_date;
+	$bottomline_link = $this->conf->bottomline_link;
 ?>
 
 <div id="stream" class="normal<?php echo $hidePosts ? ' hide_posts' : ''; ?>">
 	<?php foreach ($this->entries as $item) { ?>
 
-	<?php if ($display_today && $item->isDay (FreshRSS_Days::TODAY, $today)) { ?>
+	<?php if ($display_today && $item->isDay (FreshRSS_Days::TODAY, $this->today)) { ?>
 	<div class="day" id="day_today">
 		<?php echo Minz_Translate::t ('today'); ?>
 		<span class="date"> - <?php echo timestamptodate (time (), false); ?></span>
 		<span class="name"><?php echo $this->currentName; ?></span>
 	</div>
 	<?php $display_today = false; } ?>
-	<?php if ($display_yesterday && $item->isDay (FreshRSS_Days::YESTERDAY, $today)) { ?>
+	<?php if ($display_yesterday && $item->isDay (FreshRSS_Days::YESTERDAY, $this->today)) { ?>
 	<div class="day" id="day_yesterday">
 		<?php echo Minz_Translate::t ('yesterday'); ?>
 		<span class="date"> - <?php echo timestamptodate (time () - 86400, false); ?></span>
 		<span class="name"><?php echo $this->currentName; ?></span>
 	</div>
 	<?php $display_yesterday = false; } ?>
-	<?php if ($display_others && $item->isDay (FreshRSS_Days::BEFORE_YESTERDAY, $today)) { ?>
+	<?php if ($display_others && $item->isDay (FreshRSS_Days::BEFORE_YESTERDAY, $this->today)) { ?>
 	<div class="day" id="day_before_yesterday">
 		<?php echo Minz_Translate::t ('before_yesterday'); ?>
 		<span class="name"><?php echo $this->currentName; ?></span>
@@ -48,14 +57,14 @@ if (!empty($this->entries)) {
 
 	<div class="flux<?php echo !$item->isRead () ? ' not_read' : ''; ?><?php echo $item->isFavorite () ? ' favorite' : ''; ?>" id="flux_<?php echo $item->id (); ?>">
 		<ul class="horizontal-list flux_header"><?php
-			if ($logged) {
-				if ($this->conf->topline_read) {
+			if ($this->loginOk) {
+				if ($topline_read) {
 					?><li class="item manage"><?php
 						?><a class="read" href="<?php echo _url ('entry', 'read', 'id', $item->id (), 'is_read', $item->isRead () ? 0 : 1); ?>"><?php
 							echo FreshRSS_Themes::icon($item->isRead () ? 'read' : 'unread'); ?></a><?php
 					?></li><?php
 				}
-				if ($this->conf->topline_favorite) {
+				if ($topline_favorite) {
 					 ?><li class="item manage"><?php
 						?><a class="bookmark" href="<?php echo _url ('entry', 'bookmark', 'id', $item->id (), 'is_favorite', $item->isFavorite () ? 0 : 1); ?>"><?php
 							echo FreshRSS_Themes::icon($item->isFavorite () ? 'starred' : 'non-starred'); ?></a><?php
@@ -67,8 +76,8 @@ if (!empty($this->entries)) {
 			?>
 			<li class="item website"><a href="<?php echo _url ('index', 'index', 'get', 'f_' . $feed->id ()); ?>"><img class="favicon" src="<?php echo $feed->favicon (); ?>" alt="✇" /> <span><?php echo $feed->name(); ?></span></a></li>
 			<li class="item title"><a target="_blank" href="<?php echo $item->link (); ?>"><?php echo $item->title (); ?></a></li>
-			<?php if ($this->conf->topline_date) { ?><li class="item date"><?php echo $item->date (); ?> </li><?php } ?>
-			<?php if ($this->conf->topline_link) { ?><li class="item link"><a target="_blank" href="<?php echo $item->link (); ?>"><?php echo FreshRSS_Themes::icon('link'); ?></a></li><?php } ?>
+			<?php if ($topline_date) { ?><li class="item date"><?php echo $item->date (); ?> </li><?php } ?>
+			<?php if ($topline_link) { ?><li class="item link"><a target="_blank" href="<?php echo $item->link (); ?>"><?php echo FreshRSS_Themes::icon('link'); ?></a></li><?php } ?>
 		</ul>
 
 		<div class="flux_content">
@@ -85,14 +94,14 @@ if (!empty($this->entries)) {
 				?>
 			</div>
 			<ul class="horizontal-list bottom"><?php
-				if ($logged) {
-					if ($this->conf->bottomline_read) {
+				if ($this->loginOk) {
+					if ($bottomline_read) {
 						?><li class="item manage"><?php
 							?><a class="read" href="<?php echo _url ('entry', 'read', 'id', $item->id (), 'is_read', $item->isRead () ? 0 : 1); ?>"><?php
 								echo FreshRSS_Themes::icon($item->isRead () ? 'read' : 'unread'); ?></a><?php
 						?></li><?php
 					}
-					if ($this->conf->bottomline_favorite) {
+					if ($bottomline_favorite) {
 						?><li class="item manage"><?php
 							?><a class="bookmark" href="<?php echo _url ('entry', 'bookmark', 'id', $item->id (), 'is_favorite', $item->isFavorite () ? 0 : 1); ?>"><?php
 								echo FreshRSS_Themes::icon($item->isFavorite () ? 'starred' : 'non-starred'); ?></a><?php
@@ -101,10 +110,7 @@ if (!empty($this->entries)) {
 				} ?>
 				<li class="item">
 					<?php
-						if ($this->conf->bottomline_sharing && (
-								$shaarli || $poche || $diaspora || $twitter ||
-								$google_plus || $facebook || $email
-							)) {
+						if ($bottomline_sharing) {
 							$link = urlencode ($item->link ());
 							$title = urlencode ($item->title () . ' - ' . $feed->name ());
 					?>
@@ -117,19 +123,19 @@ if (!empty($this->entries)) {
 
 						<ul class="dropdown-menu">
 							<li class="dropdown-close"><a href="#close">❌</a></li>
-							<?php if ($logged && $shaarli) { ?>
+							<?php if ($shaarli) { ?>
 							<li class="item">
 								<a target="_blank" href="<?php echo $shaarli . '?post=' . $link . '&amp;title=' . $title . '&amp;source=FreshRSS'; ?>">
 									<?php echo Minz_Translate::t ('shaarli'); ?>
 								</a>
 							</li>
-							<?php } if ($logged && $poche) { ?>
+							<?php } if ($poche) { ?>
 							<li class="item">
 								<a target="_blank" href="<?php echo $poche . '?action=add&amp;url=' . base64_encode (urldecode($link)); ?>">
 									<?php echo Minz_Translate::t ('poche'); ?>
 								</a>
 							</li>
-							<?php } if ($logged && $diaspora) { ?>
+							<?php } if ($diaspora) { ?>
 							<li class="item">
 								<a target="_blank" href="<?php echo $diaspora . '/bookmarklet?url=' . $link . '&amp;title=' . $title; ?>">
 									<?php echo Minz_Translate::t ('diaspora'); ?>
@@ -171,7 +177,7 @@ if (!empty($this->entries)) {
 					<?php } ?>
 				</li>
 				<?php
-					$tags = $this->conf->bottomline_tags ? $item->tags() : null;
+					$tags = $bottomline_tags ? $item->tags() : null;
 					if (!empty($tags)) {
 				?>
 				<li class="item">
@@ -190,8 +196,8 @@ if (!empty($this->entries)) {
 					</div>
 				</li>
 				<?php } ?>
-				<?php if ($this->conf->bottomline_date) { ?><li class="item date"><?php echo $item->date (); ?> </li><?php } ?>
-				<?php if ($this->conf->bottomline_link) { ?><li class="item link"><a target="_blank" href="<?php echo $item->link (); ?>"><?php echo FreshRSS_Themes::icon('link'); ?></a></li><?php } ?>
+				<?php if ($bottomline_date) { ?><li class="item date"><?php echo $item->date (); ?> </li><?php } ?>
+				<?php if ($bottomline_link) { ?><li class="item link"><a target="_blank" href="<?php echo $item->link (); ?>"><?php echo FreshRSS_Themes::icon('link'); ?></a></li><?php } ?>
 			</ul>
 		</div>
 	</div>

+ 27 - 18
app/views/index/index.phtml

@@ -1,29 +1,38 @@
 <?php
 
+function showForbidden() {
+?><div class="post content">
+	<h1><?php echo Minz_Translate::t ('forbidden_access'); ?></h1>
+	<p><?php echo Minz_Configuration::canLogIn() ?
+		Minz_Translate::t ('forbidden_access_description') :
+		Minz_Translate::t ('forbidden_access') . ' (' . Minz_Configuration::authType() . ')'; ?></p>
+	<p><a href="<?php echo _url ('index', 'about'); ?>"><?php echo Minz_Translate::t ('about_freshrss'); ?></a></p>
+</div><?php
+}
+
 $output = Minz_Request::param ('output', 'normal');
-$token = $this->conf->token;
-$token_param = Minz_Request::param ('token', '');
-$token_is_ok = ($token != '' && $token == $token_param);
 
-if(!login_is_conf ($this->conf) ||
-   is_logged() ||
-   Minz_Configuration::allowAnonymous() ||
-   ($output == 'rss' && $token_is_ok)) {
-	if($output == 'rss') {
+if ($this->loginOk || Minz_Configuration::allowAnonymous()) {
+	if ($output === 'normal') {
+		$this->renderHelper ('view/normal_view');
+	} elseif ($output === 'rss') {
 		$this->renderHelper ('view/rss_view');
-	} elseif($output == 'reader') {
+	} elseif ($output === 'reader') {
 		$this->renderHelper ('view/reader_view');
-	} elseif($output == 'global') {
+	} elseif ($output === 'global') {
 		$this->renderHelper ('view/global_view');
 	} else {
 		$this->renderHelper ('view/normal_view');
 	}
+} elseif ($output === 'rss') {
+	$token = $this->conf->token;
+	$token_param = Minz_Request::param ('token', '');
+	$token_is_ok = ($token != '' && $token == $token_param);
+	if ($token_is_ok) {
+		$this->renderHelper ('view/rss_view');
+	} else {
+		showForbidden();
+	}
 } else {
-?>
-<div class="post content">
-	<h1><?php echo Minz_Translate::t ('forbidden_access'); ?></h1>
-	<p><?php echo Minz_Translate::t ('forbidden_access_description'); ?></p>
-	<p><a href="<?php echo _url ('index', 'about'); ?>"><?php echo Minz_Translate::t ('about_freshrss'); ?></a></p>
-</div>
-<?php
-}
+	showForbidden();
+}

+ 11 - 1
lib/Minz/Configuration.php

@@ -107,6 +107,12 @@ class Minz_Configuration {
 	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 === 'persona';
+	}
 
 	public static function _allowAnonymous($allow = false) {
 		self::$allow_anonymous = (bool)$allow;
@@ -114,13 +120,17 @@ class Minz_Configuration {
 	public static function _authType($value) {
 		$value = strtolower($value);
 		switch ($value) {
-			case 'none':
 			case 'http_auth':
 			case 'persona':
+			case 'none':
 				self::$auth_type = $value;
 				break;
 		}
 	}
+	public static function _currentUser($user) {
+		self::$current_user = $user;
+	}
+
 
 	/**
 	 * Initialise les variables de configuration

+ 10 - 19
lib/Minz/View.php

@@ -13,7 +13,7 @@ class Minz_View {
 	const LAYOUT_FILENAME = '/layout.phtml';
 
 	private $view_filename = '';
-	private $use_layout = false;
+	private $use_layout = null;
 
 	private static $title = '';
 	private static $styles = array ();
@@ -31,12 +31,6 @@ class Minz_View {
 		                     . Minz_Request::controllerName () . '/'
 		                     . Minz_Request::actionName () . '.phtml';
 
-		if (file_exists (APP_PATH
-		               . self::LAYOUT_PATH_NAME
-		               . self::LAYOUT_FILENAME)) {
-			$this->use_layout = true;
-		}
-
 		self::$title = Minz_Configuration::title ();
 	}
 
@@ -44,6 +38,9 @@ class Minz_View {
 	 * Construit la vue
 	 */
 	public function build () {
+		if ($this->use_layout === null) {	//TODO: avoid file_exists and require views to be explicit
+			$this->use_layout = file_exists (APP_PATH . self::LAYOUT_PATH_NAME . self::LAYOUT_FILENAME);
+		}
 		if ($this->use_layout) {
 			$this->buildLayout ();
 		} else {
@@ -66,10 +63,8 @@ class Minz_View {
 	 * Affiche la Vue en elle-même
 	 */
 	public function render () {
-		if (file_exists ($this->view_filename)) {
-			include ($this->view_filename);
-		} else {
-			Minz_Log::record ('File doesn\'t exist : `'
+		if ((@include($this->view_filename)) === false) {
+			Minz_Log::record ('File not found: `'
 			            . $this->view_filename . '`',
 			            Minz_Log::NOTICE);
 		}
@@ -84,10 +79,8 @@ class Minz_View {
 		             . self::LAYOUT_PATH_NAME . '/'
 		             . $part . '.phtml';
 
-		if (file_exists ($fic_partial)) {
-			include ($fic_partial);
-		} else {
-			Minz_Log::record ('File doesn\'t exist : `'
+		if ((@include($fic_partial)) === false) {
+			Minz_Log::record ('File not found: `'
 			            . $fic_partial . '`',
 			            Minz_Log::WARNING);
 		}
@@ -102,10 +95,8 @@ class Minz_View {
 		            . '/views/helpers/'
 		            . $helper . '.phtml';
 
-		if (file_exists ($fic_helper)) {
-			include ($fic_helper);
-		} else {
-			Minz_Log::record ('File doesn\'t exist : `'
+		if ((@include($fic_helper)) === false) {;
+			Minz_Log::record ('File not found: `'
 			            . $fic_helper . '`',
 			            Minz_Log::WARNING);
 		}

+ 5 - 11
lib/lib_rss.php

@@ -56,16 +56,6 @@ function checkUrl($url) {
 	}
 }
 
-// vérifie qu'on est connecté
-function is_logged () {
-	return Minz_Session::param ('mail') != false;
-}
-
-// vérifie que le système d'authentification est configuré
-function login_is_conf ($conf) {
-	return $conf->mail_login != '';
-}
-
 // tiré de Shaarli de Seb Sauvage	//Format RFC 4648 base64url
 function small_hash ($txt) {
 	$t = rtrim (base64_encode (hash ('crc32', $txt, true)), '=');
@@ -173,7 +163,7 @@ function uSecString() {
 	return str_pad($t['usec'], 6, '0');
 }
 
-function invalidateHttpCache() {
+function invalidateHttpCache($currentUser = '') {	//TODO: Make multi-user compatible
 	file_put_contents(DATA_PATH . '/touch.txt', uTimeString());
 }
 
@@ -185,6 +175,10 @@ function usernameFromPath($userPath) {
 	}
 }
 
+function isValidUser($user) {
+	return $user != '' && ctype_alnum($user) && file_exists(DATA_PATH . '/' . $user . '_user.php');
+}
+
 function listUsers() {
 	return array_map('usernameFromPath', glob(DATA_PATH . '/*_user.php'));
 }

+ 20 - 4
p/i/install.php

@@ -543,6 +543,8 @@ function checkStep1 () {
 	$minz = file_exists (LIB_PATH . '/Minz');
 	$curl = extension_loaded ('curl');
 	$pdo = extension_loaded ('pdo_mysql');
+	$pcre = extension_loaded ('pcre');
+	$ctype = extension_loaded ('ctype');
 	$dom = class_exists('DOMDocument');
 	$data = DATA_PATH && is_writable (DATA_PATH);
 	$cache = CACHE_PATH && is_writable (CACHE_PATH);
@@ -554,12 +556,14 @@ function checkStep1 () {
 		'minz' => $minz ? 'ok' : 'ko',
 		'curl' => $curl ? 'ok' : 'ko',
 		'pdo-mysql' => $pdo ? 'ok' : 'ko',
+		'pcre' => $pcre ? 'ok' : 'ko',
+		'ctype' => $ctype ? 'ok' : 'ko',
 		'dom' => $dom ? 'ok' : 'ko',
 		'data' => $data ? 'ok' : 'ko',
 		'cache' => $cache ? 'ok' : 'ko',
 		'log' => $log ? 'ok' : 'ko',
 		'favicons' => $favicons ? 'ok' : 'ko',
-		'all' => $php && $minz && $curl && $pdo && $dom && $data && $cache && $log && $favicons ? 'ok' : 'ko'
+		'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom && $data && $cache && $log && $favicons ? 'ok' : 'ko'
 	);
 }
 
@@ -726,6 +730,12 @@ function printStep1 () {
 	<p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('minz_is_nok', LIB_PATH . '/Minz'); ?></p>
 	<?php } ?>
 
+	<?php if ($res['pdo-mysql'] == 'ok') { ?>
+	<p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('pdomysql_is_ok'); ?></p>
+	<?php } else { ?>
+	<p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('pdomysql_is_nok'); ?></p>
+	<?php } ?>
+
 	<?php if ($res['curl'] == 'ok') { ?>
 	<?php $version = curl_version(); ?>
 	<p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('curl_is_ok', $version['version']); ?></p>
@@ -733,10 +743,16 @@ function printStep1 () {
 	<p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('curl_is_nok'); ?></p>
 	<?php } ?>
 
-	<?php if ($res['pdo-mysql'] == 'ok') { ?>
-	<p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('pdomysql_is_ok'); ?></p>
+	<?php if ($res['pcre'] == 'ok') { ?>
+	<p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('pcre_is_ok'); ?></p>
 	<?php } else { ?>
-	<p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('pdomysql_is_nok'); ?></p>
+	<p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('pcre_is_nok'); ?></p>
+	<?php } ?>
+
+	<?php if ($res['ctype'] == 'ok') { ?>
+	<p class="alert alert-success"><span class="alert-head"><?php echo _t ('ok'); ?></span> <?php echo _t ('ctype_is_ok'); ?></p>
+	<?php } else { ?>
+	<p class="alert alert-error"><span class="alert-head"><?php echo _t ('damn'); ?></span> <?php echo _t ('ctype_is_nok'); ?></p>
 	<?php } ?>
 
 	<?php if ($res['dom'] == 'ok') { ?>