Browse Source

Merge branch 'FreshRSS/dev' into PostgreSQL

Alexandre Alapetite 9 years ago
parent
commit
967dff535d
57 changed files with 310 additions and 180 deletions
  1. 12 2
      CHANGELOG.md
  2. 4 2
      README.fr.md
  3. 4 2
      README.md
  4. 2 2
      app/Controllers/configureController.php
  5. 7 7
      app/Controllers/indexController.php
  6. 1 1
      app/FreshRSS.php
  7. 21 0
      app/Models/Auth.php
  8. 3 2
      app/Models/EntryDAOSQLite.php
  9. 1 1
      app/i18n/cz/index.php
  10. 1 1
      app/i18n/de/index.php
  11. 1 1
      app/i18n/en/index.php
  12. 1 1
      app/i18n/fr/index.php
  13. 1 1
      app/i18n/it/index.php
  14. 1 1
      app/i18n/nl/index.php
  15. 1 1
      app/i18n/ru/index.php
  16. 1 1
      app/i18n/tr/index.php
  17. 1 0
      app/layout/aside_feed.phtml
  18. 3 1
      app/layout/layout.phtml
  19. 1 0
      app/layout/nav_menu.phtml
  20. 1 0
      app/views/auth/formLogin.phtml
  21. 1 0
      app/views/auth/index.phtml
  22. 28 27
      app/views/auth/register.phtml
  23. 2 0
      app/views/configure/archiving.phtml
  24. 1 0
      app/views/configure/display.phtml
  25. 1 0
      app/views/configure/queries.phtml
  26. 1 0
      app/views/configure/reading.phtml
  27. 1 0
      app/views/configure/sharing.phtml
  28. 1 0
      app/views/configure/shortcut.phtml
  29. 1 0
      app/views/configure/system.phtml
  30. 9 10
      app/views/entry/bookmark.phtml
  31. 9 10
      app/views/entry/read.phtml
  32. 1 0
      app/views/extension/index.phtml
  33. 1 0
      app/views/feed/add.phtml
  34. 1 0
      app/views/helpers/feed/update.phtml
  35. 1 0
      app/views/helpers/javascript_vars.phtml
  36. 1 1
      app/views/helpers/logs_pagination.phtml
  37. 1 0
      app/views/helpers/pagination.phtml
  38. 2 0
      app/views/importExport/index.phtml
  39. 1 1
      app/views/index/global.phtml
  40. 1 0
      app/views/index/logs.phtml
  41. 1 0
      app/views/stats/idle.phtml
  42. 6 1
      app/views/subscription/index.phtml
  43. 2 0
      app/views/user/manage.phtml
  44. 2 0
      app/views/user/profile.phtml
  45. 3 0
      data/config.default.php
  46. BIN
      doc/FreshRSS-logo.png
  47. BIN
      doc/FreshRSS-screenshot.png
  48. 5 3
      lib/http-conditional.php
  49. 30 1
      lib/lib_opml.php
  50. 1 1
      lib/lib_rss.php
  51. 25 19
      p/scripts/category.js
  52. 3 0
      p/scripts/global_view.js
  53. 1 0
      p/scripts/install.js
  54. 0 1
      p/scripts/jquery.min.js
  55. 86 69
      p/scripts/main.js
  56. 9 9
      p/scripts/repartition.js
  57. 3 0
      p/scripts/stats.js

+ 12 - 2
CHANGELOG.md

