浏览代码

Merge branch 'dev' of github.com:marienfressinaud/FreshRSS into dev

Marien Fressinaud 11 年之前
父节点
当前提交
17cf6fae6b

+ 2 - 0
CHANGELOG

@@ -11,6 +11,8 @@
 	* Improvements
 	* Improvements
 * Security
 * Security
 	* Basic protection against XSRF (Cross-Site Request Forgery) based on HTTP Referer (POST requests only)
 	* Basic protection against XSRF (Cross-Site Request Forgery) based on HTTP Referer (POST requests only)
+* API
+	* Compatible with lighttpd
 * Misc.
 * Misc.
 	* Changed lazyload implementation
 	* Changed lazyload implementation
 	* Support of HTML5 notifications for new upcoming articles
 	* Support of HTML5 notifications for new upcoming articles

+ 101 - 0
README.fr.md

@@ -0,0 +1,101 @@
+* [English version](README.md)
+
+# FreshRSS
+FreshRSS est un agrégateur de flux RSS à auto-héberger à l’image de [Leed](http://projet.idleman.fr/leed/) ou de [Kriss Feed](http://tontof.net/kriss/feed/).
+
+Il se veut léger et facile à prendre en main tout en étant un outil puissant et paramétrable.
+
+Il permet de gérer plusieurs utilisateurs, et dispose d’un mode de lecture anonyme.
+
+* Site officiel : http://freshrss.org
+* Démo : http://demo.freshrss.org/
+* Développeur : Marien Fressinaud <dev@marienfressinaud.fr>
+* Version actuelle : 0.8-dev
+* Date de publication 2014-0x-xx
+* License [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html)
+
+![Logo de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_title.png)
+
+# Note sur les branches
+**Ce logiciel est encore en développement !** Veuillez vous assurer d'utiliser la branche qui vous correspond :
+
+* Utilisez [la branche master](https://github.com/marienfressinaud/FreshRSS/tree/master/) si vous visez la stabilité.
+* [La branche beta](https://github.com/marienfressinaud/FreshRSS/tree/beta) est celle par défaut : les nouveautés y sont ajoutées environ tous les mois.
+* Pour les développeurs et ceux qui savent ce qu'ils font, [la branche dev](https://github.com/marienfressinaud/FreshRSS/tree/dev) vous ouvre les bras !
+
+# Disclaimer
+Cette application a été développée pour s’adapter à des besoins personnels et non professionnels.
+Je ne garantis en aucun cas la sécurité de celle-ci, ni son bon fonctionnement.
+Je m’engage néanmoins à répondre dans la mesure du possible aux demandes d’évolution si celles-ci me semblent justifiées.
+Privilégiez pour cela des demandes sur GitHub
+(https://github.com/marienfressinaud/FreshRSS/issues) ou par mail (dev@marienfressinaud.fr)
+
+# Pré-requis
+* Serveur modeste, par exemple sous Linux ou Windows
+	* Fonctionne même sur un Raspberry Pi avec des temps de réponse < 1s (testé sur 150 flux, 22k articles, soit 32Mo de données partiellement compressées)
+* Serveur Web Apache2 (recommandé), ou nginx, lighttpd (non testé sur les autres)
+* PHP 5.2.1+ (PHP 5.3.7+ recommandé)
+	* Requis : [PDO_MySQL](http://php.net/pdo-mysql) ou [PDO_SQLite](http://php.net/pdo-sqlite), [cURL](http://php.net/curl), [GMP](http://php.net/gmp) (seulement pour accès API sur platformes < 64 bits)
+	* Recommandés : [JSON](http://php.net/json), [mbstring](http://php.net/mbstring), [zlib](http://php.net/zlib), [Zip](http://php.net/zip)
+* MySQL 5.0.3+ (recommandé) ou SQLite 3.7.4+
+* Un navigateur Web récent tel Firefox 4+, Chrome, Opera, Safari, Internet Explorer 9+
+	* Fonctionne aussi sur mobile
+
+![Capture d’écran de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_default-design.png)
+
+# Installation
+1. Récupérez l’application FreshRSS via la commande git ou [en téléchargeant l’archive](https://github.com/marienfressinaud/FreshRSS/archive/master.zip)
+2. Placez l’application sur votre serveur (la partie à exposer au Web est le répertoire `./p/`)
+3. Le serveur Web doit avoir les droits d’écriture dans le répertoire `./data/`
+4. Accédez à FreshRSS à travers votre navigateur Web et suivez les instructions d’installation
+5. Tout devrait fonctionner :) En cas de problème, n’hésitez pas à me contacter.
+
+# Contrôle d’accès
+Il est requis pour le mode multi-utilisateur, et recommandé dans tous les cas, de limiter l’accès à votre FreshRSS. Au choix :
+* En utilisant l’identification par formulaire (requiert JavaScript, et PHP 5.3.7+ recommandé – fonctionne avec certaines versions de PHP 5.3.3+)
+* En utilisant l’identification par [Mozilla Persona](https://login.persona.org/about) incluse dans FreshRSS
+* En utilisant un contrôle d’accès HTTP défini par votre serveur Web
+	* Voir par exemple la [documentation d’Apache sur l’authentification](http://httpd.apache.org/docs/trunk/howto/auth.html)
+		* Créer dans ce cas un fichier `./p/i/.htaccess` avec un fichier `.htpasswd` correspondant.
+
+# Rafraîchissement automatique des flux
+* Vous pouvez ajouter une tâche Cron lançant régulièrement le script d’actualisation automatique des flux.
+Consultez la documentation de Cron de votre système d’exploitation ([Debian/Ubuntu](http://doc.ubuntu-fr.org/cron), [Red Hat/Fedora](http://doc.fedora-fr.org/wiki/CRON_:_Configuration_de_t%C3%A2ches_automatis%C3%A9es), [Slackware](http://docs.slackware.com/fr:slackbook:process_control?#cron), [Gentoo](http://wiki.gentoo.org/wiki/Cron/fr), [Arch Linux](http://wiki.archlinux.fr/Cron)…).
+C’est une bonne idée d’utiliser le même utilisateur que votre serveur Web (souvent “www-data”).
+Par exemple, pour exécuter le script toutes les heures :
+
+```
+7 * * * * php /chemin/vers/FreshRSS/app/actualize_script.php > /tmp/FreshRSS.log 2>&1
+```
+
+# Conseils
+* Pour une meilleure sécurité, faites en sorte que seul le répertoire `./p/` soit accessible depuis le Web, par exemple en faisant pointer un sous-domaine sur le répertoire `./p/`.
+	* En particulier, les données personnelles se trouvent dans le répertoire `./data/`.
+* Le fichier `./constants.php` définit les chemins d’accès aux répertoires clés de l’application. Si vous les bougez, tout se passe ici.
+* En cas de problème, les logs peuvent être utile à lire, soit depuis l’interface de FreshRSS, soit manuellement depuis `./data/log/*.log`.
+
+# Sauvegarde
+* Il faut conserver vos fichiers `./data/config.php` ainsi que `./data/*_user.php` et éventuellement `./data/persona/`
+* Vous pouvez exporter votre liste de flux depuis FreshRSS au format OPML
+* Pour sauvegarder les articles eux-même, vous pouvez utiliser [phpMyAdmin](http://www.phpmyadmin.net) ou les outils de MySQL :
+
+```bash
+mysqldump -u utilisateur -p --databases freshrss > freshrss.sql
+```
+
+
+# Bibliothèques incluses
+* [SimplePie](http://simplepie.org/)
+* [MINZ](https://github.com/marienfressinaud/MINZ)
+* [php-http-304](http://alexandre.alapetite.fr/doc-alex/php-http-304/)
+* [jQuery](http://jquery.com/)
+* [keyboard_shortcuts](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
+* [flotr2](http://www.humblesoftware.com/flotr2)
+
+## Uniquement pour certaines options
+* [bcrypt.js](https://github.com/dcodeIO/bcrypt.js)
+* [phpQuery](http://code.google.com/p/phpquery/)
+
+## Si les fonctions natives ne sont pas disponibles
+* [Services_JSON](http://pear.php.net/pepr/pepr-proposal-show.php?id=198)
+* [password_compat](https://github.com/ircmaxell/password_compat)

+ 66 - 64
README.md

@@ -1,88 +1,90 @@
+* [Version française](README.fr.md)
+
 # FreshRSS
 # FreshRSS
-FreshRSS est un agrégateur de flux RSS à auto-héberger à l’image de [Leed](http://projet.idleman.fr/leed/) ou de [Kriss Feed](http://tontof.net/kriss/feed/).
+FreshRSS is a self-hosted RSS feed agregator like [Leed](http://projet.idleman.fr/leed/) or [Kriss Feed](http://tontof.net/kriss/feed/).
 
 
-Il se veut léger et facile à prendre en main tout en étant un outil puissant et paramétrable.
+It is at the same time light-weight, easy to work with, powerful and customizable.
 
 
-Il permet de gérer plusieurs utilisateurs, et dispose d’un mode de lecture anonyme.
+It is a multi-user application with an anonymous reading mode.
 
 
-* Site officiel : http://freshrss.org
-* Démo : http://demo.freshrss.org/
-* Développeur : Marien Fressinaud <dev@marienfressinaud.fr>
-* Version actuelle : 0.8-dev
-* Date de publication 2014-0x-xx
+* Official website: http://freshrss.org
+* Demo: http://demo.freshrss.org/
+* Developer: Marien Fressinaud <dev@marienfressinaud.fr>
+* Current version: 0.8-dev
+* Publication date: 2014-0x-xx
 * License [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html)
 * License [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html)
 
 
-![Logo de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_title.png)
+![FreshRSS logo](http://marienfressinaud.fr/data/images/freshrss/freshrss_title.png)
 
 
-# Note sur les branches
-**Ce logiciel est encore en développement !** Veuillez vous assurer d'utiliser la branche qui vous correspond :
+# Note on branches
+**This application is still in development!** Please use the branch that suits your needs:
 
 
-* Utilisez [la branche master](https://github.com/marienfressinaud/FreshRSS/tree/master/) si vous visez la stabilité.
-* [La branche beta](https://github.com/marienfressinaud/FreshRSS/tree/beta) est celle par défaut : les nouveautés y sont ajoutées environ tous les mois.
-* Pour les développeurs et ceux qui savent ce qu'ils font, [la branche dev](https://github.com/marienfressinaud/FreshRSS/tree/dev) vous ouvre les bras !
+* Use [the master branch](https://github.com/marienfressinaud/FreshRSS/tree/master/) if you need a stable version.
+* [The beta branch](https://github.com/marienfressinaud/FreshRSS/tree/beta) is the default branch: new features are added on a monthly basis.
+* For developers and tech savvy persons, [the dev branch](https://github.com/marienfressinaud/FreshRSS/tree/dev) is waiting for you!
 
 
 # Disclaimer
 # Disclaimer
-Cette application a été développée pour s’adapter à des besoins personnels et non professionnels.
-Je ne garantis en aucun cas la sécurité de celle-ci, ni son bon fonctionnement.
-Je m’engage néanmoins à répondre dans la mesure du possible aux demandes d’évolution si celles-ci me semblent justifiées.
-Privilégiez pour cela des demandes sur GitHub
-(https://github.com/marienfressinaud/FreshRSS/issues) ou par mail (dev@marienfressinaud.fr)
-
-# Pré-requis
-* Serveur modeste, par exemple sous Linux ou Windows
-	* Fonctionne même sur un Raspberry Pi avec des temps de réponse < 1s (testé sur 150 flux, 22k articles, soit 32Mo de données partiellement compressées)
-* Serveur Web Apache2 ou Nginx (non testé sur les autres)
-* PHP 5.2.1+ (PHP 5.3.7+ recommandé)
-	* 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), [Zip](http://php.net/zip)
-* MySQL 5.0.3+ (recommandé) ou SQLite 3.7.4+ (en bêta)
-* Un navigateur Web récent tel Firefox 4+, Chrome, Opera, Safari, Internet Explorer 9+
-	* Fonctionne aussi sur mobile
-
-![Capture d’écran de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_default-design.png)
+This application was developed to fulfill personal needs not professional needs.
+There is no guarantee neither on its security nor its proper functioning.
+If there is feature requests which I think are good for the project, I'll do my best to include them.
+The best way is to open issues on GitHub
+(https://github.com/marienfressinaud/FreshRSS/issues) or by email (dev@marienfressinaud.fr)
+
+# Requirements
+* Light server running Linux or Windows
+	* It even works on Raspberry Pi with response time under a second (tested with 150 feeds, 22k articles, or 32Mo of compressed data)
+* A web server: Apache2 (recommanded), nginx, lighttpd (not tested on others)
+* PHP 5.2.1+ (PHP 5.3.7+ recommanded)
+	* Required extensions: [PDO_MySQL](http://php.net/pdo-mysql) or [PDO_SQLite](http://php.net/pdo-sqlite), [cURL](http://php.net/curl), [GMP](http://php.net/gmp) (only for API access on platforms under 64 bits)
+	* Recommanded extensions : [JSON](http://php.net/json), [mbstring](http://php.net/mbstring), [zlib](http://php.net/zlib), [Zip](http://php.net/zip)
+* MySQL 5.0.3+ (recommanded) ou SQLite 3.7.4+
+* A recent browser like Firefox 4+, Chrome, Opera, Safari, Internet Explorer 9+
+	* Works on mobile
+
+![FreshRSS screenshot](http://marienfressinaud.fr/data/images/freshrss/freshrss_default-design.png)
 
 
 # Installation
 # Installation
-1. Récupérez l’application FreshRSS via la commande git ou [en téléchargeant l’archive](https://github.com/marienfressinaud/FreshRSS/archive/master.zip)
-2. Placez l’application sur votre serveur (la partie à exposer au Web est le répertoire `./p/`)
-3. Le serveur Web doit avoir les droits d’écriture dans le répertoire `./data/`
-4. Accédez à FreshRSS à travers votre navigateur Web et suivez les instructions d’installation
-5. Tout devrait fonctionner :) En cas de problème, n’hésitez pas à me contacter.
-
-# Contrôle d’accès
-Il est requis pour le mode multi-utilisateur, et recommandé dans tous les cas, de limiter l’accès à votre FreshRSS. Au choix :
-* En utilisant l’identification par formulaire (requiert JavaScript, et PHP 5.3.7+ recommandé – fonctionne avec certaines versions de PHP 5.3.3+)
-* En utilisant l’identification par [Mozilla Persona](https://login.persona.org/about) incluse dans FreshRSS
-* En utilisant un contrôle d’accès HTTP défini par votre serveur Web
-	* Voir par exemple la [documentation d’Apache sur l’authentification](http://httpd.apache.org/docs/trunk/howto/auth.html)
-		* Créer dans ce cas un fichier `./p/i/.htaccess` avec un fichier `.htpasswd` correspondant.
-
-# Rafraîchissement automatique des flux
-* Vous pouvez ajouter une tâche Cron lançant régulièrement le script d’actualisation automatique des flux.
-Consultez la documentation de Cron de votre système d’exploitation ([Debian/Ubuntu](http://doc.ubuntu-fr.org/cron), [Red Hat/Fedora](http://doc.fedora-fr.org/wiki/CRON_:_Configuration_de_t%C3%A2ches_automatis%C3%A9es), [Slackware](http://docs.slackware.com/fr:slackbook:process_control?#cron), [Gentoo](http://wiki.gentoo.org/wiki/Cron/fr), [Arch Linux](http://wiki.archlinux.fr/Cron)…).
-C’est une bonne idée d’utiliser le même utilisateur que votre serveur Web (souvent “www-data”).
-Par exemple, pour exécuter le script toutes les heures :
+1. Get FreshRSS with git or [by downloading the archive](https://github.com/marienfressinaud/FreshRSS/archive/master.zip)
+2. Dump the application on your server (expose only the `./p/` folder)
+3. Add write access on `./data/` folder to the webserver user
+4. Access FreshRSS with your browser and follow the installation process
+5. Every thing should be working :) If you encounter any problem, feel free to contact me.
+
+# Access control
+It is needed for the multi-user mode to limit access to FreshRSS. You can:
+* use form authentication (need JavaScript and PHP 5.3.7+, works with some PHP 5.3.3+)
+* use [Mozilla Persona](https://login.persona.org/about) authentication included in FreshRSS
+* use HTTP authentication supported by your web server
+	* See [Apache documentation](http://httpd.apache.org/docs/trunk/howto/auth.html)
+		* In that case, create a `./p/i/.htaccess` file with a matching `.htpasswd` file.
+
+# Automatic feed update
+* You can add a Cron job to launch the update script.
+Check the Cron documentation related to your distribution ([Debian/Ubuntu](https://help.ubuntu.com/community/CronHowto), [Red Hat/Fedora](https://fedoraproject.org/wiki/Administration_Guide_Draft/Cron), [Slackware](http://docs.slackware.com/fr:slackbook:process_control?#cron), [Gentoo](https://wiki.gentoo.org/wiki/Cron), [Arch Linux](https://wiki.archlinux.org/index.php/Cron)…).
+It’s a good idea to use the web server user .
+For example, if you want to run the script every hour:
 
 
 ```
 ```
 7 * * * * php /chemin/vers/FreshRSS/app/actualize_script.php > /tmp/FreshRSS.log 2>&1
 7 * * * * php /chemin/vers/FreshRSS/app/actualize_script.php > /tmp/FreshRSS.log 2>&1
 ```
 ```
 
 
-# Conseils
-* Pour une meilleure sécurité, faites en sorte que seul le répertoire `./p/` soit accessible depuis le Web, par exemple en faisant pointer un sous-domaine sur le répertoire `./p/`.
-	* En particulier, les données personnelles se trouvent dans le répertoire `./data/`.
-* Le fichier `./constants.php` définit les chemins d’accès aux répertoires clés de l’application. Si vous les bougez, tout se passe ici.
-* En cas de problème, les logs peuvent être utile à lire, soit depuis l’interface de FreshRSS, soit manuellement depuis `./data/log/*.log`.
+# Advices
+* For a better security, expose only the `./p/` folder on the web.
+	* Be aware that the `./data/` folder contain all personal data, so it is a bad idea to expose it.
+* The `./constants.php` file define access to application folder. If you want to customize your installation, every thing happens here.
+* If you encounter some problem, logs are accessibles from the interface or manually in `./data/log/*.log` files.
 
 
-# Sauvegarde
-* Il faut conserver vos fichiers `./data/config.php` ainsi que `./data/*_user.php` et éventuellement `./data/persona/`
-* Vous pouvez exporter votre liste de flux depuis FreshRSS au format OPML
-* Pour sauvegarder les articles eux-même, vous pouvez utiliser [phpMyAdmin](http://www.phpmyadmin.net) ou les outils de MySQL :
+# Backup
+* You need to keep `./data/config.php`, `./data/*_user.php` and `./data/persona/` files
+* You can export your feed list in OPML format from FreshRSS
+* To save articles, you can use [phpMyAdmin](http://www.phpmyadmin.net) or MySQL tools:
 
 
 ```bash
 ```bash
-mysqldump -u utilisateur -p --databases freshrss > freshrss.sql
+mysqldump -u user -p --databases freshrss > freshrss.sql
 ```
 ```
 
 
 
 
-# Bibliothèques incluses
+# Included libraries
 * [SimplePie](http://simplepie.org/)
 * [SimplePie](http://simplepie.org/)
 * [MINZ](https://github.com/marienfressinaud/MINZ)
 * [MINZ](https://github.com/marienfressinaud/MINZ)
 * [php-http-304](http://alexandre.alapetite.fr/doc-alex/php-http-304/)
 * [php-http-304](http://alexandre.alapetite.fr/doc-alex/php-http-304/)
@@ -90,10 +92,10 @@ mysqldump -u utilisateur -p --databases freshrss > freshrss.sql
 * [keyboard_shortcuts](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
 * [keyboard_shortcuts](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
 * [flotr2](http://www.humblesoftware.com/flotr2)
 * [flotr2](http://www.humblesoftware.com/flotr2)
 
 
-## Uniquement pour certaines options
+## Only for some options
 * [bcrypt.js](https://github.com/dcodeIO/bcrypt.js)
 * [bcrypt.js](https://github.com/dcodeIO/bcrypt.js)
 * [phpQuery](http://code.google.com/p/phpquery/)
 * [phpQuery](http://code.google.com/p/phpquery/)
 
 
-## Si les fonctions natives ne sont pas disponibles
+## If native functions are not available
 * [Services_JSON](http://pear.php.net/pepr/pepr-proposal-show.php?id=198)
 * [Services_JSON](http://pear.php.net/pepr/pepr-proposal-show.php?id=198)
 * [password_compat](https://github.com/ircmaxell/password_compat)
 * [password_compat](https://github.com/ircmaxell/password_compat)

+ 1 - 0
app/Controllers/configureController.php

@@ -184,6 +184,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			$this->view->conf->_default_view((int)Minz_Request::param('default_view', FreshRSS_Entry::STATE_ALL));
 			$this->view->conf->_default_view((int)Minz_Request::param('default_view', FreshRSS_Entry::STATE_ALL));
 			$this->view->conf->_auto_load_more(Minz_Request::param('auto_load_more', false));
 			$this->view->conf->_auto_load_more(Minz_Request::param('auto_load_more', false));
 			$this->view->conf->_display_posts(Minz_Request::param('display_posts', false));
 			$this->view->conf->_display_posts(Minz_Request::param('display_posts', false));
+			$this->view->conf->_display_categories(Minz_Request::param('display_categories', false));
 			$this->view->conf->_hide_read_feeds(Minz_Request::param('hide_read_feeds', false));
 			$this->view->conf->_hide_read_feeds(Minz_Request::param('hide_read_feeds', false));
 			$this->view->conf->_onread_jump_next(Minz_Request::param('onread_jump_next', false));
 			$this->view->conf->_onread_jump_next(Minz_Request::param('onread_jump_next', false));
 			$this->view->conf->_lazyload(Minz_Request::param('lazyload', false));
 			$this->view->conf->_lazyload(Minz_Request::param('lazyload', false));

+ 6 - 5
app/Controllers/indexController.php

@@ -76,14 +76,14 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 		);
 		);
 
 
 		// On récupère les différents éléments de filtrage
 		// On récupère les différents éléments de filtrage
-		$this->view->state = $state = Minz_Request::param ('state', $this->view->conf->default_view);
+		$this->view->state = Minz_Request::param('state', $this->view->conf->default_view);
 		$state_param = Minz_Request::param ('state', null);
 		$state_param = Minz_Request::param ('state', null);
 		$filter = Minz_Request::param ('search', '');
 		$filter = Minz_Request::param ('search', '');
 		$this->view->order = $order = Minz_Request::param ('order', $this->view->conf->sort_order);
 		$this->view->order = $order = Minz_Request::param ('order', $this->view->conf->sort_order);
 		$nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page);
 		$nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page);
 		$first = Minz_Request::param ('next', '');
 		$first = Minz_Request::param ('next', '');
 
 
-		if ($state === FreshRSS_Entry::STATE_NOT_READ) {	//Any unread article in this category at all?
+		if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ) {	//Any unread article in this category at all?
 			switch ($getType) {
 			switch ($getType) {
 				case 'a':
 				case 'a':
 					$hasUnread = $this->view->nb_not_read > 0;
 					$hasUnread = $this->view->nb_not_read > 0;
@@ -104,7 +104,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 					break;
 					break;
 			}
 			}
 			if (!$hasUnread && ($state_param === null)) {
 			if (!$hasUnread && ($state_param === null)) {
-				$this->view->state = $state = FreshRSS_Entry::STATE_ALL;
+				$this->view->state = FreshRSS_Entry::STATE_ALL;
 			}
 			}
 		}
 		}
 
 
@@ -117,11 +117,11 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 		$keepHistoryDefault = $this->view->conf->keep_history_default;
 		$keepHistoryDefault = $this->view->conf->keep_history_default;
 
 
 		try {
 		try {
-			$entries = $entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, true, $keepHistoryDefault);
+			$entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb + 1, $first, $filter, $date_min, true, $keepHistoryDefault);
 
 
 			// Si on a récupéré aucun article "non lus"
 			// Si on a récupéré aucun article "non lus"
 			// on essaye de récupérer tous les articles
 			// on essaye de récupérer tous les articles
-			if ($state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null) && ($filter == '')) {
+			if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null) && ($filter == '')) {
 				Minz_Log::record('Conflicting information about nbNotRead!', Minz_Log::DEBUG);
 				Minz_Log::record('Conflicting information about nbNotRead!', Minz_Log::DEBUG);
 				$feedDAO = FreshRSS_Factory::createFeedDao();
 				$feedDAO = FreshRSS_Factory::createFeedDao();
 				try {
 				try {
@@ -132,6 +132,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 				$this->view->state = FreshRSS_Entry::STATE_ALL;
 				$this->view->state = FreshRSS_Entry::STATE_ALL;
 				$entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault);
 				$entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault);
 			}
 			}
+			Minz_Request::_param('state', $this->view->state);
 
 
 			if (count($entries) <= $nb) {
 			if (count($entries) <= $nb) {
 				$this->view->nextId  = '';
 				$this->view->nextId  = '';

+ 2 - 0
app/Controllers/statsController.php

@@ -58,9 +58,11 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
 
 
 	public function repartitionAction() {
 	public function repartitionAction() {
 		$statsDAO = FreshRSS_Factory::createStatsDAO();
 		$statsDAO = FreshRSS_Factory::createStatsDAO();
+		$categoryDAO = new FreshRSS_CategoryDAO();
 		$feedDAO = FreshRSS_Factory::createFeedDao();
 		$feedDAO = FreshRSS_Factory::createFeedDao();
 		Minz_View::appendScript(Minz_Url::display('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js')));
 		Minz_View::appendScript(Minz_Url::display('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js')));
 		$id = Minz_Request::param ('id', null);
 		$id = Minz_Request::param ('id', null);
+		$this->view->categories = $categoryDAO->listCategories();
 		$this->view->feed = $feedDAO->searchById($id);
 		$this->view->feed = $feedDAO->searchById($id);
 		$this->view->days = $statsDAO->getDays();
 		$this->view->days = $statsDAO->getDays();
 		$this->view->months = $statsDAO->getMonths();
 		$this->view->months = $statsDAO->getMonths();

+ 4 - 0
app/Models/Configuration.php

@@ -17,6 +17,7 @@ class FreshRSS_Configuration {
 		'default_view' => FreshRSS_Entry::STATE_NOT_READ,
 		'default_view' => FreshRSS_Entry::STATE_NOT_READ,
 		'auto_load_more' => true,
 		'auto_load_more' => true,
 		'display_posts' => false,
 		'display_posts' => false,
+		'display_categories' => false,
 		'hide_read_feeds' => true,
 		'hide_read_feeds' => true,
 		'onread_jump_next' => true,
 		'onread_jump_next' => true,
 		'lazyload' => true,
 		'lazyload' => true,
@@ -142,6 +143,9 @@ class FreshRSS_Configuration {
 	public function _display_posts ($value) {
 	public function _display_posts ($value) {
 		$this->data['display_posts'] = ((bool)$value) && $value !== 'no';
 		$this->data['display_posts'] = ((bool)$value) && $value !== 'no';
 	}
 	}
+	public function _display_categories ($value) {
+		$this->data['display_categories'] = ((bool)$value) && $value !== 'no';
+	}
 	public function _hide_read_feeds($value) {
 	public function _hide_read_feeds($value) {
 		$this->data['hide_read_feeds'] = (bool)$value;
 		$this->data['hide_read_feeds'] = (bool)$value;
 	}
 	}

+ 1 - 1
app/Models/EntryDAO.php

@@ -230,7 +230,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 		}
 		}
 		$this->bd->beginTransaction();
 		$this->bd->beginTransaction();
 
 
-		$sql = 'UPDATE `' . $this->prefix . 'entry`  '
+		$sql = 'UPDATE `' . $this->prefix . 'entry` '
 			 . 'SET is_read=1 '
 			 . 'SET is_read=1 '
 			 . 'WHERE id_feed=? AND is_read=0 AND id <= ?';
 			 . 'WHERE id_feed=? AND is_read=0 AND id <= ?';
 		$values = array($id, $idMax);
 		$values = array($id, $idMax);

+ 6 - 0
app/Models/Feed.php

@@ -28,6 +28,12 @@ class FreshRSS_Feed extends Minz_Model {
 		}
 		}
 	}
 	}
 
 
+	public static function example() {
+		$f = new FreshRSS_Feed('http://example.net/', false);
+		$f->faviconPrepare();
+		return $f;
+	}
+
 	public function id() {
 	public function id() {
 		return $this->id;
 		return $this->id;
 	}
 	}

+ 1 - 0
app/i18n/en.php

@@ -267,6 +267,7 @@ return array (
 	'sort_order'			=> 'Sort order',
 	'sort_order'			=> 'Sort order',
 	'auto_load_more'		=> 'Load next articles at the page bottom',
 	'auto_load_more'		=> 'Load next articles at the page bottom',
 	'display_articles_unfolded'	=> 'Show articles unfolded by default',
 	'display_articles_unfolded'	=> 'Show articles unfolded by default',
+	'display_categories_unfolded'	=> 'Show categories folded by default',
 	'hide_read_feeds'		=> 'Hide categories &amp; feeds with no unread article (only in “unread articles” display mode)',
 	'hide_read_feeds'		=> 'Hide categories &amp; feeds with no unread article (only in “unread articles” display mode)',
 	'after_onread'			=> 'After “mark all as read”,',
 	'after_onread'			=> 'After “mark all as read”,',
 	'jump_next'			=> 'jump to next unread sibling (feed or category)',
 	'jump_next'			=> 'jump to next unread sibling (feed or category)',

+ 1 - 0
app/i18n/fr.php

@@ -267,6 +267,7 @@ return array (
 	'sort_order'			=> 'Ordre de tri',
 	'sort_order'			=> 'Ordre de tri',
 	'auto_load_more'		=> 'Charger les articles suivants en bas de page',
 	'auto_load_more'		=> 'Charger les articles suivants en bas de page',
 	'display_articles_unfolded'	=> 'Afficher les articles dépliés par défaut',
 	'display_articles_unfolded'	=> 'Afficher les articles dépliés par défaut',
+	'display_categories_unfolded'	=> 'Afficher les catégories pliées par défaut',
 	'hide_read_feeds'		=> 'Cacher les catégories &amp; flux sans article non-lu (uniquement en affichage “articles non lus”)',
 	'hide_read_feeds'		=> 'Cacher les catégories &amp; flux sans article non-lu (uniquement en affichage “articles non lus”)',
 	'after_onread'			=> 'Après “marquer tout comme lu”,',
 	'after_onread'			=> 'Après “marquer tout comme lu”,',
 	'jump_next'			=> 'sauter au prochain voisin non lu (flux ou catégorie)',
 	'jump_next'			=> 'sauter au prochain voisin non lu (flux ou catégorie)',

+ 8 - 2
app/layout/aside_flux.phtml

@@ -42,8 +42,14 @@
 			$feeds = $cat->feeds ();
 			$feeds = $cat->feeds ();
 			if (!empty ($feeds)) {
 			if (!empty ($feeds)) {
 				$c_active = false;
 				$c_active = false;
-				if ($this->get_c == $cat->id ()) {
-					$c_active = true;
+				if ($this->conf->display_categories) {
+					if ($this->get_c == $cat->id () && $this->get_f) {
+						$c_active = true;
+					}
+				} else {
+					if ($this->get_c == $cat->id ()) {
+						$c_active = true;
+					}
 				}
 				}
 				?><li data-unread="<?php echo $cat->nbNotRead(); ?>"<?php if ($c_active) echo ' class="active"'; ?>><?php
 				?><li data-unread="<?php echo $cat->nbNotRead(); ?>"<?php if ($c_active) echo ' class="active"'; ?>><?php
 				?><div class="category stick<?php echo $c_active ? ' active' : ''; ?>"><?php
 				?><div class="category stick<?php echo $c_active ? ' active' : ''; ?>"><?php

+ 9 - 5
app/layout/nav_menu.phtml

@@ -164,11 +164,15 @@
 					break;
 					break;
 			}
 			}
 		}
 		}
-		if ($this->order === 'ASC') {
-			$idMax = 0;
-		} else {
-			$p = isset($this->entries[0]) ? $this->entries[0] : null;
-			$idMax = $p === null ? '0' : $p->id();
+
+		$p = isset($this->entries[0]) ? $this->entries[0] : null;
+		$idMax = $p === null ? (time() - 1) . '000000' : $p->id();
+
+		if ($this->order === 'ASC') {	//In this case we do not know but we guess idMax
+			$idMax2 = (time() - 1) . '000000';
+			if (strcmp($idMax2, $idMax) > 0) {
+				$idMax = $idMax2;
+			}
 		}
 		}
 
 
 		$arUrl = array('c' => 'entry', 'a' => 'read', 'params' => array('get' => $get, 'nextGet' => $nextGet, 'idMax' => $idMax));
 		$arUrl = array('c' => 'entry', 'a' => 'read', 'params' => array('get' => $get, 'nextGet' => $nextGet, 'idMax' => $idMax));

+ 10 - 0
app/views/configure/reading.phtml

@@ -61,6 +61,16 @@
 			</div>
 			</div>
 		</div>
 		</div>
 
 
+		<div class="form-group">
+			<div class="group-controls">
+				<label class="checkbox" for="display_categories">
+					<input type="checkbox" name="display_categories" id="display_categories" value="1"<?php echo $this->conf->display_categories ? ' checked="checked"' : ''; ?> />
+					<?php echo Minz_Translate::t ('display_categories_unfolded'); ?>
+					<noscript> — <strong><?php echo Minz_Translate::t ('javascript_should_be_activated'); ?></strong></noscript>
+				</label>
+			</div>
+		</div>
+
 		<div class="form-group">
 		<div class="form-group">
 			<div class="group-controls">
 			<div class="group-controls">
 				<label class="checkbox" for="sticky_post">
 				<label class="checkbox" for="sticky_post">

+ 6 - 1
app/views/helpers/view/normal_view.phtml

@@ -81,7 +81,12 @@ if (!empty($this->entries)) {
 				}
 				}
 			}
 			}
 			$feed = FreshRSS_CategoryDAO::findFeed($this->cat_aside, $item->feed ());	//We most likely already have the feed object in cache
 			$feed = FreshRSS_CategoryDAO::findFeed($this->cat_aside, $item->feed ());	//We most likely already have the feed object in cache
-			if (empty($feed)) $feed = $item->feed (true);
+			if ($feed == null) {
+				$feed = $item->feed(true);
+				if ($feed == null) {
+					$feed = FreshRSS_Feed::example();
+				}
+			}
 			?><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 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>
 			<li class="item title"><a target="_blank" href="<?php echo $item->link (); ?>"><?php echo $item->title (); ?></a></li>
 			<?php if ($topline_date) { ?><li class="item date"><?php echo $item->date (); ?> </li><?php } ?>
 			<?php if ($topline_date) { ?><li class="item date"><?php echo $item->date (); ?> </li><?php } ?>

+ 27 - 12
app/views/stats/repartition.phtml

@@ -2,23 +2,38 @@
 
 
 <div class="post content">
 <div class="post content">
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a>
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a>
-	
+
+	<h1><?php echo _t('stats_repartition'); ?></h1>
+
+	<select id="feed_select">
+		<option data-url="<?php echo _url('stats', 'repartition')?>"><?php echo _t('all_feeds')?></option>
+	<?php foreach ($this->categories as $category) {
+		$feeds = $category->feeds();
+		if (!empty($feeds)) {
+			echo '<optgroup label=', $category->name(), '>';
+			foreach ($feeds as $feed) {
+				if ($this->feed && $feed->id() == $this->feed->id()){
+					echo '<option value ="', $feed->id(), '" selected data-url="', _url('stats', 'repartition', 'id', $feed->id()), '">', $feed->name(), '</option>';
+				} else {
+					echo '<option value ="', $feed->id(), '" data-url="', _url('stats', 'repartition', 'id', $feed->id()), '">', $feed->name(), '</option>';
+				}
+			}
+			echo '</optgroup>';
+		}
+	}?>
+	</select>
+
 	<?php if ($this->feed) {?>
 	<?php if ($this->feed) {?>
-		<h1>
-			<?php echo _t('stats_repartition'), " - "; ?>
-			<a href="<?php echo _url('configure', 'feed', 'id', $this->feed->id()); ?>">
-				<?php echo $this->feed->name(); ?>
-			</a>
-		</h1>
-	<?php } else {?>
-		<h1><?php echo _t('stats_repartition'); ?></h1>
+		<a href="<?php echo _url('configure', 'feed', 'id', $this->feed->id()); ?>">
+			<?php echo _t('administration'); ?>
+		</a>
 	<?php }?>
 	<?php }?>
-	
+
 	<div class="stat">
 	<div class="stat">
 		<h2><?php echo _t('stats_entry_per_hour'); ?></h2>
 		<h2><?php echo _t('stats_entry_per_hour'); ?></h2>
 		<div id="statsEntryPerHour" style="height: 300px"></div>
 		<div id="statsEntryPerHour" style="height: 300px"></div>
 	</div>
 	</div>
-	
+
 	<div class="stat">
 	<div class="stat">
 		<h2><?php echo _t('stats_entry_per_day_of_week'); ?></h2>
 		<h2><?php echo _t('stats_entry_per_day_of_week'); ?></h2>
 		<div id="statsEntryPerDayOfWeek" style="height: 300px"></div>
 		<div id="statsEntryPerDayOfWeek" style="height: 300px"></div>
@@ -93,7 +108,7 @@ function initStats() {
 			yaxis: {min: 0},
 			yaxis: {min: 0},
 			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
 			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
 		});
 		});
-	
+
 }
 }
 initStats();
 initStats();
 </script>
 </script>

+ 1 - 0
p/api/greader.php

@@ -135,6 +135,7 @@ function checkCompatibility() {
 	}
 	}
 	if ((!array_key_exists('HTTP_AUTHORIZATION', $_SERVER)) &&	//Apache mod_rewrite trick should be fine
 	if ((!array_key_exists('HTTP_AUTHORIZATION', $_SERVER)) &&	//Apache mod_rewrite trick should be fine
 		(empty($_SERVER['SERVER_SOFTWARE']) || (stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') === false)) &&	//nginx should be fine
 		(empty($_SERVER['SERVER_SOFTWARE']) || (stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') === false)) &&	//nginx should be fine
+		(empty($_SERVER['SERVER_SOFTWARE']) || (stripos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') === false)) &&	//lighttpd should be fine
 		((!function_exists('getallheaders')) || (stripos(php_sapi_name(), 'cgi') !== false))) {	//Main problem is Apache/CGI mode
 		((!function_exists('getallheaders')) || (stripos(php_sapi_name(), 'cgi') !== false))) {	//Main problem is Apache/CGI mode
 		die('FAIL getallheaders! (probably)');
 		die('FAIL getallheaders! (probably)');
 	}
 	}

+ 7 - 0
p/scripts/main.js

@@ -1063,6 +1063,12 @@ function init_share_observers() {
 	});
 	});
 }
 }
 
 
+function init_stats_observers() {
+	$('#feed_select').on('change', function(e) {
+		redirect($(this).find(':selected').data('url'));
+	});
+}
+
 function init_remove_observers() {
 function init_remove_observers() {
 	$('.post').on('click', 'a.remove', function(e) {
 	$('.post').on('click', 'a.remove', function(e) {
 		var remove_what = $(this).attr('data-remove');
 		var remove_what = $(this).attr('data-remove');
@@ -1177,6 +1183,7 @@ function init_all() {
 		init_remove_observers();
 		init_remove_observers();
 		init_feed_observers();
 		init_feed_observers();
 		init_password_observers();
 		init_password_observers();
+		init_stats_observers();
 	}
 	}
 
 
 	if (window.console) {
 	if (window.console) {