From 46a5bdea25c0ae0624082dd08f3c41060e7fed86 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Wed, 8 Jan 2014 00:05:00 -0500 Subject: Ajout d'un filtre sur les articles lus Ajout d'un filtre sur les articles favoris --- app/layout/nav_menu.phtml | 48 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) (limited to 'app/layout') diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 44b49b10c..adb8aea9b 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -128,24 +128,52 @@
  • + state, $url_state['params']['state']) <> 0) { + ?>
  • - state == 'not_read') { - $url_state['params']['state'] = 'all'; - ?> - +
  • + + + state, $url_state['params']['state']) <> 0) { + ?> +
  • -
  • + + + state, $url_state['params']['state']) <> 0) { + ?> +
  • + + + +
  • + + + state, $url_state['params']['state']) <> 0) { + ?> +
  • + + + +
  • + + +
  • Date: Wed, 8 Jan 2014 23:33:33 +0100 Subject: Changements syntaxiques mineurs aledeg/filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/marienfressinaud/FreshRSS/pull/352 Évite quelques appels de fonctions, et affectations inutiles --- CHANGELOG | 1 + app/i18n/en.php | 2 +- app/i18n/fr.php | 2 +- app/layout/nav_menu.phtml | 31 ++++++++++++++----------------- 4 files changed, 17 insertions(+), 19 deletions(-) (limited to 'app/layout') diff --git a/CHANGELOG b/CHANGELOG index 2d5a38d7a..45936ac03 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -26,6 +26,7 @@ * Permet de modifier la description et l’adresse d’un flux RSS ainsi que le site Web associé * Nouveau raccourci pour ouvrir/fermer un article (‘c’ par défaut) * Boutons pour effacer les logs et pour purger les vieux articles + * Nouveaux filtres d’affichage : seulement les articles favoris, et seulement les articles lus * SQL : * Nouveau moteur de recherche, aussi accessible depuis la vue mobile * Mots clefs de recherche “intitle:”, “inurl:”, “author:” diff --git a/app/i18n/en.php b/app/i18n/en.php index 3717e0245..89af15b17 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -44,7 +44,7 @@ return array ( 'rss_view' => 'RSS feed', 'show_all_articles' => 'Show all articles', 'show_not_reads' => 'Show only unread', - 'show_read' => 'Show only read', + 'show_read' => 'Show only read', 'show_favorite' => 'Show favorites', 'older_first' => 'Oldest first', 'newer_first' => 'Newer first', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 42b6f8dcf..d4c96c1db 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -44,7 +44,7 @@ return array ( 'rss_view' => 'Flux RSS', 'show_all_articles' => 'Afficher tous les articles', 'show_not_reads' => 'Afficher les non lus', - 'show_read' => 'Afficher les lus', + 'show_read' => 'Afficher les lus', 'show_favorite' => 'Afficher les favoris', 'older_first' => 'Plus anciens en premier', 'newer_first' => 'Plus récents en premier', diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index adb8aea9b..566304353 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -103,21 +103,21 @@ $url_output = $url; $actual_view = Minz_Request::param('output', 'normal'); ?> - +
  • - +
  • - +
  • @@ -130,41 +130,38 @@ state, $url_state['params']['state']) <> 0) { + if ($this->state !== 'all') { + $url_state['params']['state'] = 'all'; ?>
  • - - state, $url_state['params']['state']) <> 0) { + } + if ($this->state !== 'not_read') { + $url_state['params']['state'] = 'not_read'; ?>
  • - - state, $url_state['params']['state']) <> 0) { + } + if ($this->state !== 'read') { + $url_state['params']['state'] = 'read'; ?>
  • - - state, $url_state['params']['state']) <> 0) { + } + if ($this->state != 'favorite') { + $url_state['params']['state'] = 'favorite'; ?>
  • -- cgit v1.2.3 From 8f1a30ee889e7aaf34ac80ae92cbd04cc5671a10 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Wed, 8 Jan 2014 20:44:25 -0500 Subject: Modification des filtres MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Affichage de l'ensemble des filtres disponibles en tout temps Ajout d'un indicateur de sélection en CSS --- app/layout/nav_menu.phtml | 22 +++++++--------------- p/themes/default/global.css | 6 ++++++ p/themes/default_dark/global.css | 6 ++++++ p/themes/flat-design/global.css | 7 +++++++ 4 files changed, 26 insertions(+), 15 deletions(-) (limited to 'app/layout') diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 566304353..1fabac996 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -130,45 +130,37 @@ state !== 'all') { - $url_state['params']['state'] = 'all'; + $url_state['params']['state'] = 'all'; ?> -
  • +
  • ">
  • state !== 'not_read') { - $url_state['params']['state'] = 'not_read'; + $url_state['params']['state'] = 'not_read'; ?> -
  • +
  • ">
  • state !== 'read') { $url_state['params']['state'] = 'read'; ?> -
  • +
  • ">
  • state != 'favorite') { $url_state['params']['state'] = 'favorite'; ?> -
  • - +
  • "> +
  • -
  • diff --git a/p/themes/default/global.css b/p/themes/default/global.css index 2cc195f90..d3a5cc68c 100644 --- a/p/themes/default/global.css +++ b/p/themes/default/global.css @@ -397,6 +397,12 @@ input, select, textarea { height: 30px; font-size: 90%; line-height: 30px; + } + .dropdown-menu .item.selected{ + background: #0062be; + } + .dropdown-menu .item.selected a{ + color: #fff; } .dropdown-menu .item > * { display: block; diff --git a/p/themes/default_dark/global.css b/p/themes/default_dark/global.css index 40694045a..a9c83d9de 100644 --- a/p/themes/default_dark/global.css +++ b/p/themes/default_dark/global.css @@ -385,6 +385,12 @@ input, select, textarea { height: 30px; font-size: 90%; line-height: 30px; + } + .dropdown-menu .item.selected{ + background: #26303f; + } + .dropdown-menu .item.selected a{ + color: #888; } .dropdown-menu .item > * { display: block; diff --git a/p/themes/flat-design/global.css b/p/themes/flat-design/global.css index c24a9f481..7686b3662 100644 --- a/p/themes/flat-design/global.css +++ b/p/themes/flat-design/global.css @@ -386,6 +386,13 @@ input, select, textarea { height: 30px; font-size: 90%; line-height: 30px; + } + .dropdown-menu .item.selected{ + background: #2980b9; + } + .dropdown-menu .item.selected a{ + background: #2980b9; + color: #fff; } .dropdown-menu .item > * { display: block; -- cgit v1.2.3 From bddbe5f79f42d526d7d56a7189ca0b65970abe89 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 9 Jan 2014 21:24:48 +0100 Subject: Changements styles filtres MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Utilisation d'une coche plutôt qu'un changement de couleur. Meilleure accessibilité avec ARIA. Optimisation des sélecteurs CSS. En particulier, évite les sélecteurs universels * qui sont très couteux en performances. https://github.com/marienfressinaud/FreshRSS/issues/277 https://github.com/marienfressinaud/FreshRSS/pull/357 --- app/layout/nav_menu.phtml | 12 ++++++------ p/themes/default/global.css | 20 ++++++++++---------- p/themes/default_dark/global.css | 18 +++++++++--------- p/themes/flat-design/global.css | 21 ++++++++++----------- 4 files changed, 35 insertions(+), 36 deletions(-) (limited to 'app/layout') diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 1fabac996..705ed3314 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -132,7 +132,7 @@ $url_state = $url; $url_state['params']['state'] = 'all'; ?> -
  • "> +
  • "> +
  • -
  • "> +
  • -
  • "> +
  • + conf->view_mode !== Minz_Request::param('output', 'normal')) { + $output = array('output', 'normal'); + } + ?>
  • - + @@ -24,7 +30,7 @@
  • get_c == $cat->id ()) { $c_active = true; } ?> @@ -53,7 +59,7 @@ ✇ - name(); ?> + name(); ?>
  • -- cgit v1.2.3 From d58886a937cbe425163526fc2ba3d2a118602035 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 12 Jan 2014 03:10:31 +0100 Subject: Implémentation de l'indentification par mot de passe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implémentation de https://github.com/marienfressinaud/FreshRSS/issues/104 --- CHANGELOG | 3 ++ app/Controllers/indexController.php | 52 +++++++++++++++++++++++++++-- app/Controllers/javascriptController.php | 12 +++---- app/Controllers/usersController.php | 26 ++++++++++++--- app/FreshRSS.php | 27 ++++++++++++--- app/i18n/en.php | 7 ++-- app/i18n/fr.php | 7 ++-- app/layout/header.phtml | 51 +++++++++++++++++++--------- app/views/configure/users.phtml | 16 ++++++--- app/views/helpers/javascript_vars.phtml | 4 +-- app/views/helpers/view/login.phtml | 43 ++++++++++++++++++++++++ app/views/index/index.phtml | 14 ++------ lib/Minz/Configuration.php | 3 +- lib/Minz/FrontController.php | 2 +- p/scripts/main.js | 57 ++++++++++++++++++++++++++++++-- 15 files changed, 265 insertions(+), 59 deletions(-) create mode 100644 app/views/helpers/view/login.phtml (limited to 'app/layout') diff --git a/CHANGELOG b/CHANGELOG index fe856fe4a..5c9b56465 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,8 @@ * Nouveau mode multi-utilisateur * L’utilisateur par défaut (administrateur) peut créer et supprimer d’autres utilisateurs * Nécessite un contrôle d’accès, soit : + * par le nouveau mode de connexion par formulaire (nom d’utilisateur + mot de passe) + * relativement sûr même sans HTTPS (le mot de passe n’est pas transmis en clair) * par HTTP (par exemple sous Apache en créant un fichier ./p/i/.htaccess et .htpasswd) * le nom d’utilisateur HTTP doit correspondre au nom d’utilisateur FreshRSS * par Mozilla Persona, en renseignant l’adresse courriel des utilisateurs @@ -68,6 +70,7 @@ * Réorganisation des fichiers et répertoires, en particulier : * Tous les fichiers utilisateur sont dans “./data/” (y compris “cache”, “favicons”, et “log”) * Déplacement de “./app/configuration/application.ini” vers “./data/config.php” + * Meilleure sécurité et compatibilité * Déplacement de “./public/data/Configuration.array.php” vers “./data/*_user.php” * Déplacement de “./public/” vers “./p/” * Déplacement de “./public/index.php” vers “./p/i/index.php” (voir cookie ci-dessous) diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 81dfefabb..772a08f30 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -47,8 +47,6 @@ class FreshRSS_index_Controller extends Minz_ActionController { $this->view->_useLayout (false); header('Content-Type: application/rss+xml; charset=utf-8'); } else { - Minz_View::appendScript (Minz_Url::display ('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); - if ($output === 'global') { Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); } @@ -290,8 +288,56 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function logoutAction () { + $this->view->_useLayout(false); + invalidateHttpCache(); + Minz_Session::_param('currentUser'); + Minz_Session::_param('mail'); + Minz_Session::_param('passwordHash'); + } + + public function formLoginAction () { $this->view->_useLayout (false); - Minz_Session::_param ('mail'); + if (Minz_Request::isPost()) { + $ok = false; + $nonce = Minz_Session::param('nonce'); + $username = Minz_Request::param('username', ''); + $c = Minz_Request::param('challenge', ''); + if (ctype_alnum($username) && ctype_graph($c) && ctype_alnum($nonce)) { + if (!function_exists('password_verify')) { + include_once(LIB_PATH . '/password_compat.php'); + } + try { + $conf = new FreshRSS_Configuration($username); + $s = $conf->passwordHash; + $ok = password_verify($nonce . $s, $c); + if ($ok) { + Minz_Session::_param('currentUser', $username); + Minz_Session::_param('passwordHash', $s); + } else { + Minz_Log::record('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c, Minz_Log::WARNING); + } + } catch (Minz_Exception $me) { + Minz_Log::record('Login failure: ' . $me->getMessage(), Minz_Log::WARNING); + } + } + if (!$ok) { + $notif = array( + 'type' => 'bad', + 'content' => Minz_Translate::t('invalid_login') + ); + Minz_Session::_param('notification', $notif); + } + } + invalidateHttpCache(); + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); + } + + public function formLogoutAction () { + $this->view->_useLayout(false); invalidateHttpCache(); + Minz_Session::_param('currentUser'); + Minz_Session::_param('mail'); + Minz_Session::_param('passwordHash'); + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } } diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php index e29f439d8..02e424437 100755 --- a/app/Controllers/javascriptController.php +++ b/app/Controllers/javascriptController.php @@ -17,7 +17,7 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { $this->view->categories = $catDAO->listCategories(true, false); } - // For Web-form login + //For Web-form login public function nonceAction() { header('Content-Type: application/json; charset=UTF-8'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T')); @@ -29,15 +29,15 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { if (ctype_alnum($user)) { try { $conf = new FreshRSS_Configuration($user); - $hash = $conf->passwordHash; //CRYPT_BLOWFISH - Blowfish hashing with a salt as follows: "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". - if (strlen($hash) >= 60) { - $this->view->salt1 = substr($hash, 0, 29); + $s = $conf->passwordHash; + if (strlen($s) >= 60) { + $this->view->salt1 = substr($s, 0, 29); //CRYPT_BLOWFISH Salt: "$2a$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". $this->view->nonce = sha1(Minz_Configuration::salt() . uniqid(mt_rand(), true)); - Minz_Session::_param ('nonce', $this->view->nonce); + Minz_Session::_param('nonce', $this->view->nonce); return; //Success } } catch (Minz_Exception $me) { - Minz_Log::record ('Login failure: ' . $me->getMessage(), Minz_Log::WARNING); + Minz_Log::record('Login failure: ' . $me->getMessage(), Minz_Log::WARNING); } } $this->view->nonce = ''; //Failure diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index 8954c845d..cb5ebd209 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -21,18 +21,20 @@ class FreshRSS_users_Controller extends Minz_ActionController { if (!function_exists('password_hash')) { include_once(LIB_PATH . '/password_compat.php'); } - $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT); //A bit expensive, on purpose + $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => 8)); //This will also have to be computed client side on mobile devices, so do not use a too high cost $passwordPlain = ''; + $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js $this->view->conf->_passwordHash($passwordHash); } - $mail = Minz_Request::param('mail_login', false); - $this->view->conf->_mail_login($mail); + $email = Minz_Request::param('mail_login', false); + $this->view->conf->_mail_login($email); $ok &= $this->view->conf->save(); $email = $this->view->conf->mail_login; Minz_Session::_param('mail', $email); + Minz_Session::_param('passwordHash', $this->view->conf->passwordHash); if ($email != '') { $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; @@ -89,10 +91,25 @@ class FreshRSS_users_Controller extends Minz_ActionController { $ok &= !file_exists($configPath); } if ($ok) { + + $passwordPlain = Minz_Request::param('new_user_passwordPlain', false); + $passwordHash = ''; + if ($passwordPlain != '') { + Minz_Request::_param('new_user_passwordPlain'); //Discard plain-text password ASAP + $_POST['new_user_passwordPlain'] = ''; + if (!function_exists('password_hash')) { + include_once(LIB_PATH . '/password_compat.php'); + } + $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => 8)); + $passwordPlain = ''; + } + if (empty($passwordHash)) { + $passwordHash = ''; + } + $new_user_email = filter_var($_POST['new_user_email'], FILTER_VALIDATE_EMAIL); if (empty($new_user_email)) { $new_user_email = ''; - $ok &= Minz_Configuration::authType() !== 'persona'; } else { $personaFile = DATA_PATH . '/persona/' . $new_user_email . '.txt'; @unlink($personaFile); @@ -102,6 +119,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { if ($ok) { $config_array = array( 'language' => $new_user_language, + 'passwordHash' => $passwordHash, 'mail_login' => $new_user_email, ); $ok &= (file_put_contents($configPath, "accessControl(Minz_Session::param('currentUser', '')); + $loginOk = $this->accessControl(Minz_Session::param('currentUser', '')); $this->loadParamsView(); - $this->loadStylesAndScripts(); //TODO: Do not load that when not needed, e.g. some Ajax requests + $this->loadStylesAndScripts($loginOk); //TODO: Do not load that when not needed, e.g. some Ajax requests $this->loadNotifications(); } private function accessControl($currentUser) { if ($currentUser == '') { switch (Minz_Configuration::authType()) { + case 'form': + $currentUser = Minz_Configuration::defaultUser(); + Minz_Session::_param('passwordHash'); + $loginOk = false; + break; case 'http_auth': $currentUser = httpAuthUser(); $loginOk = $currentUser != ''; @@ -73,6 +78,9 @@ class FreshRSS extends Minz_FrontController { if ($loginOk) { switch (Minz_Configuration::authType()) { + case 'form': + $loginOk = Minz_Session::param('passwordHash') === $this->conf->passwordHash; + break; case 'http_auth': $loginOk = strcasecmp($currentUser, httpAuthUser()) === 0; break; @@ -92,6 +100,7 @@ class FreshRSS extends Minz_FrontController { } } Minz_View::_param ('loginOk', $loginOk); + return $loginOk; } private function loadParamsView () { @@ -104,7 +113,7 @@ class FreshRSS extends Minz_FrontController { } } - private function loadStylesAndScripts () { + private function loadStylesAndScripts ($loginOk) { $theme = FreshRSS_Themes::get_infos($this->conf->theme); if ($theme) { foreach($theme['files'] as $file) { @@ -112,14 +121,22 @@ class FreshRSS extends Minz_FrontController { } } - if (Minz_Configuration::authType() === 'persona') { - Minz_View::appendScript ('https://login.persona.org/include.js'); + switch (Minz_Configuration::authType()) { + case 'form': + if (!$loginOk) { + Minz_View::appendScript(Minz_Url::display ('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js'))); + } + break; + case 'persona': + Minz_View::appendScript('https://login.persona.org/include.js'); + break; } $includeLazyLoad = $this->conf->lazyload && ($this->conf->display_posts || Minz_Request::param ('output') === 'reader'); Minz_View::appendScript (Minz_Url::display ('/scripts/jquery.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.min.js')), false, !$includeLazyLoad, !$includeLazyLoad); if ($includeLazyLoad) { Minz_View::appendScript (Minz_Url::display ('/scripts/jquery.lazyload.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.lazyload.min.js'))); } + 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'))); } diff --git a/app/i18n/en.php b/app/i18n/en.php index 3b9936e8e..71ca9538f 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -170,6 +170,9 @@ return array ( 'is_admin' => 'is administrator', 'auth_type' => 'Authentication method', 'auth_none' => 'None (dangerous)', + 'auth_form' => 'Web form (traditional, requires JavaScript)', + 'http_auth' => 'HTTP (for advanced users with HTTPS)', + 'auth_persona' => 'Mozilla Persona (modern, requires JavaScript)', 'users_list' => 'List of users', 'create_user' => 'Create new user', 'username' => 'Username', @@ -258,8 +261,8 @@ return array ( 'logs_empty' => 'Log file is empty', 'clear_logs' => 'Clear the logs', - 'forbidden_access' => 'Forbidden access', - 'forbidden_access_description' => 'Access is password protected, please to read your feeds.', + 'forbidden_access' => 'Access forbidden! (%s)', + 'login_required' => 'Login required:', 'confirm_action' => 'Are you sure you want to perform this action? It cannot be cancelled!', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 7e71cbb6d..8ffc5ec88 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -170,6 +170,9 @@ return array ( 'is_admin' => 'est administrateur', 'auth_type' => 'Méthode d’authentification', 'auth_none' => 'Aucune (dangereux)', + 'auth_form' => 'Formulaire (traditionnel, requiert JavaScript)', + 'http_auth' => 'HTTP (pour utilisateurs avancés avec HTTPS)', + 'auth_persona' => 'Mozilla Persona (moderne, requiert JavaScript)', 'users_list' => 'Liste des utilisateurs', 'create_user' => 'Créer un nouvel utilisateur', 'username' => 'Nom d’utilisateur', @@ -258,8 +261,8 @@ return array ( 'logs_empty' => 'Les logs sont vides', 'clear_logs' => 'Effacer les logs', - 'forbidden_access' => 'Accès interdit', - 'forbidden_access_description' => 'L’accès est protégé par un mot de passe, veuillez pour accéder aux flux.', + 'forbidden_access' => 'Accès interdit ! (%s)', + 'login_required' => 'Accès protégé par mot de passe :', 'confirm_action' => 'Êtes-vous sûr(e) de vouloir continuer ? Cette action ne peut être annulée !', diff --git a/app/layout/header.phtml b/app/layout/header.phtml index 0f2c524c4..e90da6196 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -1,12 +1,25 @@ - - - +
    @@ -62,16 +75,24 @@
  • - -
  • -
  • - +
  • - +
    - + diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml index 68111bdbe..1597004e1 100644 --- a/app/views/configure/users.phtml +++ b/app/views/configure/users.phtml @@ -20,7 +20,7 @@
    - +
    @@ -52,11 +52,11 @@
    - $_SERVER['REMOTE_USER'] = ``
    @@ -141,6 +141,14 @@ +
    + +
    + + +
    +
    +
    conf->mail_login; ?> diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index 92c068f7e..935294e60 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -30,8 +30,8 @@ if ($mail != 'null') { $mail = '"' . $mail . '"'; } - echo 'use_persona=', Minz_Configuration::authType() === 'persona' ? 'true' : 'false', - ',url_freshrss="', _url ('index', 'index'), '",', + echo 'authType="', Minz_Configuration::authType(), '",', + 'url_freshrss="', _url ('index', 'index'), '",', 'url_login="', _url ('index', 'login'), '",', 'url_logout="', _url ('index', 'logout'), '",', 'current_user_mail=', $mail, ",\n"; diff --git a/app/views/helpers/view/login.phtml b/app/views/helpers/view/login.phtml new file mode 100644 index 000000000..e4a24f9ed --- /dev/null +++ b/app/views/helpers/view/login.phtml @@ -0,0 +1,43 @@ +
    + +

    +
    + +
    + +
    +
    +
    + +
    + + + +
    +
    +
    +
    + +
    +
    +

    FreshRSS

    +

    + +

    +
    diff --git a/app/views/index/index.phtml b/app/views/index/index.phtml index 549d0b61e..9b69233e9 100644 --- a/app/views/index/index.phtml +++ b/app/views/index/index.phtml @@ -1,15 +1,5 @@
    -

    -

    -

    -
    loginOk || Minz_Configuration::allowAnonymous()) { @@ -31,8 +21,8 @@ if ($this->loginOk || Minz_Configuration::allowAnonymous()) { if ($token_is_ok) { $this->renderHelper ('view/rss_view'); } else { - showForbidden(); + $this->renderHelper ('view/login'); } } else { - showForbidden(); + $this->renderHelper ('view/login'); } diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 2c30661ed..433992e0d 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -109,7 +109,7 @@ class Minz_Configuration { return self::$auth_type !== 'none'; } public static function canLogIn() { - return self::$auth_type === 'persona'; + return self::$auth_type === 'form' || self::$auth_type === 'persona'; } public static function _allowAnonymous($allow = false) { @@ -118,6 +118,7 @@ class Minz_Configuration { public static function _authType($value) { $value = strtolower($value); switch ($value) { + case 'form': case 'http_auth': case 'persona': case 'none': diff --git a/lib/Minz/FrontController.php b/lib/Minz/FrontController.php index 7b8526bc8..80eda8877 100644 --- a/lib/Minz/FrontController.php +++ b/lib/Minz/FrontController.php @@ -34,7 +34,7 @@ class Minz_FrontController { */ public function __construct () { if (LOG_PATH === false) { - $this->killApp ('Path doesn’t exist : LOG_PATH'); + $this->killApp ('Path not found: LOG_PATH'); } try { diff --git a/p/scripts/main.js b/p/scripts/main.js index 24af1b210..0c4c3f1b2 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -587,6 +587,54 @@ function init_load_more(box) { } // +// +function poormanSalt() { //If crypto.getRandomValues is not available + var text = '$2a$04$', + base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz'; + for (var i = 22; i > 0; i--) { + text += base.charAt(Math.floor(Math.random() * 64)); + } + return text; +} + +function init_loginForm() { + var $loginForm = $('#loginForm'); + if ($loginForm.length === 0) { + return; + } + if (!(window.dcodeIO)) { + if (window.console) { + console.log('FreshRSS waiting for bcrypt.js…'); + } + window.setTimeout(init_loginForm, 100); + return; + } + $loginForm.on('submit', function() { + $('#loginButton').attr('disabled', ''); + var success = false; + $.ajax({ + url: './?c=javascript&a=nonce&user=' + $('#username').val(), + dataType: 'json', + async: false + }).done(function (data) { + if (data.salt1 == '' || data.nonce == '') { + alert('Invalid user!'); + } else { + var strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'), + s = dcodeIO.bcrypt.hashSync($('#passwordPlain').val(), data.salt1), + c = dcodeIO.bcrypt.hashSync(data.nonce + s, strong ? 4 : poormanSalt()); + $('#challenge').val(c); + success = true; + } + }).fail(function() { + alert('Communication error!'); + }); + $('#loginButton').removeAttr('disabled'); + return success; + }); +} +// + // function init_persona() { if (!(navigator.id)) { @@ -696,8 +744,13 @@ function init_all() { init_notifications(); init_actualize(); init_load_more($stream); - if (use_persona) { - init_persona(); + switch (authType) { + case 'form': + init_loginForm(); + break; + case 'persona': + init_persona(); + break; } init_confirm_action(); init_print_action(); -- cgit v1.2.3 From 7261a551e908229ea4625b6645d490712570e71c Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 17 Jan 2014 22:47:21 +0100 Subject: Essaye d'éviter les problèmes d'auto-remplissage des champs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrige https://github.com/marienfressinaud/FreshRSS/issues/327#issuecomment-32635516 --- app/Controllers/feedController.php | 4 ++-- app/layout/aside_feed.phtml | 6 +++--- app/views/configure/feed.phtml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'app/layout') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 2d7c0ab43..d2117f665 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -30,8 +30,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $cat = $def_cat->id (); } - $user = Minz_Request::param ('username'); - $pass = Minz_Request::param ('password'); + $user = Minz_Request::param ('http_user'); + $pass = Minz_Request::param ('http_pass'); $params = array (); $transactionStarted = false; diff --git a/app/layout/aside_feed.phtml b/app/layout/aside_feed.phtml index 7fbccce1e..2446e72cb 100644 --- a/app/layout/aside_feed.phtml +++ b/app/layout/aside_feed.phtml @@ -1,7 +1,7 @@
    diff --git a/app/views/configure/feed.phtml b/app/views/configure/feed.phtml index a0fe39f8a..fc26ab58b 100644 --- a/app/views/configure/feed.phtml +++ b/app/views/configure/feed.phtml @@ -11,7 +11,7 @@

    - +
    -- cgit v1.2.3 From dda7b002def51405bab82bd4a31fc3ad8c33c572 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 18 Jan 2014 01:38:11 +0100 Subject: Corrections mode anonyme avec formulaire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contribue à https://github.com/marienfressinaud/FreshRSS/issues/361 --- app/Controllers/indexController.php | 4 ++-- app/layout/header.phtml | 17 ++++++++++----- app/views/helpers/view/login.phtml | 43 ------------------------------------- app/views/index/formLogin.phtml | 43 +++++++++++++++++++++++++++++++++++++ app/views/index/index.phtml | 4 ++-- 5 files changed, 59 insertions(+), 52 deletions(-) delete mode 100644 app/views/helpers/view/login.phtml create mode 100644 app/views/index/formLogin.phtml (limited to 'app/layout') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 772a08f30..d05a106bb 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -296,7 +296,6 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function formLoginAction () { - $this->view->_useLayout (false); if (Minz_Request::isPost()) { $ok = false; $nonce = Minz_Session::param('nonce'); @@ -327,9 +326,10 @@ class FreshRSS_index_Controller extends Minz_ActionController { ); Minz_Session::_param('notification', $notif); } + $this->view->_useLayout(false); + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } invalidateHttpCache(); - Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } public function formLogoutAction () { diff --git a/app/layout/header.phtml b/app/layout/header.phtml index e90da6196..d43f682b0 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -90,9 +90,16 @@ if (Minz_Configuration::canLogIn()) {
    - -
    - -
    - +
    diff --git a/app/views/helpers/view/login.phtml b/app/views/helpers/view/login.phtml deleted file mode 100644 index e4a24f9ed..000000000 --- a/app/views/helpers/view/login.phtml +++ /dev/null @@ -1,43 +0,0 @@ -
    - -

    -
    - -
    - -
    -
    -
    - -
    - - - -
    -
    -
    -
    - -
    -
    -

    FreshRSS

    -

    - -

    -
    diff --git a/app/views/index/formLogin.phtml b/app/views/index/formLogin.phtml new file mode 100644 index 000000000..e4a24f9ed --- /dev/null +++ b/app/views/index/formLogin.phtml @@ -0,0 +1,43 @@ +
    + +

    +
    + +
    + +
    +
    +
    + +
    + + + +
    +
    +
    +
    + +
    +
    +

    FreshRSS

    +

    + +

    +
    diff --git a/app/views/index/index.phtml b/app/views/index/index.phtml index 9b69233e9..9a7c9f3b9 100644 --- a/app/views/index/index.phtml +++ b/app/views/index/index.phtml @@ -21,8 +21,8 @@ if ($this->loginOk || Minz_Configuration::allowAnonymous()) { if ($token_is_ok) { $this->renderHelper ('view/rss_view'); } else { - $this->renderHelper ('view/login'); + Minz_Request::forward(array('c' => 'index', 'a' => 'formLogin'), true); } } else { - $this->renderHelper ('view/login'); + Minz_Request::forward(array('c' => 'index', 'a' => 'formLogin'), true); } -- cgit v1.2.3 From 69f7bce75b6f45b7f8be376a5d9c14d5da62c91d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 18 Jan 2014 16:41:10 +0100 Subject: Changements de vues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correction d'un bug JavaScript récent dans la vue globale. Corrections de nombreux bugs lors des changements de vue https://github.com/marienfressinaud/FreshRSS/issues/346 et amélioration des performances pour la génération des URL en évitant beaucoup d'appels de fonctions https://github.com/marienfressinaud/FreshRSS/pull/362 De plus, dans les URL, is_favorite et is_read ont maintenant une valeur par défaut de 1, ce qui évite de les écrire dans beaucoup de cas. Suppression des espaces blancs de la sortie HTML au niveau de quelques boucles critiques. --- app/Controllers/entryController.php | 13 ++-- app/i18n/en.php | 2 +- app/i18n/fr.php | 2 +- app/layout/aside_flux.phtml | 73 +++++++++--------- app/layout/nav_menu.phtml | 8 +- app/views/entry/bookmark.phtml | 2 +- app/views/entry/read.phtml | 2 +- app/views/helpers/view/normal_view.phtml | 122 +++++++++++++++++-------------- p/scripts/global_view.js | 7 +- 9 files changed, 131 insertions(+), 100 deletions(-) (limited to 'app/layout') diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index a24dfe6d6..ded7598d9 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -10,6 +10,11 @@ class FreshRSS_entry_Controller extends Minz_ActionController { } $this->params = array (); + $output = Minz_Request::param('output', ''); + if (($output != '') && ($this->conf->view_mode !== $output)) { + $this->params['output'] = $output; + } + $this->redirect = false; $ajax = Minz_Request::param ('ajax'); if ($ajax) { @@ -34,13 +39,10 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $this->redirect = true; $id = Minz_Request::param ('id'); - $is_read = Minz_Request::param ('is_read'); $get = Minz_Request::param ('get'); $nextGet = Minz_Request::param ('nextGet', $get); $idMax = Minz_Request::param ('idMax', 0); - $is_read = (bool)$is_read; - $entryDAO = new FreshRSS_EntryDAO (); if ($id == false) { if (!$get) { @@ -63,7 +65,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { break; } if ($nextGet !== 'a') { - $this->params = array ('get' => $nextGet); + $this->params['get'] = $nextGet; } } @@ -73,6 +75,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { ); Minz_Session::_param ('notification', $notif); } else { + $is_read = (bool)(Minz_Request::param ('is_read', true)); $entryDAO->markRead ($id, $is_read); } } @@ -83,7 +86,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $id = Minz_Request::param ('id'); if ($id) { $entryDAO = new FreshRSS_EntryDAO (); - $entryDAO->markFavorite ($id, Minz_Request::param ('is_favorite')); + $entryDAO->markFavorite ($id, (bool)(Minz_Request::param ('is_favorite', true))); } } diff --git a/app/i18n/en.php b/app/i18n/en.php index 1b76dfc52..01d86e5da 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -161,7 +161,7 @@ return array ( 'current_user' => 'Current user', 'default_user' => 'Username of the default user (maximum 16 alphanumeric characters)', - 'password_form' =>'Password
    (for the Web-form login method)', + 'password_form' => 'Password
    (for the Web-form login method)', 'persona_connection_email' => 'Login mail address
    (for Mozilla Persona)', 'allow_anonymous' => 'Allow anonymous reading of the articles of the default user (%s)', 'auth_token' => 'Authentication token', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 5d54dc2dd..2c44aa8d0 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -160,7 +160,7 @@ return array ( 'think_to_add' => 'Pensez à en ajouter !', 'current_user' => 'Utilisateur actuel', - 'password_form' =>'Mot de passe
    (pour connexion par formulaire)', + 'password_form' => 'Mot de passe
    (pour connexion par formulaire)', 'default_user' => 'Nom de l’utilisateur par défaut (16 caractères alphanumériques maximum)', 'persona_connection_email' => 'Adresse courriel de connexion
    (pour Mozilla Persona)', 'allow_anonymous' => 'Autoriser la lecture anonyme des articles de l’utilisateur par défaut (%s)', diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index f2f85df30..f7d8b12b9 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -13,15 +13,15 @@
  • - 'index', 'a' => 'index', 'params' => array()); if ($this->conf->view_mode !== Minz_Request::param('output', 'normal')) { - $output = array('output', 'normal'); + $arUrl['params']['output'] = 'normal'; } ?>
  • - + @@ -30,43 +30,46 @@
  • - cat_aside as $cat) { ?> - feeds (); ?> - -
  • - get_c == $cat->id ()) { $c_active = true; } ?> -
    - name (); ?> - -
    - -
      - id (); $nbEntries = $feed->nbEntries (); - $f_active = ($this->get_f == $feed_id); - ?> -
    • - - ✇ - name(); ?> -
    • - -
    -
  • - + cat_aside as $cat) { + $feeds = $cat->feeds (); + if (!empty ($feeds)) { + ?>
  • get_c == $cat->id ()) { + $c_active = true; + } + ?>
      id (); + $nbEntries = $feed->nbEntries (); + $f_active = ($this->get_f == $feed_id); + ?>
    • ✇ name(); ?>
  • - @@ -79,7 +82,7 @@
  • -
  • +
  • diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 705ed3314..b9ce33295 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -56,7 +56,13 @@ } $p = isset($this->entries[0]) ? $this->entries[0] : null; $idMax = $p === null ? '0' : $p->id(); - $markReadUrl = _url ('entry', 'read', 'is_read', 1, 'get', $get, 'nextGet', $nextGet, 'idMax', $idMax); + + $arUrl = array('c' => 'entry', 'a' => 'read', 'params' => array('get' => $get, 'nextGet' => $nextGet, 'idMax' => $idMax)); + $output = Minz_Request::param('output', ''); + if (($output != '') && ($this->conf->view_mode !== $output)) { + $arUrl['params']['output'] = $output; + } + $markReadUrl = Minz_Url::display($arUrl); Minz_Session::_param ('markReadUrl', $markReadUrl); ?> diff --git a/app/views/entry/bookmark.phtml b/app/views/entry/bookmark.phtml index 55b2d3d3d..c1fc32b7f 100755 --- a/app/views/entry/bookmark.phtml +++ b/app/views/entry/bookmark.phtml @@ -1,7 +1,7 @@ entries)) { $bottomline_link = $this->conf->bottomline_link; ?> -
    - entries as $item) { ?> +
    entries as $item) { + if ($display_today && $item->isDay (FreshRSS_Days::TODAY, $this->today)) { + ?>
    currentName; ?>
    isDay (FreshRSS_Days::YESTERDAY, $this->today)) { + ?>
    currentName; ?>
    isDay (FreshRSS_Days::BEFORE_YESTERDAY, $this->today)) { + ?>
    currentName; ?>
    isDay (FreshRSS_Days::TODAY, $this->today)) { ?> -
    - - - currentName; ?> -
    - - isDay (FreshRSS_Days::YESTERDAY, $this->today)) { ?> -
    - - - currentName; ?> -
    - - isDay (FreshRSS_Days::BEFORE_YESTERDAY, $this->today)) { ?> -
    - - currentName; ?> -
    - - -
    diff --git a/p/scripts/global_view.js b/p/scripts/global_view.js index 0cdcdd3fa..c34fbf1a7 100644 --- a/p/scripts/global_view.js +++ b/p/scripts/global_view.js @@ -50,11 +50,14 @@ function init_global_view() { $(".nav_menu #nav_menu_read_all, .nav_menu .toggle_aside").remove(); - init_stream_delegates($("#panel")); + init_stream($("#panel")); } function init_all_global_view() { - if (!(window.$ && window.init_stream_delegates)) { + if (!(window.$ && window.init_stream)) { + if (window.console) { + console.log('FreshRSS Global view waiting for JS…'); + } window.setTimeout(init_all_global_view, 50); //Wait for all js to be loaded return; } -- cgit v1.2.3