@@ -1,21 +1,31 @@
-# Changelog
+# Changelog
 
 ## 2016-XX-XX FreshRSS 1.5.x-dev
 
 * Compatibility
 	* Require at least MySQL 5.5.3+ [#1153](https://github.com/FreshRSS/FreshRSS/issues/1153)
 	* Require at least PHP 5.3.3+ [#1183](https://github.com/FreshRSS/FreshRSS/pull/1183)
+		* Restore compatibility with PHP 5.3.3 [#1208](https://github.com/FreshRSS/FreshRSS/issues/1208)
 	* Restore compatibility with Microsoft Internet Explorer 11 / Edge [#772](https://github.com/FreshRSS/FreshRSS/issues/772)
 * Features
 	* Support for full Unicode such as emoji 💕 in MySQL with utf8mb4 [#1153](https://github.com/FreshRSS/FreshRSS/issues/1153)
 		* FreshRSS will automatically migrate MySQL tables to utf8mb4 the first time it is needed.
 * Security
 	* Remove Mozilla Persona login (the service closes on 2016-11-30) [#1052](https://github.com/FreshRSS/FreshRSS/issues/1052)
+	* Use Referrer Policy `<meta name="referrer" content="never" />` for anonymizing HTTP Referer [#955](https://github.com/FreshRSS/FreshRSS/issues/955)
+	* Implement CSRF tokens for POST security [#570](https://github.com/FreshRSS/FreshRSS/issues/570)
 * Bug fixing
 	* Fixed scroll in log view [#1178](https://github.com/FreshRSS/FreshRSS/issues/1178)
 	* Fixed JavaScript bug when articles were not always marked as read [#1123](https://github.com/FreshRSS/FreshRSS/issues/1123)
+	* Fixed Apache Etag issue that prevented caching [#1199](https://github.com/FreshRSS/FreshRSS/pull/1199)
+	* Fixed OPML import of categories [#1202](https://github.com/FreshRSS/FreshRSS/issues/1202)
+* UI
+	* Updated to jQuery 3.1.0 and several JavaScript fixes (e.g. drag & drop) [#1197](https://github.com/FreshRSS/FreshRSS/pull/1197)
 * API
 	* Add API link in FreshRSS profile settings to ease set-up [#1186](https://github.com/FreshRSS/FreshRSS/pull/1186)
+* Mics.
+	* JSHint of JavaScript code and better initialisation [#1196](https://github.com/FreshRSS/FreshRSS/pull/1196)
+	* Updated credits, and images in README [#1201](https://github.com/FreshRSS/FreshRSS/issues/1201)
 
 
 ## 2016-07-23 FreshRSS 1.4.0
@@ -102,7 +112,7 @@
 	* Session cookie bug [#924](https://github.com/FreshRSS/FreshRSS/issues/924)
 	* Better error handling for PubSubHubbub [#939](https://github.com/FreshRSS/FreshRSS/issues/939)
 	* Fix tag search link from articles [#970](https://github.com/FreshRSS/FreshRSS/issues/970)
-	* Fix all quieries deleted when deleting a feed or category [#982](https://github.com/FreshRSS/FreshRSS/pull/982)
+	* Fix all queries deleted when deleting a feed or category [#982](https://github.com/FreshRSS/FreshRSS/pull/982)
 
 
 ## 2015-07-30 FreshRSS 1.1.2-beta

+ 4 - 2
README.fr.md

@@ -12,7 +12,7 @@ Il supporte [PubSubHubbub](https://code.google.com/p/pubsubhubbub/) pour des not
 * Démo : http://demo.freshrss.org/
 * Licence : [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html)
 
-![Logo de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_title.png)
+![Logo de FreshRSS](./doc/FreshRSS-logo.png)
 
 # Téléchargement
 Voir la [liste des versions](../../releases).
@@ -41,7 +41,7 @@ Nous sommes une communauté amicale.
 	* Fonctionne aussi sur mobile
 * L’entête HTTP `Referer` ne doit pas être désactivé pour pouvoir utiliser le formulaire de connexion
 
-![Capture d’écran de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_default-design.png)
+![Capture d’écran de FreshRSS](./doc/FreshRSS-screenshot.png)
 
 # Installation
 1. Récupérez l’application FreshRSS via la commande git ou [en téléchargeant l’archive](../releases)
@@ -124,6 +124,8 @@ mysqldump -u utilisateur -p --databases freshrss > freshrss.sql
 * [MINZ](https://github.com/marienfressinaud/MINZ)
 * [php-http-304](http://alexandre.alapetite.fr/doc-alex/php-http-304/)
 * [jQuery](http://jquery.com/)
+* [ArthurHoaro/favicon](https://github.com/ArthurHoaro/favicon)
+* [lib_opml](https://github.com/marienfressinaud/lib_opml)
 * [keyboard_shortcuts](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
 * [flotr2](http://www.humblesoftware.com/flotr2)
 

+ 4 - 2
README.md

@@ -12,7 +12,7 @@ It supports [PubSubHubbub](https://code.google.com/p/pubsubhubbub/) for instant
 * Demo: http://demo.freshrss.org/
 * License: [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html)
 
-![FreshRSS logo](http://marienfressinaud.fr/data/images/freshrss/freshrss_title.png)
+![FreshRSS logo](./doc/FreshRSS-logo.png)
 
 # Releases
 See the [list of releases](../../releases).
@@ -41,7 +41,7 @@ We are a friendly community.
 	* Works on mobile
 * The browser HTTP `Referer` header must not be disabled when using the form login method
 
-![FreshRSS screenshot](http://marienfressinaud.fr/data/images/freshrss/freshrss_default-design.png)
+![FreshRSS screenshot](./doc/FreshRSS-screenshot.png)
 
 # Installation
 1. Get FreshRSS with git or [by downloading the archive](https://github.com/FreshRSS/FreshRSS/archive/master.zip)
@@ -124,6 +124,8 @@ mysqldump -u user -p --databases freshrss > freshrss.sql
 * [MINZ](https://github.com/marienfressinaud/MINZ)
 * [php-http-304](http://alexandre.alapetite.fr/doc-alex/php-http-304/)
 * [jQuery](http://jquery.com/)
+* [ArthurHoaro/favicon](https://github.com/ArthurHoaro/favicon)
+* [lib_opml](https://github.com/marienfressinaud/lib_opml)
 * [keyboard_shortcuts](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
 * [flotr2](http://www.humblesoftware.com/flotr2)
 

+ 2 - 2
app/Controllers/configureController.php

@@ -139,7 +139,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 	 */
 	public function sharingAction() {
 		if (Minz_Request::isPost()) {
-			$params = Minz_Request::params();
+			$params = Minz_Request::fetchGET();
 			FreshRSS_Context::$user_conf->sharing = $params['share'];
 			FreshRSS_Context::$user_conf->save();
 			invalidateHttpCache();
@@ -282,7 +282,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 		foreach (FreshRSS_Context::$user_conf->queries as $key => $query) {
 			$queries[$key] = new FreshRSS_UserQuery($query, $feed_dao, $category_dao);
 		}
-		$params = Minz_Request::params();
+		$params = Minz_Request::fetchGET();
 		$params['url'] = Minz_Url::display(array('params' => $params));
 		$params['name'] = _t('conf.query.number', count($queries) + 1);
 		$queries[] = new FreshRSS_UserQuery($params, $feed_dao, $category_dao);

+ 7 - 7
app/Controllers/indexController.php

@@ -32,9 +32,9 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 			Minz_Error::error(404);
 		}
 
-		$this->view->callbackBeforeContent = function() {
+		$this->view->callbackBeforeContent = function($view) {
 			try {
-				$entries = $this->listEntriesByContext();
+				$entries = FreshRSS_index_Controller::listEntriesByContext();
 
 				$nb_entries = count($entries);
 				if ($nb_entries > FreshRSS_Context::$number) {
@@ -55,15 +55,15 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 					}
 				}
 
-				$this->view->entries = $entries;
+				$view->entries = $entries;
 			} catch (FreshRSS_EntriesGetter_Exception $e) {
 				Minz_Log::notice($e->getMessage());
 				Minz_Error::error(404);
 			}
 
-			$this->view->categories = FreshRSS_Context::$categories;
+			$view->categories = FreshRSS_Context::$categories;
 
-			$this->view->rss_title = FreshRSS_Context::$name . ' | ' . Minz_View::title();
+			$view->rss_title = FreshRSS_Context::$name . ' | ' . Minz_View::title();
 			$title = FreshRSS_Context::$name;
 			if (FreshRSS_Context::$get_unread > 0) {
 				$title = '(' . FreshRSS_Context::$get_unread . ') ' . $title;
@@ -132,7 +132,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 		}
 
 		try {
-			$this->view->entries = $this->listEntriesByContext();
+			$this->view->entries = FreshRSS_index_Controller::listEntriesByContext();
 		} catch (FreshRSS_EntriesGetter_Exception $e) {
 			Minz_Log::notice($e->getMessage());
 			Minz_Error::error(404);
@@ -189,7 +189,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 	/**
 	 * This method returns a list of entries based on the Context object.
 	 */
-	private function listEntriesByContext() {
+	public static function listEntriesByContext() {
 		$entryDAO = FreshRSS_Factory::createEntryDao();
 
 		$get = FreshRSS_Context::currentGet(true);

+ 1 - 1
app/FreshRSS.php

@@ -57,7 +57,7 @@ class FreshRSS extends Minz_FrontController {
 
 	private static function initAuth() {
 		FreshRSS_Auth::init();
-		if (Minz_Request::isPost() && !is_referer_from_same_domain()) {
+		if (Minz_Request::isPost() && !(is_referer_from_same_domain() && FreshRSS_Auth::isCsrfOk())) {
 			// Basic protection against XSRF attacks
 			FreshRSS_Auth::removeAccess();
 			$http_referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];

+ 21 - 0
app/Models/Auth.php

@@ -124,6 +124,7 @@ class FreshRSS_Auth {
 		self::$login_ok = false;
 		$conf = Minz_Configuration::get('system');
 		Minz_Session::_param('currentUser', $conf->default_user);
+		Minz_Session::_param('csrf');
 
 		switch ($conf->auth_type) {
 		case 'form':
@@ -156,6 +157,26 @@ class FreshRSS_Auth {
 		$auth_type = $conf->auth_type;
 		return $auth_type === 'form';
 	}
+
+	public static function csrfToken() {
+		$csrf = Minz_Session::param('csrf');
+		if ($csrf == '') {
+			$salt = FreshRSS_Context::$system_conf->salt;
+			$csrf = sha1($salt . uniqid(mt_rand(), true));
+			Minz_Session::_param('csrf', $csrf);
+		}
+		return $csrf;
+	}
+	public static function isCsrfOk($token = null) {
+		$csrf = Minz_Session::param('csrf');
+		if ($csrf == '') {
+			return true;	//Not logged in yet
+		}
+		if ($token === null) {
+			$token = Minz_Request::fetchPOST('_csrf');
+		}
+		return $token === $csrf;
+	}
 }
 
 

+ 3 - 2
app/Models/EntryDAOSQLite.php

@@ -2,11 +2,12 @@
 
 class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
 
-	protected function autoAddColumn($errorInfo) {
+	protected function autoUpdateDb($errorInfo) {
 		if (empty($errorInfo[0]) || $errorInfo[0] == '42S22') {	//ER_BAD_FIELD_ERROR
+			//autoAddColumn
 			if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entry'")) {
 				$showCreate = $tableInfo->fetchColumn();
-				Minz_Log::debug('FreshRSS_EntryDAOSQLite::autoAddColumn: ' . $showCreate);
+				Minz_Log::debug('FreshRSS_EntryDAOSQLite::autoUpdateDb: ' . $showCreate);
 				foreach (array('lastSeen', 'hash') as $column) {
 					if (stripos($showCreate, $column) === false) {
 						return $this->addColumn($column);

+ 1 - 1
app/i18n/cz/index.php

@@ -6,7 +6,7 @@ return array(
 		'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
 		'bugs_reports' => 'Hlášení chyb',
 		'credits' => 'Poděkování',
-		'credits_content' => 'Některé designové prvky pocházejí z <a href="http://twitter.github.io/bootstrap/">Bootstrap</a>, FreshRSS ale tuto platformu nevyužívá. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Ikony</a> pocházejí z <a href="https://www.gnome.org/">GNOME projektu</a>. Font <em>Open Sans</em> vytvořil <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Favicony jsou shromažďovány pomocí <a href="https://getfavicon.appspot.com/">getFavicon API</a>. FreshRSS je založen na PHP framework <a href="https://github.com/marienfressinaud/MINZ">Minz</a>.',
+		'credits_content' => 'Některé designové prvky pocházejí z <a href="http://twitter.github.io/bootstrap/">Bootstrap</a>, FreshRSS ale tuto platformu nevyužívá. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Ikony</a> pocházejí z <a href="https://www.gnome.org/">GNOME projektu</a>. Font <em>Open Sans</em> vytvořil <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS je založen na PHP framework <a href="https://github.com/marienfressinaud/MINZ">Minz</a>.',
 		'freshrss_description' => 'FreshRSS je čtečka RSS kanálů určená k provozu na vlastním serveru, podobná <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> nebo <a href="http://projet.idleman.fr/leed/">Leed</a>. Je to nenáročný a jednoduchý, zároveň ale mocný a konfigurovatelný nástroj.',
 		'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">na Github</a>',
 		'license' => 'Licence',

+ 1 - 1
app/i18n/de/index.php

@@ -6,7 +6,7 @@ return array(
 		'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
 		'bugs_reports' => 'Fehlerberichte',
 		'credits' => 'Credits',
-		'credits_content' => 'Einige Designelemente stammen von <a href="http://twitter.github.io/bootstrap/">Bootstrap</a>, obwohl FreshRSS dieses Framework nicht nutzt. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> stammen vom <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> Font wurde von <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a> erstellt. Favicons werden mit <a href="https://getfavicon.appspot.com/">getFavicon API</a> gesammelt. FreshRSS basiert auf <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, einem PHP-Framework.',
+		'credits_content' => 'Einige Designelemente stammen von <a href="http://twitter.github.io/bootstrap/">Bootstrap</a>, obwohl FreshRSS dieses Framework nicht nutzt. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> stammen vom <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> Font wurde von <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a> erstellt. FreshRSS basiert auf <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, einem PHP-Framework.',
 		'freshrss_description' => 'FreshRSS ist ein RSS-Feedsaggregator zum selbst hosten wie zum Beispiel <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> oder <a href="http://projet.idleman.fr/leed/">Leed</a>. Er ist leicht und einfach zu handhaben und gleichzeitig ein leistungsstarkes und konfigurierbares Werkzeug.',
 		'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">on Github</a>',
 		'license' => 'Lizenz',

+ 1 - 1
app/i18n/en/index.php

@@ -6,7 +6,7 @@ return array(
 		'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
 		'bugs_reports' => 'Bugs reports',
 		'credits' => 'Credits',
-		'credits_content' => 'Some design elements come from <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> although FreshRSS doesn’t use this framework. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> come from <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> font police has been created by <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Favicons are collected with <a href="https://getfavicon.appspot.com/">getFavicon API</a>. FreshRSS is based on <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, a PHP framework.',
+		'credits_content' => 'Some design elements come from <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> although FreshRSS doesn’t use this framework. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> come from <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> font police has been created by <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS is based on <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, a PHP framework.',
 		'freshrss_description' => 'FreshRSS is a RSS feeds aggregator to self-host like <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> or <a href="http://projet.idleman.fr/leed/">Leed</a>. It is light and easy to take in hand while being powerful and configurable tool.',
 		'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">on Github</a>',
 		'license' => 'License',

+ 1 - 1
app/i18n/fr/index.php

@@ -6,7 +6,7 @@ return array(
 		'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
 		'bugs_reports' => 'Rapports de bugs',
 		'credits' => 'Crédits',
-		'credits_content' => 'Des éléments de design sont issus du <a href="http://twitter.github.io/bootstrap/">projet Bootstrap</a> bien que FreshRSS n’utilise pas ce framework. Les <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">icônes</a> sont issues du <a href="https://www.gnome.org/">projet GNOME</a>. La police <em>Open Sans</em> utilisée a été créée par <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Les favicons sont récupérés grâce au site <a href="https://getfavicon.appspot.com/">getFavicon</a>. FreshRSS repose sur <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, un framework PHP.',
+		'credits_content' => 'Des éléments de design sont issus du <a href="http://twitter.github.io/bootstrap/">projet Bootstrap</a> bien que FreshRSS n’utilise pas ce framework. Les <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">icônes</a> sont issues du <a href="https://www.gnome.org/">projet GNOME</a>. La police <em>Open Sans</em> utilisée a été créée par <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS repose sur <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, un framework PHP.',
 		'freshrss_description' => 'FreshRSS est un agrégateur de flux RSS à auto-héberger à l’image de <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> ou <a href="http://projet.idleman.fr/leed/">Leed</a>. Il se veut léger et facile à prendre en main tout en étant un outil puissant et paramétrable.',
 		'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">sur Github</a>',
 		'license' => 'Licence',

+ 1 - 1
app/i18n/it/index.php

@@ -6,7 +6,7 @@ return array(
 		'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
 		'bugs_reports' => 'Bugs',
 		'credits' => 'Crediti',
-		'credits_content' => 'Alcuni elementi di design provengono da <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> sebbene FreshRSS non usi questo framework. Le <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">icone</a> provengono dal progetto <a href="https://www.gnome.org/">GNOME</a>. Il carattere <em>Open Sans</em> è stato creato da <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Le Favicons vengono estratte con le API <a href="https://getfavicon.appspot.com/">getFavicon</a>. FreshRSS è basato su <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, un framework PHP.',
+		'credits_content' => 'Alcuni elementi di design provengono da <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> sebbene FreshRSS non usi questo framework. Le <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">icone</a> provengono dal progetto <a href="https://www.gnome.org/">GNOME</a>. Il carattere <em>Open Sans</em> è stato creato da <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS è basato su <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, un framework PHP.',
 		'freshrss_description' => 'FreshRSS è un aggregatore di feeds RSS da installare sul proprio host come <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> o <a href="http://projet.idleman.fr/leed/">Leed</a>. Leggero e facile da mantenere pur essendo molto configurabile e potente.',
 		'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">su Github</a>',
 		'license' => 'Licenza',

+ 1 - 1
app/i18n/nl/index.php

@@ -6,7 +6,7 @@ return array(
 		'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
 		'bugs_reports' => 'Rapporteer fouten',
 		'credits' => 'Waarderingen',
-		'credits_content' => 'Sommige ontwerp elementen komen van <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> alhoewel FreshRSS dit raamwerk niet gebruikt. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Pictogrammen</a> komen van het <a href="https://www.gnome.org/">GNOME project</a>. <em>De Open Sans</em> font police is gemaakt door <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Favicons zijn verzameld met de <a href="https://getfavicon.appspot.com/">getFavicon API</a>. FreshRSS is gebaseerd op <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, een PHP raamwerk. Nederlandse vertaling door Wanabo, <a href="http://www.nieuwskop.be" title="NieuwsKop">NieuwsKop.be</a>. Link naar de Nederlandse vertaling, <a href="https://github.com/Wanabo/FreshRSS-Dutch-translation/tree/master">FreshRSS-Dutch-translation</a>.',
+		'credits_content' => 'Sommige ontwerp elementen komen van <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> alhoewel FreshRSS dit raamwerk niet gebruikt. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Pictogrammen</a> komen van het <a href="https://www.gnome.org/">GNOME project</a>. <em>De Open Sans</em> font police is gemaakt door <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS is gebaseerd op <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, een PHP raamwerk. Nederlandse vertaling door Wanabo, <a href="http://www.nieuwskop.be" title="NieuwsKop">NieuwsKop.be</a>. Link naar de Nederlandse vertaling, <a href="https://github.com/Wanabo/FreshRSS-Dutch-translation/tree/master">FreshRSS-Dutch-translation</a>.',
 		'freshrss_description' => 'FreshRSS is een RSS feed aggregator om zelf te hosten zoals <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> of <a href="http://projet.idleman.fr/leed/">Leed</a>. Het gebruikt weinig systeembronnen en is makkelijk te administreren terwijl het een krachtig en makkelijk te configureren programma is.',
 		'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">op Github</a>',
 		'license' => 'License',

+ 1 - 1
app/i18n/ru/index.php

@@ -6,7 +6,7 @@ return array(
 		'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
 		'bugs_reports' => 'Bugs reports',
 		'credits' => 'Credits',
-		'credits_content' => 'Some design elements come from <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> although FreshRSS doesn’t use this framework. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> come from <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> font police has been created by <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Favicons are collected with <a href="https://getfavicon.appspot.com/">getFavicon API</a>. FreshRSS is based on <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, a PHP framework.',
+		'credits_content' => 'Some design elements come from <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> although FreshRSS doesn’t use this framework. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> come from <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> font police has been created by <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS is based on <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, a PHP framework.',
 		'freshrss_description' => 'FreshRSS is a RSS feeds aggregator to self-host like <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> or <a href="http://projet.idleman.fr/leed/">Leed</a>. It is light and easy to take in hand while being powerful and configurable tool.',
 		'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">on Github</a>',
 		'license' => 'License',

+ 1 - 1
app/i18n/tr/index.php

@@ -6,7 +6,7 @@ return array(
 		'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
 		'bugs_reports' => 'Hata raporu',
 		'credits' => 'Tanıtım',
-		'credits_content' => 'Bu frameworkü kullanmamasına rağmen FreshRSS bazı tasarım ögelerini <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> dan almıştır. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">İkonlar</a> <a href="https://www.gnome.org/">GNOME projesinden</a> alınmıştır. <em>Open Sans</em> yazı tipi <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a> tarafından oluşturulmuştur. Site ikonları <a href="https://getfavicon.appspot.com/">getFavicon API</a> ile oluşturuldu. FreshRSS bir PHP framework olan <a href="https://github.com/marienfressinaud/MINZ">Minz</a> i temel alır.',
+		'credits_content' => 'Bu frameworkü kullanmamasına rağmen FreshRSS bazı tasarım ögelerini <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> dan almıştır. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">İkonlar</a> <a href="https://www.gnome.org/">GNOME projesinden</a> alınmıştır. <em>Open Sans</em> yazı tipi <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a> tarafından oluşturulmuştur. FreshRSS bir PHP framework olan <a href="https://github.com/marienfressinaud/MINZ">Minz</a> i temel alır.',
 		'freshrss_description' => 'FreshRSS <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> veya <a href="http://projet.idleman.fr/leed/">Leed</a> gibi kendi hostunuzda çalışan bir RSS akış toplayıcısıdır. Güçlü ve yapılandırılabilir araçlarıyla basit ve kullanımı kolay bir uygulamadır.',
 		'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">Github sayfası</a>',
 		'license' => 'Lisans',

+ 1 - 0
app/layout/aside_feed.phtml

@@ -20,6 +20,7 @@
 	<?php } ?>
 
 	<form id="mark-read-aside" method="post">
+	<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 	<ul class="tree">
 		<li class="tree-folder category all<?php echo FreshRSS_Context::isCurrentGet('a') ? ' active' : ''; ?>">
 			<div class="tree-folder-title">

+ 3 - 1
app/layout/layout.phtml

@@ -23,7 +23,7 @@
 <?php
 	flush();
 	if (isset($this->callbackBeforeContent)) {
-		call_user_func($this->callbackBeforeContent);
+		call_user_func($this->callbackBeforeContent, $this);
 	}
 ?>
 		<?php echo self::headTitle(); ?>
@@ -41,6 +41,8 @@
 		$url_rss['a'] = 'rss';
 ?>
 		<link rel="alternate" type="application/rss+xml" title="<?php echo $this->rss_title; ?>" href="<?php echo Minz_Url::display($url_rss); ?>" />
+<?php } if (!FreshRSS_Context::$system_conf->allow_referrer) { ?>
+		<meta name="referrer" content="never" />
 <?php } if (FreshRSS_Context::$system_conf->allow_robots) { ?>
 		<meta name="description" content="<?php echo htmlspecialchars(FreshRSS_Context::$name . ' | ' . FreshRSS_Context::$description, ENT_COMPAT, 'UTF-8'); ?>" />
 <?php } else { ?>

+ 1 - 0
app/layout/nav_menu.phtml

@@ -88,6 +88,7 @@
 		        type="submit"><?php echo _t('gen.action.mark_read'); ?></button>
 
 		<div class="dropdown">
+			<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 			<div id="dropdown-read" class="dropdown-target"></div>
 
 			<a class="dropdown-toggle btn" href="#dropdown-read"><?php echo _i('down'); ?></a>

+ 1 - 0
app/views/auth/formLogin.phtml

@@ -6,6 +6,7 @@
 	<?php } ?>
 
 	<form id="crypto-form" method="post" action="<?php echo _url('auth', 'login'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<div>
 			<label for="username"><?php echo _t('gen.auth.username'); ?></label>
 			<input type="text" id="username" name="username" size="16" required="required" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" autofocus="autofocus" />

+ 1 - 0
app/views/auth/index.phtml

@@ -4,6 +4,7 @@
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a>
 
 	<form method="post" action="<?php echo _url('auth', 'index'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('admin.auth.type'); ?></legend>
 
 		<div class="form-group">

+ 28 - 27
app/views/auth/register.phtml

@@ -1,33 +1,34 @@
 <div class="prompt">
-    <h1><?php echo _t('gen.auth.registration'); ?></h1>
+	<h1><?php echo _t('gen.auth.registration'); ?></h1>
 
-    <form method="post" action="<?php echo _url('user', 'create'); ?>">
-        <div>
-            <label class="group-name" for="new_user_name"><?php echo _t('gen.auth.username'), '<br />', _i('help'), ' ', _t('gen.auth.username.format'); ?></label>
-            <input id="new_user_name" name="new_user_name" type="text" size="16" required="required" maxlength="16" autocomplete="off" pattern="[0-9a-zA-Z]{1,16}" />
-        </div>
+	<form method="post" action="<?php echo _url('user', 'create'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
+		<div>
+			<label class="group-name" for="new_user_name"><?php echo _t('gen.auth.username'), '<br />', _i('help'), ' ', _t('gen.auth.username.format'); ?></label>
+			<input id="new_user_name" name="new_user_name" type="text" size="16" required="required" maxlength="16" autocomplete="off" pattern="[0-9a-zA-Z]{1,16}" />
+		</div>
 
-        <div>
-            <label class="group-name" for="new_user_passwordPlain"><?php echo _t('gen.auth.password'), '<br />', _i('help'), ' ', _t('gen.auth.password.format'); ?></label>
-            <div class="stick">
-                <input type="password" id="new_user_passwordPlain" name="new_user_passwordPlain" required="required" autocomplete="off" pattern=".{7,}" />
-                <a class="btn toggle-password" data-toggle="new_user_passwordPlain"><?php echo _i('key'); ?></a>
-            </div>
-            <noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript>
-        </div>
+		<div>
+			<label class="group-name" for="new_user_passwordPlain"><?php echo _t('gen.auth.password'), '<br />', _i('help'), ' ', _t('gen.auth.password.format'); ?></label>
+			<div class="stick">
+				<input type="password" id="new_user_passwordPlain" name="new_user_passwordPlain" required="required" autocomplete="off" pattern=".{7,}" />
+				<a class="btn toggle-password" data-toggle="new_user_passwordPlain"><?php echo _i('key'); ?></a>
+			</div>
+			<noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript>
+		</div>
 
-        <div>
-            <?php
-                $redirect_url = urlencode(Minz_Url::display(
-                    array('c' => 'index', 'a' => 'index'),
-                    'php', true
-                ));
-            ?>
-            <input type="hidden" name="r" value="<?php echo $redirect_url; ?>" />
-            <button type="submit" class="btn btn-important"><?php echo _t('gen.action.create'); ?></button>
-            <a class="btn" href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.cancel'); ?></a>
-        </div>
-    </form>
+		<div>
+			<?php
+				$redirect_url = urlencode(Minz_Url::display(
+					array('c' => 'index', 'a' => 'index'),
+					'php', true
+				));
+			?>
+			<input type="hidden" name="r" value="<?php echo $redirect_url; ?>" />
+			<button type="submit" class="btn btn-important"><?php echo _t('gen.action.create'); ?></button>
+			<a class="btn" href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.cancel'); ?></a>
+		</div>
+	</form>
 
-    <p><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('gen.freshrss.about'); ?></a></p>
+	<p><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('gen.freshrss.about'); ?></a></p>
 </div>

+ 2 - 0
app/views/configure/archiving.phtml

@@ -4,6 +4,7 @@
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a>
 
 	<form method="post" action="<?php echo _url('configure', 'archiving'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('conf.archiving'); ?></legend>
 		<p><?php echo _i('help'); ?> <?php echo _t('conf.archiving.help'); ?></p>
 
@@ -55,6 +56,7 @@
 	</form>
 
 	<form method="post" action="<?php echo _url('entry', 'optimize'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('conf.archiving.advanced'); ?></legend>
 
 		<div class="form-group">

+ 1 - 0
app/views/configure/display.phtml

@@ -4,6 +4,7 @@
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a>
 
 	<form method="post" action="<?php echo _url('configure', 'display'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('conf.display'); ?></legend>
 
 		<div class="form-group">

+ 1 - 0
app/views/configure/queries.phtml

@@ -4,6 +4,7 @@
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a>
 
 	<form method="post" action="<?php echo _url('configure', 'queries'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('conf.query'); ?></legend>
 
 		<?php foreach ($this->queries as $key => $query) { ?>

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

@@ -4,6 +4,7 @@
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a>
 
 	<form method="post" action="<?php echo _url('configure', 'reading'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('conf.reading'); ?></legend>
 
 		<div class="form-group">

+ 1 - 0
app/views/configure/sharing.phtml

@@ -15,6 +15,7 @@
 			<a href="#" class="remove btn btn-attention" data-remove="group-share-##key##"><?php echo _i('close'); ?></a></div>
 			<a target="_blank" class="btn" title="<?php echo _t('conf.sharing.more_information'); ?>" href="##help##"><?php echo _i('help'); ?></a>
 			</div></div>'>
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('conf.sharing'); ?></legend>
 		<?php
 			foreach (FreshRSS_Context::$user_conf->sharing as $key => $share_options) {

+ 1 - 0
app/views/configure/shortcut.phtml

@@ -12,6 +12,7 @@
 	<?php $s = FreshRSS_Context::$user_conf->shortcuts; ?>
 
 	<form method="post" action="<?php echo _url('configure', 'shortcut'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('conf.shortcut'); ?></legend>
 
 		<noscript><p class="alert alert-error"><?php echo _t('conf.shortcut.javascript'); ?></p></noscript>

+ 1 - 0
app/views/configure/system.phtml

@@ -4,6 +4,7 @@
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a>
 
 	<form method="post" action="<?php echo _url('configure', 'system'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('admin.system'); ?></legend>
 
 		<div class="form-group">

+ 9 - 10
app/views/entry/bookmark.phtml

@@ -1,17 +1,16 @@
 <?php
 header('Content-Type: application/json; charset=UTF-8');
 
-if (Minz_Request::param('is_favorite', true)) {
-	Minz_Request::_param('is_favorite', 0);
-} else {
-	Minz_Request::_param('is_favorite', 1);
-}
-
-$url = Minz_Url::display(array(
+$url = array(
 	'c' => Minz_Request::controllerName(),
 	'a' => Minz_Request::actionName(),
-	'params' => Minz_Request::params(),
-));
+	'params' => Minz_Request::fetchGET(),
+);
+
+$url['params']['is_favorite'] = Minz_Request::param('is_favorite', true) ? '0' : '1';
 
 FreshRSS::loadStylesAndScripts();
-echo json_encode(array('url' => str_ireplace('&amp;', '&', $url), 'icon' => _i(Minz_Request::param('is_favorite') ? 'non-starred' : 'starred')));
+echo json_encode(array(
+		'url' => str_ireplace('&amp;', '&', Minz_Url::display($url)),
+		'icon' => _i($url['params']['is_favorite'] === '1' ? 'non-starred' : 'starred')
+	));

+ 9 - 10
app/views/entry/read.phtml

@@ -1,17 +1,16 @@
 <?php
 header('Content-Type: application/json; charset=UTF-8');
 
-if (Minz_Request::param('is_read', true)) {
-	Minz_Request::_param('is_read', 0);
-} else {
-	Minz_Request::_param('is_read', 1);
-}
-
-$url = Minz_Url::display(array(
+$url = array(
 	'c' => Minz_Request::controllerName(),
 	'a' => Minz_Request::actionName(),
-	'params' => Minz_Request::params(),
-));
+	'params' => Minz_Request::fetchGET(),
+);
+
+$url['params']['is_read'] = Minz_Request::param('is_read', true) ? '0' : '1';
 
 FreshRSS::loadStylesAndScripts();
-echo json_encode(array('url' => str_ireplace('&amp;', '&', $url), 'icon' => _i(Minz_Request::param('is_read') ? 'unread' : 'read')));
+echo json_encode(array(
+		'url' => str_ireplace('&amp;', '&', Minz_Url::display($url)),
+		'icon' => _i($url['params']['is_read'] === '1' ? 'unread' : 'read')
+	));

+ 1 - 0
app/views/extension/index.phtml

@@ -6,6 +6,7 @@
 	<h1><?php echo _t('admin.extensions.title'); ?></h1>
 
 	<form id="form-extension" method="post">
+	<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 	<?php if (!empty($this->extension_list['system'])) { ?>
 	<h2><?php echo _t('admin.extensions.system'); ?></h2>
 	<?php

+ 1 - 0
app/views/feed/add.phtml

@@ -7,6 +7,7 @@
 	<?php } ?>
 
 	<form method="post" action="<?php echo _url('feed', 'add'); ?>" autocomplete="off">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('sub.feed.informations'); ?></legend>
 		<?php if ($this->load_ok) { ?>
 		<div class="form-group">

+ 1 - 0
app/views/helpers/feed/update.phtml

@@ -18,6 +18,7 @@
 	<?php } ?>
 
 	<form method="post" action="<?php echo _url('subscription', 'feed', 'id', $this->feed->id()); ?>" autocomplete="off">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('sub.feed.informations'); ?></legend>
 		<div class="form-group">
 			<label class="group-name" for="name"><?php echo _t('sub.feed.title'); ?></label>

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

@@ -16,6 +16,7 @@ echo htmlspecialchars(json_encode(array(
 		'html5_notif_timeout' => FreshRSS_Context::$user_conf->html5_notif_timeout,
 		'auth_type' => FreshRSS_Context::$system_conf->auth_type,
 		'current_view' => Minz_Request::actionName(),
+		'csrf' => FreshRSS_Auth::csrfToken(),
 	),
 	'shortcuts' => array(
 		'mark_read' => @$s['mark_read'],

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

@@ -1,7 +1,7 @@
 <?php
 	$c = Minz_Request::controllerName();
 	$a = Minz_Request::actionName();
-	$params = Minz_Request::params();
+	$params = Minz_Request::fetchGET();
 ?>
 
 <?php if ($this->nbPage > 1) { ?>

+ 1 - 0
app/views/helpers/pagination.phtml

@@ -15,6 +15,7 @@
 ?>
 
 <form id="mark-read-pagination" method="post">
+<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 <ul class="pagination">
 	<li class="item pager-next">
 	<?php if (FreshRSS_Context::$next_id) { ?>

+ 2 - 0
app/views/importExport/index.phtml

@@ -4,6 +4,7 @@
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a>
 
 	<form method="post" action="<?php echo _url('importExport', 'import'); ?>" enctype="multipart/form-data">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('sub.import_export.import'); ?></legend>
 		<div class="form-group">
 			<label class="group-name" for="file">
@@ -23,6 +24,7 @@
 
 	<?php if (count($this->feeds) > 0) { ?>
 	<form method="post" action="<?php echo _url('importExport', 'export'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('sub.import_export.export'); ?></legend>
 		<div class="form-group">
 			<div class="group-controls">

+ 1 - 1
app/views/index/global.phtml

@@ -14,7 +14,7 @@
 	$url_base = array(
 		'c' => 'index',
 		'a' => 'normal',
-		'params' => Minz_Request::params()
+		'params' => Minz_Request::fetchGET(),
 	);
 
 	foreach ($this->categories as $cat) {

+ 1 - 0
app/views/index/logs.phtml

@@ -3,6 +3,7 @@
 
 	<h1><?php echo _t('index.log'); ?></h1>
 	<form method="post" action="<?php echo _url('index', 'logs'); ?>"><p>
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<input type="hidden" name="clearLogs" />
 		<button type="submit" class="btn"><?php echo _t('index.log.clear'); ?></button>
 	</p></form>

+ 1 - 0
app/views/stats/idle.phtml

@@ -19,6 +19,7 @@
 			<h2><?php echo _t('gen.date.' . $period); ?></h2>
 
 			<form id="form-delete" method="post">
+			<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 			<?php foreach ($feeds as $feed) { ?>
 			<ul class="horizontal-list">
 				<li class="item">

+ 6 - 1
app/views/subscription/index.phtml

@@ -6,6 +6,7 @@
 	<h2><?php echo _t('sub.title'); ?></h2>
 
 	<form id="add_rss" method="post" action="<?php echo _url('feed', 'add'); ?>" autocomplete="off">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<div class="stick">
 			<input type="url" name="url_rss" class="long" placeholder="<?php echo _t('sub.feed.add'); ?>" />
 			<div class="dropdown">
@@ -56,13 +57,16 @@
 
 		<ul class="box-content box-content-centered">
 			<form action="<?php echo _url('category', 'create'); ?>" method="post">
+				<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 				<li class="item"><input type="text" id="new-category" name="new-category" placeholder="<?php echo _t('sub.category.new'); ?>" /></li>
 				<li class="item"><button class="btn btn-important" type="submit"><?php echo _t('gen.action.submit'); ?></button></li>
 			</form>
 		</ul>
 	</div>
 
-	<form id="controller-category" method="post" aria-hidden="true"></form>
+	<form id="controller-category" method="post" aria-hidden="true">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
+	</form>
 
 	<?php
 		foreach ($this->categories as $cat) {
@@ -71,6 +75,7 @@
 	<div class="box">
 		<div class="box-title">
 			<form action="<?php echo _url('category', 'update', 'id', $cat->id()); ?>" method="post">
+				<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 				<input type="text" name="name" value="<?php echo $cat->name(); ?>" />
 
 				<div class="dropdown">

+ 2 - 0
app/views/user/manage.phtml

@@ -4,6 +4,7 @@
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a>
 
 	<form method="post" action="<?php echo _url('user', 'create'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('admin.user.create'); ?></legend>
 
 		<div class="form-group">
@@ -46,6 +47,7 @@
 	</form>
 
 	<form method="post" action="<?php echo _url('user', 'delete'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('admin.user.users'); ?></legend>
 
 		<div class="form-group">

+ 2 - 0
app/views/user/profile.phtml

@@ -4,6 +4,7 @@
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a>
 
 	<form method="post" action="<?php echo _url('user', 'profile'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('conf.profile'); ?></legend>
 
 		<div class="form-group">
@@ -52,6 +53,7 @@
 
 	<?php if (!FreshRSS_Auth::hasAccess('admin')) { ?>
 	<form id="crypto-form" method="post" action="<?php echo _url('user', 'delete'); ?>">
+		<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
 		<legend><?php echo _t('conf.profile.delete'); ?></legend>
 
 		<p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.attention'); ?></span> <?php echo _t('conf.profile.delete.warn'); ?></p>

+ 3 - 0
data/config.default.php

@@ -66,6 +66,9 @@ return array(
 	# Allow or not Web robots (e.g. search engines) in HTML headers.
 	'allow_robots' => false,
 
+	# If true does nothing, if false restricts HTTP Referer via: meta referrer origin
+	'allow_referrer' => false,
+
 	'limits' => array(
 
 		# Duration in seconds of the SimplePie cache,

BIN
doc/FreshRSS-logo.png


BIN
doc/FreshRSS-screenshot.png


+ 5 - 3
lib/http-conditional.php

@@ -35,12 +35,12 @@
   ... //Rest of the script, just as you would do normally.
  ?>
 
- Version 1.7 beta, 2013-12-02, http://alexandre.alapetite.fr/doc-alex/php-http-304/
+ Version 1.8 beta, 2016-08-07, http://alexandre.alapetite.fr/doc-alex/php-http-304/
 
  ------------------------------------------------------------------
  Written by Alexandre Alapetite, http://alexandre.alapetite.fr/cv/
 
- Copyright 2004-2013, Licence: Creative Commons "Attribution-ShareAlike 2.0 France" BY-SA (FR),
+ Copyright 2004-2016, Licence: Creative Commons "Attribution-ShareAlike 2.0 France" BY-SA (FR),
  http://creativecommons.org/licenses/by-sa/2.0/fr/
  http://alexandre.alapetite.fr/divers/apropos/#by-sa
  - Attribution. You must give the original author credit
@@ -96,7 +96,8 @@ function httpConditional($UnixTimeStamp,$cacheSeconds=0,$cachePrivacy=0,$feedMod
 	if ((!$is412)&&isset($_SERVER['HTTP_IF_MATCH']))
 	{//rfc2616-sec14.html#sec14.24
 		$etagsClient=stripslashes($_SERVER['HTTP_IF_MATCH']);
-		$is412=(($etagClient!=='*')&&(strpos($etagsClient,$etagServer)===false));
+		$etagsClient=str_ireplace('-gzip','',$etagsClient);
+		$is412=(($etagsClient!=='*')&&(strpos($etagsClient,$etagServer)===false));
 	}
 	if ($is304&&isset($_SERVER['HTTP_IF_MODIFIED_SINCE']))
 	{//rfc2616-sec14.html#sec14.25 //rfc1945.txt
@@ -111,6 +112,7 @@ function httpConditional($UnixTimeStamp,$cacheSeconds=0,$cachePrivacy=0,$feedMod
 	{//rfc2616-sec14.html#sec14.26
 		$nbCond++;
 		$etagClient=stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
+		$etagClient=str_ireplace('-gzip','',$etagClient);
 		$is304=(($etagClient===$etagServer)||($etagClient==='*'));
 	}
 	if ((!$is412)&&isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE']))

+ 30 - 1
lib/lib_opml.php

@@ -12,7 +12,7 @@
  *
  * @author   Marien Fressinaud <dev@marienfressinaud.fr>
  * @link     https://github.com/marienfressinaud/lib_opml
- * @version  0.2
+ * @version  0.2-FreshRSS~1.5.1
  * @license  public domain
  *
  * Usages:
@@ -123,6 +123,32 @@ function libopml_parse_outline($outline_xml, $strict = true) {
 	return $outline;
 }
 
+/**
+ * Reformat the XML document as a hierarchy when
+ * the OPML 2.0 category attribute is used
+ */
+function preprocessing_categories($doc) {
+	$outline_categories = array();
+	$body = $doc->getElementsByTagName('body')->item(0);
+	$xpath = new DOMXpath($doc);
+	$outlines = $xpath->query('/opml/body/outline[@category]');
+	foreach ($outlines as $outline) {
+		$category = trim($outline->getAttribute('category'));
+		if ($category != '') {
+			$outline_categorie = null;
+			if (!isset($outline_categories[$category])) {
+				$outline_categorie = $doc->createElement('outline');
+				$outline_categorie->setAttribute('text', $category);
+				$body->insertBefore($outline_categorie, $body->firstChild);
+				$outline_categories[$category] = $outline_categorie;
+			} else {
+				$outline_categorie = $outline_categories[$category];
+			}
+			$outline->parentNode->removeChild($outline);
+			$outline_categorie->appendChild($outline);
+		}
+	}
+}
 
 /**
  * Parse a string as a XML one and returns the corresponding array
@@ -140,6 +166,9 @@ function libopml_parse_string($xml, $strict = true) {
 	$dom->loadXML($xml);
 	$dom->encoding = 'UTF-8';
 
+	//Partial compatibility with the category attribute of OPML 2.0
+	preprocessing_categories($dom);
+
 	$opml = simplexml_import_dom($dom);
 
 	if (!$opml) {

+ 1 - 1
lib/lib_rss.php

@@ -391,7 +391,7 @@ function cryptAvailable() {
 
 function is_referer_from_same_domain() {
 	if (empty($_SERVER['HTTP_REFERER'])) {
-		return false;
+		return true;	//Accept empty referer while waiting for good support of meta referrer same-origin policy in browsers
 	}
 	$host = parse_url(((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https://' : 'http://') .
 		(empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST']));

+ 25 - 19
p/scripts/category.js

@@ -1,30 +1,37 @@
 "use strict";
+/* globals i18n */
+/* jshint globalstrict: true */
 
 var loading = false,
 	dnd_successful = false;
 
 function dragend_process(t) {
-	t.style.display = 'none';
+	t.setAttribute('draggable', 'false');
 
 	if (loading) {
 		window.setTimeout(function() {
 			dragend_process(t);
 		}, 50);
+		return;
 	}
 
 	if (!dnd_successful) {
-		t.style.display = 'block';
-		t.style.opacity = 1.0;
+		t.style.display = '';
+		t.style.opacity = '';
+		t.setAttribute('draggable', 'true');
 	} else {
 		var parent = $(t.parentNode);
 		$(t).remove();
 
 		if (parent.children().length <= 0) {
-			parent.append('<li class="item disabled" dropzone="move">' + i18n['category_empty'] + '</li>');
+			parent.append('<li class="item disabled" dropzone="move">' + i18n.category_empty + '</li>');
 		}
 	}
 }
 
+var dragFeedId = '',
+	dragHtml = '';
+
 function init_draggable() {
 	if (!(window.$ && window.i18n)) {
 		if (window.console) {
@@ -34,16 +41,16 @@ function init_draggable() {
 		return;
 	}
 
-	$.event.props.push('dataTransfer');
-
 	var draggable = '[draggable="true"]',
 	    dropzone = '[dropzone="move"]';
 
 	$('.drop-section').on('dragstart', draggable, function(e) {
-		e.dataTransfer.effectAllowed = 'move';
-		e.dataTransfer.setData('text/html', e.target.outerHTML);
-		e.dataTransfer.setData('text', e.target.getAttribute('data-feed-id'));
-		e.target.style.opacity = 0.3;
+		var drag = $(e.target).closest('[draggable]')[0];
+		e.originalEvent.dataTransfer.effectAllowed = 'move';
+		dragHtml = drag.outerHTML;
+		dragFeedId = drag.getAttribute('data-feed-id');
+		e.originalEvent.dataTransfer.setData('text', dragFeedId);
+		drag.style.opacity = 0.3;
 
 		dnd_successful = false;
 	});
@@ -74,32 +81,31 @@ function init_draggable() {
 		$(this).removeClass('drag-hover');
 	});
 	$('.drop-section').on('dragover', dropzone, function(e) {
-		e.dataTransfer.dropEffect = "move";
+		e.originalEvent.dataTransfer.dropEffect = "move";
 
 		e.preventDefault();
 		return false;
 	});
 	$('.drop-section').on('drop', dropzone, function(e) {
-		var feed_id = e.dataTransfer.getData('text'),
-		    cat_id = e.target.parentNode.getAttribute('data-cat-id');
-
 		loading = true;
 
 		$.ajax({
 			type: 'POST',
 			url: './?c=feed&a=move',
 			data : {
-				f_id: feed_id,
-				c_id: cat_id
+				f_id: dragFeedId,
+				c_id: e.target.parentNode.getAttribute('data-cat-id'),
 			}
-		}).success(function() {
-			$(e.target).after(e.dataTransfer.getData('text/html'));
+		}).done(function() {
+			$(e.target).after(dragHtml);
 			if ($(e.target).hasClass('disabled')) {
 				$(e.target).remove();
 			}
 			dnd_successful = true;
-		}).complete(function() {
+		}).always(function() {
 			loading = false;
+			dragFeedId = '';
+			dragHtml = '';
 		});
 
 		$(this).removeClass('drag-hover');

+ 3 - 0
p/scripts/global_view.js

@@ -1,4 +1,7 @@
 "use strict";
+/* globals init_load_more, init_posts, init_stream */
+/* jshint globalstrict: true */
+
 var panel_loading = false;
 
 function load_panel(link) {

+ 1 - 0
p/scripts/install.js

@@ -1,4 +1,5 @@
 "use strict";
+/* jshint globalstrict: true */
 
 function show_password() {
 	var button = this;

File diff suppressed because it is too large
+ 0 - 1
p/scripts/jquery.min.js


+ 86 - 69
p/scripts/main.js

@@ -1,4 +1,7 @@
 "use strict";
+/* globals context, i18n, shortcut, shortcuts, url */
+/* jshint globalstrict: true */
+
 var $stream = null,
 	isCollapsed = true,
 	shares = 0,
@@ -49,9 +52,7 @@ function numberFormat(nStr) {
 
 function incLabel(p, inc, spaceAfter) {
 	var i = str2int(p) + inc;
-	return i > 0
-		? ((spaceAfter ? '' : ' ') + '(' + numberFormat(i) + ')' + (spaceAfter ? ' ' : ''))
-		: '';
+	return i > 0 ? ((spaceAfter ? '' : ' ') + '(' + numberFormat(i) + ')' + (spaceAfter ? ' ' : '')) : '';
 }
 
 function incUnreadsFeed(article, feed_id, nb) {
@@ -133,7 +134,10 @@ function mark_read(active, only_not_read) {
 	$.ajax({
 		type: 'POST',
 		url: url,
-		data : { ajax: true }
+		data : {
+			ajax: true,
+			_csrf: context.csrf,
+		},
 	}).done(function (data) {
 		var $r = active.find("a.read").attr("href", data.url),
 			inc = 0;
@@ -177,7 +181,10 @@ function mark_favorite(active) {
 	$.ajax({
 		type: 'POST',
 		url: url,
-		data : { ajax: true }
+		data : {
+			ajax: true,
+			_csrf: context.csrf,
+		},
 	}).done(function (data) {
 		var $b = active.find("a.bookmark").attr("href", data.url),
 			inc = 0;
@@ -217,7 +224,7 @@ function toggleContent(new_active, old_active) {
 		return;
 	}
 
-	if (context['does_lazyload']) {
+	if (context.does_lazyload) {
 		new_active.find('img[data-original], iframe[data-original]').each(function () {
 			this.setAttribute('src', this.getAttribute('data-original'));
 			this.removeAttribute('data-original');
@@ -230,17 +237,17 @@ function toggleContent(new_active, old_active) {
 		}
 		old_active.removeClass("active current");
 		new_active.addClass("current");
-		if (context['auto_remove_article'] && !old_active.hasClass('not_read')) {
+		if (context.auto_remove_article && !old_active.hasClass('not_read')) {
 			auto_remove(old_active);
 		}
 	} else {
 		new_active.toggleClass('active');
 	}
 
-	var relative_move = context['current_view'] === 'global',
+	var relative_move = context.current_view === 'global',
 		box_to_move = $(relative_move ? "#panel" : "html,body");
 
-	if (context['sticky_post']) {
+	if (context.sticky_post) {
 		var prev_article = new_active.prevAll('.flux'),
 			new_pos = new_active.offset().top,
 			old_scroll = box_to_move.scrollTop();
@@ -252,7 +259,7 @@ function toggleContent(new_active, old_active) {
 			}
 		}
 
-		if (context['hide_posts']) {
+		if (context.hide_posts) {
 			if (relative_move) {
 				new_pos += old_scroll;
 			}
@@ -271,7 +278,7 @@ function toggleContent(new_active, old_active) {
 		}
 	}
 
-	if (context['auto_mark_article'] && new_active.hasClass('active')) {
+	if (context.auto_mark_article && new_active.hasClass('active')) {
 		mark_read(new_active, true);
 	}
 }
@@ -381,7 +388,7 @@ function collapse_entry() {
 
 	var flux_current = $(".flux.current");
 	flux_current.toggleClass("active");
-	if (isCollapsed && context['auto_mark_article']) {
+	if (isCollapsed && context.auto_mark_article) {
 		mark_read(flux_current, true);
 	}
 }
@@ -456,11 +463,11 @@ function inMarkViewport(flux, box_to_follow) {
 
 function init_posts() {
 	var box_to_follow = $(window);
-	if (context['current_view'] === 'global') {
+	if (context.current_view === 'global') {
 		box_to_follow = $("#panel");
 	}
 
-	if (context['auto_mark_scroll']) {
+	if (context.auto_mark_scroll) {
 		box_to_follow.scroll(function () {
 			$('.not_read:visible').each(function () {
 				if ($(this).children(".flux_content").is(':visible') && inMarkViewport($(this), box_to_follow)) {
@@ -470,7 +477,7 @@ function init_posts() {
 		});
 	}
 
-	if (context['auto_load_more']) {
+	if (context.auto_load_more) {
 		box_to_follow.scroll(function () {
 			var load_more = $("#load_more");
 			if (!load_more.is(':visible')) {
@@ -487,7 +494,7 @@ function init_posts() {
 }
 
 function init_column_categories() {
-	if (context['current_view'] !== 'normal') {
+	if (context.current_view !== 'normal') {
 		return;
 	}
 
@@ -559,14 +566,16 @@ function init_shortcuts() {
 	}, {
 		'disable_in_input': true
 	});
-	for(var i = 1; i < 10; i++){
-		shortcut.add(i.toString(), function (e) {
-			if ($('#dropdown-query').siblings('.dropdown-menu').is(':visible')) {
-				user_filter(String.fromCharCode(e.keyCode));
-			} else {
-				auto_share(String.fromCharCode(e.keyCode));
-			}
-		}, {
+
+	function addShortcut(evt) {
+		if ($('#dropdown-query').siblings('.dropdown-menu').is(':visible')) {
+			user_filter(String.fromCharCode(evt.keyCode));
+		} else {
+			auto_share(String.fromCharCode(evt.keyCode));
+		}
+	}
+	for(var i = 1; i < 10; i++) {
+		shortcut.add(i.toString(), addShortcut, {
 			'disable_in_input': true
 		});
 	}
@@ -628,7 +637,7 @@ function init_shortcuts() {
 	shortcut.add(shortcuts.go_website, function () {
 		var url_website = $('.flux.current > .flux_header > .title > a').attr("href");
 
-		if (context['auto_mark_site']) {
+		if (context.auto_mark_site) {
 			$(".flux.current").each(function () {
 				mark_read($(this), true);
 			});
@@ -652,7 +661,7 @@ function init_shortcuts() {
 	});
 
 	shortcut.add(shortcuts.help, function () {
-		redirect(url['help'], true);
+		redirect(url.help, true);
 	}, {
 		'disable_in_input': true
 	});
@@ -674,7 +683,7 @@ function init_stream(divStream) {
 			new_active = $(this).parent();
 		isCollapsed = true;
 		if (e.target.tagName.toUpperCase() === 'A') {	//Leave real links alone
-			if (context['auto_mark_article']) {
+			if (context.auto_mark_article) {
 				mark_read(new_active, true);
 			}
 			return true;
@@ -684,7 +693,7 @@ function init_stream(divStream) {
 
 	divStream.on('click', '.flux a.read', function () {
 		var active = $(this).parents(".flux");
-		if (context['auto_remove_article'] && active.hasClass('not_read')) {
+		if (context.auto_remove_article && active.hasClass('not_read')) {
 			auto_remove(active);
 		}
 		mark_read(active, false);
@@ -710,9 +719,9 @@ function init_stream(divStream) {
 
 		if (e.which == 2) {
 			// If middle click, we want same behaviour as CTRL+click.
-			var e = jQuery.Event("click");
-			e.ctrlKey = true;
-			$(this).trigger(e);
+			var ev = jQuery.Event("click");
+			ev.ctrlKey = true;
+			$(this).trigger(ev);
 		} else if(e.which == 1) {
 			// Normal click, just toggle article.
 			$(this).parent().click();
@@ -723,7 +732,7 @@ function init_stream(divStream) {
 		$(this).attr('target', '_blank');
 	});
 
-	if (context['auto_mark_site']) {
+	if (context.auto_mark_site) {
 		// catch mouseup instead of click so we can have the correct behaviour
 		// with middle button click (scroll button).
 		divStream.on('mouseup', '.flux .link > a', function (e) {
@@ -765,17 +774,20 @@ var feed_processed = 0;
 
 function updateFeed(feeds, feeds_count) {
 	var feed = feeds.pop();
-	if (feed == undefined) {
+	if (!feed) {
 		return;
 	}
 
 	$.ajax({
 		type: 'POST',
-		url: feed['url'],
-	}).complete(function (data) {
+		url: feed.url,
+		data : {
+			_csrf: context.csrf,
+		},
+	}).always(function (data) {
 		feed_processed++;
 		$("#actualizeProgress .progress").html(feed_processed + " / " + feeds_count);
-		$("#actualizeProgress .title").html(feed['title']);
+		$("#actualizeProgress .title").html(feed.title);
 
 		if (feed_processed === feeds_count) {
 			window.location.reload();
@@ -819,7 +831,7 @@ function init_actualize() {
 		return false;
 	});
 
-	if (context['auto_actualize_feeds']) {
+	if (context.auto_actualize_feeds) {
 		auto = true;
 		$("#actualize").click();
 	}
@@ -887,12 +899,12 @@ function notifs_html5_ask_permission() {
 
 function notifs_html5_show(nb) {
 	if (notifs_html5_permission !== "granted") {
-		return
+		return;
 	}
 
-	var notification = new window.Notification(i18n['notif_title_articles'], {
+	var notification = new window.Notification(i18n.notif_title_articles, {
 		icon: "../themes/icons/favicon-256.png",
-		body: i18n['notif_body_articles'].replace('%d', nb),
+		body: i18n.notif_body_articles.replace('%d', nb),
 		tag: "freshRssNewArticles"
 	});
 
@@ -900,10 +912,10 @@ function notifs_html5_show(nb) {
 		window.location.reload();
 	};
 
-	if (context['html5_notif_timeout'] !== 0) {
+	if (context.html5_notif_timeout !== 0) {
 		setTimeout(function() {
 			notification.close();
-		}, context['html5_notif_timeout'] * 1000);
+		}, context.html5_notif_timeout * 1000);
 	}
 }
 
@@ -930,7 +942,7 @@ function refreshUnreads() {
 				(nbUnreads - feed_unreads > 0)) {
 				$('#new-article').attr('aria-hidden', 'false').show();
 				new_articles = true;
-			};
+			}
 		});
 
 		var nb_unreads = str2int($('.category.all .title').attr('data-unread'));
@@ -957,7 +969,7 @@ function load_more_posts() {
 	$.get(url_load_more, function (data) {
 		box_load_more.children('.flux:last').after($('#stream', data).children('.flux, .day'));
 		$('.pagination').replaceWith($('.pagination', data));
-		if (context['display_order'] === 'ASC') {
+		if (context.display_order === 'ASC') {
 			$('#nav_menu_read_all > .read_all').attr(
 				'formaction', $('#bigMarkAsRead').attr('formaction')
 			);
@@ -988,7 +1000,7 @@ function focus_search() {
 function init_load_more(box) {
 	box_load_more = box;
 
-	if (!context['does_lazyload']) {
+	if (!context.does_lazyload) {
 		$('img[postpone], audio[postpone], iframe[postpone], video[postpone]').each(function () {
 			this.removeAttribute('postpone');
 		});
@@ -1027,6 +1039,7 @@ function poormanSalt() {	//If crypto.getRandomValues is not available
 }
 
 function init_crypto_form() {
+	/* globals dcodeIO */
 	var $crypto_form = $('#crypto-form');
 	if ($crypto_form.length === 0) {
 		return;
@@ -1050,7 +1063,7 @@ function init_crypto_form() {
 			dataType: 'json',
 			async: false
 		}).done(function (data) {
-			if (data.salt1 == '' || data.nonce == '') {
+			if (!data.salt1 || !data.nonce) {
 				openNotification('Invalid user!', 'bad');
 			} else {
 				try {
@@ -1058,7 +1071,7 @@ function init_crypto_form() {
 						s = dcodeIO.bcrypt.hashSync($('#passwordPlain').val(), data.salt1),
 						c = dcodeIO.bcrypt.hashSync(data.nonce + s, strong ? dcodeIO.bcrypt.genSaltSync(4) : poormanSalt());
 					$('#challenge').val(c);
-					if (s == '' || c == '') {
+					if (!s || !c) {
 						openNotification('Crypto error!', 'bad');
 					} else {
 						success = true;
@@ -1083,7 +1096,7 @@ function init_confirm_action() {
 	$('body').on('click', '.confirm', function () {
 		var str_confirmation = $(this).attr('data-str-confirm');
 		if (!str_confirmation) {
-			str_confirmation = i18n['confirmation_default'];
+			str_confirmation = i18n.confirmation_default;
 		}
 
 		return confirm(str_confirmation);
@@ -1092,13 +1105,13 @@ function init_confirm_action() {
 
 function init_print_action() {
 	$('.item.share > a[href="#"]').click(function () {
-		var content = "<html><head><style>"
-			+ "body { font-family: Serif; text-align: justify; }"
-			+ "a { color: #000; text-decoration: none; }"
-			+ "a:after { content: ' [' attr(href) ']'}"
-			+ "</style></head><body>"
-			+ $(".flux.current .content").html()
-			+ "</body></html>";
+		var content = "<html><head><style>" +
+			"body { font-family: Serif; text-align: justify; }" +
+			"a { color: #000; text-decoration: none; }" +
+			"a:after { content: ' [' attr(href) ']'}" +
+			"</style></head><body>" +
+			$(".flux.current .content").html() +
+			"</body></html>";
 
 		var tmp_window = window.open();
 		tmp_window.document.writeln(content);
@@ -1291,6 +1304,22 @@ function parseJsonVars() {
 	window.icons = json.icons;
 }
 
+function init_normal() {
+	$stream = $('#stream');
+	if ($stream.length < 1) {
+		if (window.console) {
+			console.log('FreshRSS waiting for content…');
+		}
+		window.setTimeout(init_normal, 50);
+		return;
+	}
+	init_column_categories();
+	init_stream($stream);
+	init_shortcuts();
+	init_actualize();
+	faviconNbUnread();
+}
+
 function init_beforeDOM() {
 	if (!window.$) {
 		if (window.console) {
@@ -1300,20 +1329,8 @@ function init_beforeDOM() {
 		return;
 	}
 	init_confirm_action();
-	if (['normal', 'reader', 'global'].indexOf(context['current_view']) >= 0) {
-		$stream = $('#stream');
-		if ($stream.length < 1) {
-			if (window.console) {
-				console.log('FreshRSS waiting for content…');
-			}
-			window.setTimeout(init_beforeDOM, 50);
-			return;
-		}
-		init_column_categories();
-		init_stream($stream);
-		init_shortcuts();
-		init_actualize();
-		faviconNbUnread();
+	if (['normal', 'reader', 'global'].indexOf(context.current_view) >= 0) {
+		init_normal();
 	}
 }
 

+ 9 - 9
p/scripts/repartition.js

@@ -1,4 +1,7 @@
 "use strict";
+/* globals Flotr, numberFormat */
+/* jshint globalstrict: true */
+
 function initStats() {
 	if (!window.Flotr) {
 		if (window.console) {
@@ -19,9 +22,8 @@ function initStats() {
 		{
 			grid: {verticalLines: false},
 			xaxis: {noTicks: 23,
-				tickFormatter: function(x) {
-					var x = parseInt(x);
-					return x + 1;
+				tickFormatter: function(x1) {
+					return 1 + parseInt(x1);
 				},
 				min: -0.9,
 				max: 23.9,
@@ -38,9 +40,8 @@ function initStats() {
 		{
 			grid: {verticalLines: false},
 			xaxis: {noTicks: 6,
-				tickFormatter: function(x) {
-					var x = parseInt(x);
-					return stats.days[x];
+				tickFormatter: function(x2) {
+					return stats.days[parseInt(x2)];
 				},
 				min: -0.9,
 				max: 6.9,
@@ -57,9 +58,8 @@ function initStats() {
 		{
 			grid: {verticalLines: false},
 			xaxis: {noTicks: 12,
-				tickFormatter: function(x) {
-					var x = parseInt(x);
-					return stats.months[(x - 1)];
+				tickFormatter: function(x3) {
+					return stats.months[parseInt(x3) - 1];
 				},
 				min: 0.1,
 				max: 12.9,

+ 3 - 0
p/scripts/stats.js

@@ -1,4 +1,7 @@
 "use strict";
+/* globals Flotr, numberFormat */
+/* jshint globalstrict: true */
+
 function initStats() {
 	if (!window.Flotr) {
 		if (window.console) {

Some files were not shown because too many files changed in this diff