Browse Source

Merge branch 'dev' into beta

Marien Fressinaud 11 năm trước cách đây
mục cha
commit
f0fb1fbb07
100 tập tin đã thay đổi với 2896 bổ sung3229 xóa
  1. 23 1
      CHANGELOG
  2. 229 91
      app/Controllers/configureController.php
  3. 4 0
      app/Controllers/entryController.php
  4. 19 3
      app/Controllers/feedController.php
  5. 20 8
      app/Controllers/importExportController.php
  6. 80 0
      app/Controllers/indexController.php
  7. 45 0
      app/Controllers/statsController.php
  8. 129 0
      app/Controllers/updateController.php
  9. 13 4
      app/FreshRSS.php
  10. 37 2
      app/Models/Configuration.php
  11. 1 0
      app/Models/Entry.php
  12. 2 0
      app/Models/EntryDAO.php
  13. 1 1
      app/Models/FeedDAO.php
  14. 69 0
      app/Models/StatsDAO.php
  15. 1 0
      app/Models/StatsDAOSQLite.php
  16. 1 0
      app/Models/Themes.php
  17. 45 8
      app/i18n/en.php
  18. 46 9
      app/i18n/fr.php
  19. 2 0
      app/i18n/install.en.php
  20. 2 0
      app/i18n/install.fr.php
  21. 246 509
      app/install.php
  22. 23 15
      app/layout/aside_configure.phtml
  23. 41 33
      app/layout/aside_flux.phtml
  24. 6 0
      app/layout/header.phtml
  25. 1 0
      app/layout/layout.phtml
  26. 101 74
      app/layout/nav_menu.phtml
  27. 7 1
      app/views/configure/categorize.phtml
  28. 7 0
      app/views/configure/display.phtml
  29. 18 12
      app/views/configure/feed.phtml
  30. 11 4
      app/views/configure/queries.phtml
  31. 12 8
      app/views/configure/reading.phtml
  32. 15 0
      app/views/configure/shortcut.phtml
  33. 8 3
      app/views/helpers/javascript_vars.phtml
  34. 26 15
      app/views/helpers/pagination.phtml
  35. 8 0
      app/views/helpers/view/global_view.phtml
  36. 3 3
      app/views/helpers/view/normal_view.phtml
  37. 3 3
      app/views/helpers/view/reader_view.phtml
  38. 10 3
      app/views/index/formLogin.phtml
  39. 33 0
      app/views/index/resetAuth.phtml
  40. 28 5
      app/views/stats/idle.phtml
  41. 24 24
      app/views/stats/index.phtml
  42. 51 15
      app/views/stats/repartition.phtml
  43. 9 0
      app/views/update/apply.phtml
  44. 36 0
      app/views/update/index.phtml
  45. 3 0
      constants.php
  46. 2 0
      data/.gitignore
  47. 3 0
      lib/Minz/ModelPdo.php
  48. 5 0
      lib/Minz/Request.php
  49. 11 4
      lib/Minz/View.php
  50. 1 1
      lib/SimplePie/SimplePie/Parser.php
  51. 14 0
      lib/lib_rss.php
  52. 131 40
      p/scripts/main.js
  53. 17 5
      p/themes/Dark/dark.css
  54. 1 1
      p/themes/Dark/metadata.json
  55. 0 698
      p/themes/Dark/template.css
  56. 33 7
      p/themes/Flat/flat.css
  57. 1 1
      p/themes/Flat/metadata.json
  58. 0 698
      p/themes/Flat/template.css
  59. 1 1
      p/themes/Origine/metadata.json
  60. 20 12
      p/themes/Origine/origine.css
  61. 0 698
      p/themes/Origine/template.css
  62. 4 0
      p/themes/Pafat/README.md
  63. 7 0
      p/themes/Pafat/icons/all.svg
  64. 5 0
      p/themes/Pafat/icons/bookmark.svg
  65. 1 1
      p/themes/Pafat/icons/down.svg
  66. 2 2
      p/themes/Pafat/icons/icon.svg
  67. 4 0
      p/themes/Pafat/icons/link.svg
  68. 2 2
      p/themes/Pafat/icons/login.svg
  69. 2 2
      p/themes/Pafat/icons/logout.svg
  70. 1 1
      p/themes/Pafat/icons/next.svg
  71. 2 0
      p/themes/Pafat/icons/non-starred.svg
  72. 5 0
      p/themes/Pafat/icons/prev.svg
  73. 5 0
      p/themes/Pafat/icons/read.svg
  74. 1 1
      p/themes/Pafat/icons/share.svg
  75. 5 0
      p/themes/Pafat/icons/starred.svg
  76. 1 1
      p/themes/Pafat/icons/tag.svg
  77. 6 0
      p/themes/Pafat/icons/unread.svg
  78. 5 0
      p/themes/Pafat/icons/up.svg
  79. BIN
      p/themes/Pafat/loader.gif
  80. 7 0
      p/themes/Pafat/metadata.json
  81. 1084 0
      p/themes/Pafat/pafat.css
  82. 0 5
      p/themes/Screwdriver/icons/add.svg
  83. 0 7
      p/themes/Screwdriver/icons/all.svg
  84. BIN
      p/themes/Screwdriver/icons/apple-touch-icon.png
  85. 0 6
      p/themes/Screwdriver/icons/bookmark-add.svg
  86. 5 60
      p/themes/Screwdriver/icons/bookmark.svg
  87. 0 7
      p/themes/Screwdriver/icons/category-white.svg
  88. 0 7
      p/themes/Screwdriver/icons/category.svg
  89. 0 7
      p/themes/Screwdriver/icons/close.svg
  90. 0 5
      p/themes/Screwdriver/icons/configure.svg
  91. BIN
      p/themes/Screwdriver/icons/favicon-16-32-48-64.ico
  92. BIN
      p/themes/Screwdriver/icons/favicon-256.png
  93. 0 13
      p/themes/Screwdriver/icons/favicon.svg
  94. BIN
      p/themes/Screwdriver/icons/grey.gif
  95. 0 7
      p/themes/Screwdriver/icons/help.svg
  96. 0 7
      p/themes/Screwdriver/icons/key.svg
  97. 0 4
      p/themes/Screwdriver/icons/link.svg
  98. 0 2
      p/themes/Screwdriver/icons/non-starred.svg
  99. 0 5
      p/themes/Screwdriver/icons/prev.svg
  100. 3 57
      p/themes/Screwdriver/icons/read.svg

+ 23 - 1
CHANGELOG

@@ -1,6 +1,28 @@
 # Journal des modifications
 
-## 2014-08-xx FreshRSS 0.7.4
+## 2014-09-xx FreshRSS 0.8.0
+
+* UI
+	* New interface for statistics
+	* Fix filter buttons
+	* Number of articles divided by 2 in reading view
+	* Redesign of bigMarkAsRead
+* Features
+	* New automatic update system
+	* New reset auth system
+* Security
+	* "Mark as read" requires POST requests for several articles
+	* Test HTTP REFERER in install.php
+* Configuration
+	* New "Show all articles" / "Show only unread" / "Adjust viewing" option
+	* New notification timeout option
+* Misc.
+	* Improve coding style + comments
+	* Fix SQLite bug "ON DELETE CASCADE"
+	* Improve performance when importing articles
+
+
+## 2014-08-24 FreshRSS 0.7.4
 
 * UI
 	* Hide categories/feeds with unread articles when showing only unread articles

+ 229 - 91
app/Controllers/configureController.php

@@ -1,11 +1,22 @@
 <?php
 
+/**
+ * Controller to handle every configuration options.
+ */
 class FreshRSS_configure_Controller extends Minz_ActionController {
+	/**
+	 * This action is called before every other action in that class. It is
+	 * the common boiler plate for every action. It is triggered by the
+	 * underlying framework.
+	 *
+	 * @todo see if the category default configuration is needed here or if
+	 *       we can move it to the categorize action
+	 */
 	public function firstAction() {
 		if (!$this->view->loginOk) {
 			Minz_Error::error(
 				403,
-				array('error' => array(Minz_Translate::t('access_denied')))
+				array('error' => array(_t('access_denied')))
 			);
 		}
 
@@ -13,6 +24,18 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 		$catDAO->checkDefault();
 	}
 
+	/**
+	 * This action handles the category configuration page
+	 *
+	 * It displays the category configuration page.
+	 * If this action is reached through a POST request, it loops through
+	 * every category to check for modification then add a new category if
+	 * needed then sends a notification to the user.
+	 * If a category name is emptied, the category is deleted and all
+	 * related feeds are moved to the default category. Related user queries
+	 * are deleted too.
+	 * If a category name is changed, it is updated.
+	 */
 	public function categorizeAction() {
 		$feedDAO = FreshRSS_Factory::createFeedDao();
 		$catDAO = new FreshRSS_CategoryDAO();
@@ -34,6 +57,10 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 				} elseif ($ids[$key] != $defaultId) {
 					$feedDAO->changeCategory($ids[$key], $defaultId);
 					$catDAO->deleteCategory($ids[$key]);
+
+					// Remove related queries.
+					$this->view->conf->remove_query_by_get('c_' . $ids[$key]);
+					$this->view->conf->save();
 				}
 			}
 
@@ -50,22 +77,37 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			}
 			invalidateHttpCache();
 
-			$notif = array(
-				'type' => 'good',
-				'content' => Minz_Translate::t('categories_updated')
-			);
-			Minz_Session::_param('notification', $notif);
-
-			Minz_Request::forward(array('c' => 'configure', 'a' => 'categorize'), true);
+			Minz_Request::good(_t('categories_updated'),
+			                   array('c' => 'configure', 'a' => 'categorize'));
 		}
 
 		$this->view->categories = $catDAO->listCategories(false);
 		$this->view->defaultCategory = $catDAO->getDefault();
 		$this->view->feeds = $feedDAO->listFeeds();
 
-		Minz_View::prependTitle(Minz_Translate::t('categories_management') . ' · ');
+		Minz_View::prependTitle(_t('categories_management') . ' · ');
 	}
 
+	/**
+	 * This action handles the feed configuration page.
+	 *
+	 * It displays the feed configuration page.
+	 * If this action is reached through a POST request, it stores all new
+	 * configuraiton values then sends a notification to the user.
+	 *
+	 * The options available on the page are:
+	 *   - name
+	 *   - description
+	 *   - website URL
+	 *   - feed URL
+	 *   - category id (default: default category id)
+	 *   - CSS path to article on website
+	 *   - display in main stream (default: 0)
+	 *   - HTTP authentication
+	 *   - number of article to retain (default: -2)
+	 *   - refresh frequency (default: -2)
+	 * Default values are empty strings unless specified.
+	 */
 	public function feedAction() {
 		$catDAO = new FreshRSS_CategoryDAO();
 		$this->view->categories = $catDAO->listCategories(false);
@@ -85,7 +127,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			if (!$this->view->flux) {
 				Minz_Error::error(
 					404,
-					array('error' => array(Minz_Translate::t('page_not_found')))
+					array('error' => array(_t('page_not_found')))
 				);
 			} else {
 				if (Minz_Request::isPost() && $this->view->flux) {
@@ -117,12 +159,12 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 						$this->view->flux->faviconPrepare();
 						$notif = array(
 							'type' => 'good',
-							'content' => Minz_Translate::t('feed_updated')
+							'content' => _t('feed_updated')
 						);
 					} else {
 						$notif = array(
 							'type' => 'bad',
-							'content' => Minz_Translate::t('error_occurred_update')
+							'content' => _t('error_occurred_update')
 						);
 					}
 					invalidateHttpCache();
@@ -131,21 +173,41 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 					Minz_Request::forward(array('c' => 'configure', 'a' => 'feed', 'params' => array('id' => $id)), true);
 				}
 
-				Minz_View::prependTitle(Minz_Translate::t('rss_feed_management') . ' — ' . $this->view->flux->name() . ' · ');
+				Minz_View::prependTitle(_t('rss_feed_management') . ' — ' . $this->view->flux->name() . ' · ');
 			}
 		} else {
-			Minz_View::prependTitle(Minz_Translate::t('rss_feed_management') . ' · ');
+			Minz_View::prependTitle(_t('rss_feed_management') . ' · ');
 		}
 	}
 
+	/**
+	 * This action handles the display configuration page.
+	 *
+	 * It displays the display configuration page.
+	 * If this action is reached through a POST request, it stores all new
+	 * configuration values then sends a notification to the user.
+	 *
+	 * The options available on the page are:
+	 *   - language (default: en)
+	 *   - theme (default: Origin)
+	 *   - content width (default: thin)
+	 *   - display of read action in header
+	 *   - display of favorite action in header
+	 *   - display of date in header
+	 *   - display of open action in header
+	 *   - display of read action in footer
+	 *   - display of favorite action in footer
+	 *   - display of sharing action in footer
+	 *   - display of tags in footer
+	 *   - display of date in footer
+	 *   - display of open action in footer
+	 *   - html5 notification timeout (default: 0)
+	 * Default values are false unless specified.
+	 */
 	public function displayAction() {
 		if (Minz_Request::isPost()) {
 			$this->view->conf->_language(Minz_Request::param('language', 'en'));
-			$themeId = Minz_Request::param('theme', '');
-			if ($themeId == '') {
-				$themeId = FreshRSS_Themes::defaultTheme;
-			}
-			$this->view->conf->_theme($themeId);
+			$this->view->conf->_theme(Minz_Request::param('theme', FreshRSS_Themes::$defaultTheme));
 			$this->view->conf->_content_width(Minz_Request::param('content_width', 'thin'));
 			$this->view->conf->_topline_read(Minz_Request::param('topline_read', false));
 			$this->view->conf->_topline_favorite(Minz_Request::param('topline_favorite', false));
@@ -157,26 +219,49 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			$this->view->conf->_bottomline_tags(Minz_Request::param('bottomline_tags', false));
 			$this->view->conf->_bottomline_date(Minz_Request::param('bottomline_date', false));
 			$this->view->conf->_bottomline_link(Minz_Request::param('bottomline_link', false));
+			$this->view->conf->_html5_notif_timeout(Minz_Request::param('html5_notif_timeout', 0));
 			$this->view->conf->save();
 
 			Minz_Session::_param('language', $this->view->conf->language);
 			Minz_Translate::reset();
 			invalidateHttpCache();
 
-			$notif = array(
-				'type' => 'good',
-				'content' => Minz_Translate::t('configuration_updated')
-			);
-			Minz_Session::_param('notification', $notif);
-
-			Minz_Request::forward(array('c' => 'configure', 'a' => 'display'), true);
+			Minz_Request::good(_t('configuration_updated'),
+			                   array('c' => 'configure', 'a' => 'display'));
 		}
 
 		$this->view->themes = FreshRSS_Themes::get();
 
-		Minz_View::prependTitle(Minz_Translate::t('display_configuration') . ' · ');
+		Minz_View::prependTitle(_t('display_configuration') . ' · ');
 	}
 
+	/**
+	 * This action handles the reading configuration page.
+	 *
+	 * It displays the reading configuration page.
+	 * If this action is reached through a POST request, it stores all new
+	 * configuration values then sends a notification to the user.
+	 *
+	 * The options available on the page are:
+	 *   - number of posts per page (default: 10)
+	 *   - view mode (default: normal)
+	 *   - default article view (default: all)
+	 *   - load automatically articles
+	 *   - display expanded articles
+	 *   - display expanded categories
+	 *   - hide categories and feeds without unread articles
+	 *   - jump on next category or feed when marked as read
+	 *   - image lazy loading
+	 *   - stick open articles to the top
+	 *   - display a confirmation when reading all articles
+	 *   - article order (default: DESC)
+	 *   - mark articles as read when:
+	 *       - displayed
+	 *       - opened on site
+	 *       - scrolled
+	 *       - received
+	 * Default values are false unless specified.
+	 */
 	public function readingAction() {
 		if (Minz_Request::isPost()) {
 			$this->view->conf->_posts_per_page(Minz_Request::param('posts_per_page', 10));
@@ -203,18 +288,20 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			Minz_Translate::reset();
 			invalidateHttpCache();
 
-			$notif = array(
-				'type' => 'good',
-				'content' => Minz_Translate::t('configuration_updated')
-			);
-			Minz_Session::_param('notification', $notif);
-
-			Minz_Request::forward(array('c' => 'configure', 'a' => 'reading'), true);
+			Minz_Request::good(_t('configuration_updated'),
+			                   array('c' => 'configure', 'a' => 'reading'));
 		}
 
-		Minz_View::prependTitle(Minz_Translate::t('reading_configuration') . ' · ');
+		Minz_View::prependTitle(_t('reading_configuration') . ' · ');
 	}
 
+	/**
+	 * This action handles the sharing configuration page.
+	 *
+	 * It displays the sharing configuration page.
+	 * If this action is reached through a POST request, it stores all
+	 * configuration values then sends a notification to the user.
+	 */
 	public function sharingAction() {
 		if (Minz_Request::isPost()) {
 			$params = Minz_Request::params();
@@ -222,25 +309,31 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			$this->view->conf->save();
 			invalidateHttpCache();
 
-			$notif = array(
-				'type' => 'good',
-				'content' => Minz_Translate::t('configuration_updated')
-			);
-			Minz_Session::_param('notification', $notif);
-
-			Minz_Request::forward(array('c' => 'configure', 'a' => 'sharing'), true);
+			Minz_Request::good(_t('configuration_updated'),
+			                   array('c' => 'configure', 'a' => 'sharing'));
 		}
 
-		Minz_View::prependTitle(Minz_Translate::t('sharing') . ' · ');
+		Minz_View::prependTitle(_t('sharing') . ' · ');
 	}
 
+	/**
+	 * This action handles the shortcut configuration page.
+	 *
+	 * It displays the shortcut configuration page.
+	 * If this action is reached through a POST request, it stores all new
+	 * configuration values then sends a notification to the user.
+	 *
+	 * The authorized values for shortcuts are letters (a to z), numbers (0
+	 * to 9), function keys (f1 to f12), backspace, delete, down, end, enter,
+	 * escape, home, insert, left, page down, page up, return, right, space,
+	 * tab and up.
+	 */
 	public function shortcutAction() {
 		$list_keys = array('a', 'b', 'backspace', 'c', 'd', 'delete', 'down', 'e', 'end', 'enter',
 		                    'escape', 'f', 'g', 'h', 'home', 'i', 'insert', 'j', 'k', 'l', 'left',
 		                    'm', 'n', 'o', 'p', 'page_down', 'page_up', 'q', 'r', 'return', 'right',
 		                    's', 'space', 't', 'tab', 'u', 'up', 'v', 'w', 'x', 'y',
-		                    'z', '0', '1', '2', '3', '4', '5', '6', '7', '8',
-		                    '9', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9',
+		                    'z', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9',
 		                    'f10', 'f11', 'f12');
 		$this->view->list_keys = $list_keys;
 
@@ -258,44 +351,50 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			$this->view->conf->save();
 			invalidateHttpCache();
 
-			$notif = array(
-				'type' => 'good',
-				'content' => Minz_Translate::t('shortcuts_updated')
-			);
-			Minz_Session::_param('notification', $notif);
-
-			Minz_Request::forward(array('c' => 'configure', 'a' => 'shortcut'), true);
+			Minz_Request::good(_t('shortcuts_updated'),
+			                   array('c' => 'configure', 'a' => 'shortcut'));
 		}
 
-		Minz_View::prependTitle(Minz_Translate::t('shortcuts') . ' · ');
+		Minz_View::prependTitle(_t('shortcuts') . ' · ');
 	}
 
+	/**
+	 * This action display the user configuration page
+	 *
+	 * @todo move that action in the user controller
+	 */
 	public function usersAction() {
-		Minz_View::prependTitle(Minz_Translate::t('users') . ' · ');
+		Minz_View::prependTitle(_t('users') . ' · ');
 	}
 
+	/**
+	 * This action handles the archive configuration page.
+	 *
+	 * It displays the archive configuration page.
+	 * If this action is reached through a POST request, it stores all new
+	 * configuration values then sends a notification to the user.
+	 *
+	 * The options available on that page are:
+	 *   - duration to retain old article (default: 3)
+	 *   - number of article to retain per feed (default: 0)
+	 *   - refresh frequency (default: -2)
+	 *
+	 * @todo explain why the default value is -2 but this value does not
+	 *       exist in the drop-down list
+	 */
 	public function archivingAction() {
 		if (Minz_Request::isPost()) {
-			$old = Minz_Request::param('old_entries', 3);
-			$keepHistoryDefault = Minz_Request::param('keep_history_default', 0);
-			$ttlDefault = Minz_Request::param('ttl_default', -2);
-
-			$this->view->conf->_old_entries($old);
-			$this->view->conf->_keep_history_default($keepHistoryDefault);
-			$this->view->conf->_ttl_default($ttlDefault);
+			$this->view->conf->_old_entries(Minz_Request::param('old_entries', 3));
+			$this->view->conf->_keep_history_default(Minz_Request::param('keep_history_default', 0));
+			$this->view->conf->_ttl_default(Minz_Request::param('ttl_default', -2));
 			$this->view->conf->save();
 			invalidateHttpCache();
 
-			$notif = array(
-				'type' => 'good',
-				'content' => Minz_Translate::t('configuration_updated')
-			);
-			Minz_Session::_param('notification', $notif);
-
-			Minz_Request::forward(array('c' => 'configure', 'a' => 'archiving'), true);
+			Minz_Request::good(_t('configuration_updated'),
+			                   array('c' => 'configure', 'a' => 'archiving'));
 		}
 
-		Minz_View::prependTitle(Minz_Translate::t('archiving_configuration') . ' · ');
+		Minz_View::prependTitle(_t('archiving_configuration') . ' · ');
 
 		$entryDAO = FreshRSS_Factory::createEntryDao();
 		$this->view->nb_total = $entryDAO->count();
@@ -305,28 +404,35 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			$this->view->size_total = $entryDAO->size(true);
 		}
 	}
-	
+
+	/**
+	 * This action handles the user queries configuration page.
+	 *
+	 * If this action is reached through a POST request, it stores all new
+	 * configuration values then sends a notification to the user then
+	 * redirect to the same page.
+	 * If this action is not reached through a POST request, it displays the
+	 * configuration page and verifies that every user query is runable by
+	 * checking if categories and feeds are still in use.
+	 */
 	public function queriesAction() {
 		if (Minz_Request::isPost()) {
 			$queries = Minz_Request::param('queries', array());
 
 			foreach ($queries as $key => $query) {
 				if (!$query['name']) {
-					$query['name'] = Minz_Translate::t('query_number', $key + 1);
+					$query['name'] = _t('query_number', $key + 1);
 				}
 			}
 			$this->view->conf->_queries($queries);
 			$this->view->conf->save();
 
-			$notif = array(
-				'type' => 'good',
-				'content' => Minz_Translate::t('configuration_updated')
-			);
-			Minz_Session::_param('notification', $notif);
-
-			Minz_Request::forward(array('c' => 'configure', 'a' => 'queries'), true);
+			Minz_Request::good(_t('configuration_updated'),
+			                   array('c' => 'configure', 'a' => 'queries'));
 		} else {
 			$this->view->query_get = array();
+			$cat_dao = new FreshRSS_CategoryDAO();
+			$feed_dao = FreshRSS_Factory::createFeedDao();
 			foreach ($this->view->conf->queries as $key => $query) {
 				if (!isset($query['get'])) {
 					continue;
@@ -334,51 +440,83 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 
 				switch ($query['get'][0]) {
 				case 'c':
-					$dao = new FreshRSS_CategoryDAO();
-					$category = $dao->searchById(substr($query['get'], 2));
+					$category = $cat_dao->searchById(substr($query['get'], 2));
+
+					$deprecated = true;
+					$cat_name = '';
+					if ($category) {
+						$cat_name = $category->name();
+						$deprecated = false;
+					}
+
 					$this->view->query_get[$key] = array(
 						'type' => 'category',
-						'name' => $category->name(),
+						'name' => $cat_name,
+						'deprecated' => $deprecated,
 					);
 					break;
 				case 'f':
-					$dao = FreshRSS_Factory::createFeedDao();
-					$feed = $dao->searchById(substr($query['get'], 2));
+					$feed = $feed_dao->searchById(substr($query['get'], 2));
+
+					$deprecated = true;
+					$feed_name = '';
+					if ($feed) {
+						$feed_name = $feed->name();
+						$deprecated = false;
+					}
+
 					$this->view->query_get[$key] = array(
 						'type' => 'feed',
-						'name' => $feed->name(),
+						'name' => $feed_name,
+						'deprecated' => $deprecated,
 					);
 					break;
 				case 's':
 					$this->view->query_get[$key] = array(
 						'type' => 'favorite',
 						'name' => 'favorite',
+						'deprecated' => false,
 					);
 					break;
 				case 'a':
 					$this->view->query_get[$key] = array(
 						'type' => 'all',
 						'name' => 'all',
+						'deprecated' => false,
 					);
 					break;
 				}
 			}
 		}
 
-		Minz_View::prependTitle(Minz_Translate::t('queries') . ' · ');
+		Minz_View::prependTitle(_t('queries') . ' · ');
 	}
-	
+
+	/**
+	 * This action handles the creation of a user query.
+	 *
+	 * It gets the GET parameters and stores them in the configuration query
+	 * storage. Before it is saved, the unwanted parameters are unset to keep
+	 * lean data.
+	 */
 	public function addQueryAction() {
+		$whitelist = array('get', 'order', 'name', 'search', 'state');
 		$queries = $this->view->conf->queries;
 		$query = Minz_Request::params();
-		$query['name'] = Minz_Translate::t('query_number', count($queries) + 1);
-		unset($query['output']);
-		unset($query['token']);
+		$query['name'] = _t('query_number', count($queries) + 1);
+		foreach ($query as $key => $value) {
+			if (!in_array($key, $whitelist)) {
+				unset($query[$key]);
+			}
+		}
+		if (!empty($query['state']) && $query['state'] & FreshRSS_Entry::STATE_STRICT) {
+			$query['state'] -= FreshRSS_Entry::STATE_STRICT;
+		}
 		$queries[] = $query;
 		$this->view->conf->_queries($queries);
 		$this->view->conf->save();
 
-		// Minz_Request::forward(array('params' => $query), true);
-		Minz_Request::forward(array('c' => 'configure', 'a' => 'queries'), true);
+		Minz_Request::good(_t('query_created', $query['name']),
+		                   array('c' => 'configure', 'a' => 'queries'));
 	}
 }

+ 4 - 0
app/Controllers/entryController.php

@@ -45,6 +45,10 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
 
 		$entryDAO = FreshRSS_Factory::createEntryDao();
 		if ($id == false) {
+			if (!Minz_Request::isPost()) {
+				return;
+			}
+
 			if (!$get) {
 				$entryDAO->markReadEntries ($idMax);
 			} else {

+ 19 - 3
app/Controllers/feedController.php

@@ -383,7 +383,16 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 
 			$feedDAO = FreshRSS_Factory::createFeedDao();
 			if ($type == 'category') {
+				// List feeds to remove then related user queries.
+				$feeds = $feedDAO->listByCategory($id);
+
 				if ($feedDAO->deleteFeedByCategory ($id)) {
+					// Remove related queries
+					foreach ($feeds as $feed) {
+						$this->view->conf->remove_query_by_get('f_' . $feed->id());
+					}
+					$this->view->conf->save();
+
 					$notif = array (
 						'type' => 'good',
 						'content' => Minz_Translate::t ('category_emptied')
@@ -397,6 +406,10 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 				}
 			} else {
 				if ($feedDAO->deleteFeed ($id)) {
+					// Remove related queries
+					$this->view->conf->remove_query_by_get('f_' . $id);
+					$this->view->conf->save();
+
 					$notif = array (
 						'type' => 'good',
 						'content' => Minz_Translate::t ('feed_deleted')
@@ -412,10 +425,13 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 
 			Minz_Session::_param ('notification', $notif);
 
-			if ($type == 'category') {
-				Minz_Request::forward (array ('c' => 'configure', 'a' => 'categorize'), true);
+			$redirect_url = Minz_Request::param('r', false, true);
+			if ($redirect_url) {
+				Minz_Request::forward($redirect_url);
+			} elseif ($type == 'category') {
+				Minz_Request::forward(array ('c' => 'configure', 'a' => 'categorize'), true);
 			} else {
-				Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed'), true);
+				Minz_Request::forward(array ('c' => 'configure', 'a' => 'feed'), true);
 			}
 		}
 	}

+ 20 - 8
app/Controllers/importExportController.php

@@ -109,7 +109,6 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 		// A *very* basic guess file type function. Only based on filename
 		// That's could be improved but should be enough, at least for a first
 		// implementation.
-		// TODO: improve this function?
 
 		if (substr_compare($filename, '.zip', -4) === 0) {
 			return 'zip';
@@ -119,8 +118,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 		} elseif (substr_compare($filename, '.json', -5) === 0 &&
 		          strpos($filename, 'starred') !== false) {
 			return 'json_starred';
-		} elseif (substr_compare($filename, '.json', -5) === 0 &&
-		          strpos($filename, 'feed_') === 0) {
+		} elseif (substr_compare($filename, '.json', -5) === 0) {
 			return 'json_feed';
 		} else {
 			return 'unknown';
@@ -239,13 +237,27 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 		);
 
 		$error = false;
+		$article_to_feed = array();
+
+		// First, we check feeds of articles are in DB (and add them if needed).
 		foreach ($article_object['items'] as $item) {
 			$feed = $this->addFeedArticles($item['origin'], $google_compliant);
 			if (is_null($feed)) {
 				$error = true;
+			} else {
+				$article_to_feed[$item['id']] = $feed->id();
+			}
+		}
+
+		// Then, articles are imported.
+		$prepared_statement = $this->entryDAO->addEntryPrepare();
+		$this->entryDAO->beginTransaction();
+		foreach ($article_object['items'] as $item) {
+			if (!isset($article_to_feed[$item['id']])) {
 				continue;
 			}
 
+			$feed_id = $article_to_feed[$item['id']];
 			$author = isset($item['author']) ? $item['author'] : '';
 			$key_content = ($google_compliant && !isset($item['content'])) ?
 			               'summary' : 'content';
@@ -257,21 +269,21 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 			}
 
 			$entry = new FreshRSS_Entry(
-				$feed->id(), $item['id'], $item['title'], $author,
+				$feed_id, $item['id'], $item['title'], $author,
 				$item[$key_content]['content'], $item['alternate'][0]['href'],
 				$item['published'], $is_read, $starred
 			);
+			$entry->_id(min(time(), $entry->date(true)) . uSecString());
 			$entry->_tags($tags);
 
-			//FIME: Use entryDAO->addEntryPrepare(). Do not call entryDAO->listLastGuidsByFeed() for each entry. Consider using a transaction.
-			$id = $this->entryDAO->addEntryObject(
-				$entry, $this->view->conf, $feed->keepHistory()
-			);
+			$values = $entry->toArray();
+			$id = $this->entryDAO->addEntry($values, $prepared_statement);
 
 			if (!$error && ($id === false)) {
 				$error = true;
 			}
 		}
+		$this->entryDAO->commit();
 
 		return $error;
 	}

+ 80 - 0
app/Controllers/indexController.php

@@ -83,6 +83,11 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 		$nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page);
 		$first = Minz_Request::param ('next', '');
 
+		$ajax_request = Minz_Request::param('ajax', false);
+		if ($output === 'reader') {
+			$nb = max(1, round($nb / 2));
+		}
+
 		if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ) {	//Any unread article in this category at all?
 			switch ($getType) {
 				case 'a':
@@ -332,6 +337,10 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 	}
 
 	public function formLoginAction () {
+		if ($this->view->loginOk) {
+			Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
+		}
+
 		if (Minz_Request::isPost()) {
 			$ok = false;
 			$nonce = Minz_Session::param('nonce');
@@ -415,4 +424,75 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 		self::deleteLongTermCookie();
 		Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
 	}
+
+	public function resetAuthAction() {
+		Minz_View::prependTitle(_t('auth_reset') . ' · ');
+		Minz_View::appendScript(Minz_Url::display(
+			'/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')
+		));
+
+		$this->view->no_form = false;
+		// Enable changement of auth only if Persona!
+		if (Minz_Configuration::authType() != 'persona') {
+			$this->view->message = array(
+				'status' => 'bad',
+				'title' => _t('damn'),
+				'body' => _t('auth_not_persona')
+			);
+			$this->view->no_form = true;
+			return;
+		}
+
+		$conf = new FreshRSS_Configuration(Minz_Configuration::defaultUser());
+		// Admin user must have set its master password.
+		if (!$conf->passwordHash) {
+			$this->view->message = array(
+				'status' => 'bad',
+				'title' => _t('damn'),
+				'body' => _t('auth_no_password_set')
+			);
+			$this->view->no_form = true;
+			return;
+		}
+
+		invalidateHttpCache();
+
+		if (Minz_Request::isPost()) {
+			$nonce = Minz_Session::param('nonce');
+			$username = Minz_Request::param('username', '');
+			$c = Minz_Request::param('challenge', '');
+			if (!(ctype_alnum($username) && ctype_graph($c) && ctype_alnum($nonce))) {
+				Minz_Log::debug('Invalid credential parameters:' .
+				                ' user=' . $username .
+				                ' challenge=' . $c .
+				                ' nonce=' . $nonce);
+				Minz_Request::bad(_t('invalid_login'),
+				                  array('c' => 'index', 'a' => 'resetAuth'));
+			}
+
+			if (!function_exists('password_verify')) {
+				include_once(LIB_PATH . '/password_compat.php');
+			}
+
+			$s = $conf->passwordHash;
+			$ok = password_verify($nonce . $s, $c);
+			if ($ok) {
+				Minz_Configuration::_authType('form');
+				$ok = Minz_Configuration::writeFile();
+
+				if ($ok) {
+					Minz_Request::good(_t('auth_form_set'));
+				} else {
+					Minz_Request::bad(_t('auth_form_not_set'),
+				                      array('c' => 'index', 'a' => 'resetAuth'));
+				}
+			} else {
+				Minz_Log::debug('Password mismatch for user ' . $username .
+				                ', nonce=' . $nonce . ', c=' . $c);
+
+				Minz_Request::bad(_t('invalid_login'),
+				                  array('c' => 'index', 'a' => 'resetAuth'));
+			}
+		}
+	}
 }

+ 45 - 0
app/Controllers/statsController.php

@@ -1,7 +1,21 @@
 <?php
 
+/**
+ * Controller to handle application statistics.
+ */
 class FreshRSS_stats_Controller extends Minz_ActionController {
 
+	/**
+	 * This action handles the statistic main page.
+	 *
+	 * It displays the statistic main page.
+	 * The values computed to display the page are:
+	 *   - repartition of read/unread/favorite/not favorite
+	 *   - number of article per day
+	 *   - number of feed by category
+	 *   - number of article by category
+	 *   - list of most prolific feed
+	 */
 	public function indexAction() {
 		$statsDAO = FreshRSS_Factory::createStatsDAO();
 		Minz_View::appendScript(Minz_Url::display('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js')));
@@ -12,6 +26,17 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
 		$this->view->topFeed = $statsDAO->calculateTopFeed();
 	}
 
+	/**
+	 * This action handles the idle feed statistic page.
+	 *
+	 * It displays the list of idle feed for different period. The supported
+	 * periods are:
+	 *   - last year
+	 *   - last 6 months
+	 *   - last 3 months
+	 *   - last month
+	 *   - last week
+	 */
 	public function idleAction() {
 		$statsDAO = FreshRSS_Factory::createStatsDAO();
 		$feeds = $statsDAO->calculateFeedLastDate();
@@ -56,6 +81,18 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
 		$this->view->idleFeeds = $idleFeeds;
 	}
 
+	/**
+	 * This action handles the article repartition statistic page.
+	 *
+	 * It displays the number of article and the average of article for the
+	 * following periods:
+	 *   - hour of the day
+	 *   - day of the week
+	 *   - month
+	 *
+	 * @todo verify that the metrics used here make some sense. Especially
+	 *       for the average.
+	 */
 	public function repartitionAction() {
 		$statsDAO = FreshRSS_Factory::createStatsDAO();
 		$categoryDAO = new FreshRSS_CategoryDAO();
@@ -67,10 +104,18 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
 		$this->view->days = $statsDAO->getDays();
 		$this->view->months = $statsDAO->getMonths();
 		$this->view->repartitionHour = $statsDAO->calculateEntryRepartitionPerFeedPerHour($id);
+		$this->view->averageHour = $statsDAO->calculateEntryAveragePerFeedPerHour($id);
 		$this->view->repartitionDayOfWeek = $statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id);
+		$this->view->averageDayOfWeek = $statsDAO->calculateEntryAveragePerFeedPerDayOfWeek($id);
 		$this->view->repartitionMonth = $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id);
+		$this->view->averageMonth = $statsDAO->calculateEntryAveragePerFeedPerMonth($id);
 	}
 
+	/**
+	 * This action is called before every other action in that class. It is
+	 * the common boiler plate for every action. It is triggered by the
+	 * underlying framework.
+	 */
 	public function firstAction() {
 		if (!$this->view->loginOk) {
 			Minz_Error::error(

+ 129 - 0
app/Controllers/updateController.php

@@ -0,0 +1,129 @@
+<?php
+
+class FreshRSS_update_Controller extends Minz_ActionController {
+	public function firstAction() {
+		$current_user = Minz_Session::param('currentUser', '');
+		if (!$this->view->loginOk && Minz_Configuration::isAdmin($current_user)) {
+			Minz_Error::error(
+				403,
+				array('error' => array(_t('access_denied')))
+			);
+		}
+
+		invalidateHttpCache();
+
+		Minz_View::prependTitle(_t('update_system') . ' · ');
+		$this->view->update_to_apply = false;
+		$this->view->last_update_time = 'unknown';
+		$this->view->check_last_hour = false;
+		$timestamp = (int)@file_get_contents(DATA_PATH . '/last_update.txt');
+		if (is_numeric($timestamp) && $timestamp > 0) {
+			$this->view->last_update_time = timestamptodate($timestamp);
+			$this->view->check_last_hour = (time() - 3600) <= $timestamp;
+		}
+	}
+
+	public function indexAction() {
+		if (file_exists(UPDATE_FILENAME) && !is_writable(FRESHRSS_PATH)) {
+			$this->view->message = array(
+				'status' => 'bad',
+				'title' => _t('damn'),
+				'body' => _t('file_is_nok', FRESHRSS_PATH)
+			);
+		} elseif (file_exists(UPDATE_FILENAME)) {
+			// There is an update file to apply!
+			$this->view->update_to_apply = true;
+			$this->view->message = array(
+				'status' => 'good',
+				'title' => _t('ok'),
+				'body' => _t('update_can_apply')
+			);
+		}
+	}
+
+	public function checkAction() {
+		$this->view->change_view('update', 'index');
+
+		if (file_exists(UPDATE_FILENAME) || $this->view->check_last_hour) {
+			// There is already an update file to apply: we don't need to check
+			// the webserver!
+			// Or if already check during the last hour, do nothing.
+			Minz_Request::forward(array('c' => 'update'));
+
+			return;
+		}
+
+		$c = curl_init(FRESHRSS_UPDATE_WEBSITE);
+		curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
+		curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
+		curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
+		$result = curl_exec($c);
+		$c_status = curl_getinfo($c, CURLINFO_HTTP_CODE);
+		$c_error = curl_error($c);
+		curl_close($c);
+
+		if ($c_status !== 200) {
+			Minz_Log::error(
+				'Error during update (HTTP code ' . $c_status . '): ' . $c_error
+			);
+
+			$this->view->message = array(
+				'status' => 'bad',
+				'title' => _t('damn'),
+				'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE)
+			);
+			return;
+		}
+
+		$res_array = explode("\n", $result, 2);
+		$status = $res_array[0];
+		if (strpos($status, 'UPDATE') !== 0) {
+			$this->view->message = array(
+				'status' => 'bad',
+				'title' => _t('damn'),
+				'body' => _t('no_update')
+			);
+
+			@file_put_contents(DATA_PATH . '/last_update.txt', time());
+
+			return;
+		}
+
+		$script = $res_array[1];
+		if (file_put_contents(UPDATE_FILENAME, $script) !== false) {
+			Minz_Request::forward(array('c' => 'update'));
+		} else {
+			$this->view->message = array(
+				'status' => 'bad',
+				'title' => _t('damn'),
+				'body' => _t('update_problem', 'Cannot save the update script')
+			);
+		}
+	}
+
+	public function applyAction() {
+		if (!file_exists(UPDATE_FILENAME) || !is_writable(FRESHRSS_PATH)) {
+			Minz_Request::forward(array('c' => 'update'), true);
+		}
+
+		require(UPDATE_FILENAME);
+
+		if (Minz_Request::isPost()) {
+			save_info_update();
+		}
+
+		if (!need_info_update()) {
+			$res = apply_update();
+
+			if ($res === true) {
+				@unlink(UPDATE_FILENAME);
+				@file_put_contents(DATA_PATH . '/last_update.txt', time());
+
+				Minz_Request::good(_t('update_finished'));
+			} else {
+				Minz_Request::bad(_t('update_problem', $res),
+				                  array('c' => 'update', 'a' => 'index'));
+			}
+		}
+	}
+}

+ 13 - 4
app/FreshRSS.php

@@ -6,8 +6,7 @@ class FreshRSS extends Minz_FrontController {
 		}
 		$loginOk = $this->accessControl(Minz_Session::param('currentUser', ''));
 		$this->loadParamsView();
-		if (Minz_Request::isPost() && (empty($_SERVER['HTTP_REFERER']) ||
-			Minz_Request::getDomainName() !== parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST))) {
+		if (Minz_Request::isPost() && !is_referer_from_same_domain()) {
 			$loginOk = false;	//Basic protection against XSRF attacks
 			Minz_Error::error(
 				403,
@@ -140,11 +139,21 @@ class FreshRSS extends Minz_FrontController {
 		}
 	}
 
-	private function loadStylesAndScripts ($loginOk) {
+	private function loadStylesAndScripts($loginOk) {
 		$theme = FreshRSS_Themes::load($this->conf->theme);
 		if ($theme) {
 			foreach($theme['files'] as $file) {
-				Minz_View::appendStyle (Minz_Url::display ('/themes/' . $theme['id'] . '/' . $file . '?' . @filemtime(PUBLIC_PATH . '/themes/' . $theme['id'] . '/' . $file)));
+				if ($file[0] === '_') {
+					$theme_id = 'base-theme';
+					$filename = substr($file, 1);
+				} else {
+					$theme_id = $theme['id'];
+					$filename = $file;
+				}
+				$filetime = @filemtime(PUBLIC_PATH . '/themes/' . $theme_id . '/' . $filename);
+				Minz_View::appendStyle(Minz_Url::display(
+					'/themes/' . $theme_id . '/' . $filename . '?' . $filetime
+				));
 			}
 		}
 

+ 37 - 2
app/Models/Configuration.php

@@ -45,6 +45,8 @@ class FreshRSS_Configuration {
 			'load_more' => 'm',
 			'auto_share' => 's',
 			'focus_search' => 'a',
+			'user_filter' => 'u',
+			'help' => 'f1',
 		),
 		'topline_read' => true,
 		'topline_favorite' => true,
@@ -58,6 +60,7 @@ class FreshRSS_Configuration {
 		'bottomline_link' => true,
 		'sharing' => array(),
 		'queries' => array(),
+		'html5_notif_timeout' => 0,
 	);
 
 	private $available_languages = array(
@@ -120,6 +123,16 @@ class FreshRSS_Configuration {
 		return $this->available_languages;
 	}
 
+	public function remove_query_by_get($get) {
+		$final_queries = array();
+		foreach ($this->queries as $key => $query) {
+			if (empty($query['get']) || $query['get'] !== $get) {
+				$final_queries[$key] = $query;
+			}
+		}
+		$this->_queries($final_queries);
+	}
+
 	public function _language($value) {
 		if (!isset($this->available_languages[$value])) {
 			$value = 'en';
@@ -138,7 +151,18 @@ class FreshRSS_Configuration {
 		}
 	}
 	public function _default_view ($value) {
-		$this->data['default_view'] = $value === FreshRSS_Entry::STATE_ALL ? FreshRSS_Entry::STATE_ALL : FreshRSS_Entry::STATE_NOT_READ;
+		switch ($value) {
+		case FreshRSS_Entry::STATE_ALL:
+			// left blank on purpose
+		case FreshRSS_Entry::STATE_NOT_READ:
+			// left blank on purpose
+		case FreshRSS_Entry::STATE_STRICT + FreshRSS_Entry::STATE_NOT_READ:
+			$this->data['default_view'] = $value;
+			break;
+		default:
+			$this->data['default_view'] = FreshRSS_Entry::STATE_ALL;
+			break;
+		}
 	}
 	public function _display_posts ($value) {
 		$this->data['display_posts'] = ((bool)$value) && $value !== 'no';
@@ -209,6 +233,7 @@ class FreshRSS_Configuration {
 	}
 	public function _sharing ($values) {
 		$this->data['sharing'] = array();
+		$unique = array();
 		foreach ($values as $value) {
 			if (!is_array($value)) {
 				continue;
@@ -234,7 +259,11 @@ class FreshRSS_Configuration {
 				$value['name'] = $value['type'];
 			}
 
-			$this->data['sharing'][] = $value;
+			$json_value = json_encode($value);
+			if (!in_array($json_value, $unique)) {
+				$unique[] = $json_value;
+				$this->data['sharing'][] = $value;
+			}
 		}
 	}
 	public function _queries ($values) {
@@ -261,6 +290,12 @@ class FreshRSS_Configuration {
 			$this->data['content_width'] = 'thin';
 		}
 	}
+	
+	public function _html5_notif_timeout ($value) {
+		$value = intval($value);
+		$this->data['html5_notif_timeout'] = $value >= 0 ? $value : 0;
+	}
+	
 	public function _token($value) {
 		$this->data['token'] = $value;
 	}

+ 1 - 0
app/Models/Entry.php

@@ -6,6 +6,7 @@ class FreshRSS_Entry extends Minz_Model {
 	const STATE_NOT_READ = 2;
 	const STATE_FAVORITE = 4;
 	const STATE_NOT_FAVORITE = 8;
+	const STATE_STRICT = 16;
 
 	private $id = 0;
 	private $guid;

+ 2 - 0
app/Models/EntryDAO.php

@@ -333,6 +333,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
 		if ($state & FreshRSS_Entry::STATE_NOT_READ) {
 			if (!($state & FreshRSS_Entry::STATE_READ)) {
 				$where .= 'AND e1.is_read=0 ';
+			} elseif ($state & FreshRSS_Entry::STATE_STRICT) {
+				$where .= 'AND e1.is_read=0 ';
 			}
 		}
 		elseif ($state & FreshRSS_Entry::STATE_READ) {

+ 1 - 1
app/Models/FeedDAO.php

@@ -331,7 +331,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
 		$id_max = intval($date_min) . '000000';
 
 		$stm->bindParam(':id_feed', $id, PDO::PARAM_INT);
-		$stm->bindParam(':id_max', $id_max, PDO::PARAM_INT);
+		$stm->bindParam(':id_max', $id_max, PDO::PARAM_STR);
 		$stm->bindParam(':keep', $keep, PDO::PARAM_INT);
 
 		if ($stm && $stm->execute()) {

+ 69 - 0
app/Models/StatsDAO.php

@@ -151,6 +151,74 @@ SQL;
 		return $this->convertToSerie($repartition);
 	}
 
+	/**
+	 * Calculates the average number of article per hour per feed
+	 *
+	 * @param integer $feed id
+	 * @return integer
+	 */
+	public function calculateEntryAveragePerFeedPerHour($feed = null) {
+		return $this->calculateEntryAveragePerFeedPerPeriod(1/24, $feed);
+	}
+	
+	/**
+	 * Calculates the average number of article per day of week per feed
+	 *
+	 * @param integer $feed id
+	 * @return integer
+	 */
+	public function calculateEntryAveragePerFeedPerDayOfWeek($feed = null) {
+		return $this->calculateEntryAveragePerFeedPerPeriod(7, $feed);
+	}
+
+	/**
+	 * Calculates the average number of article per month per feed
+	 *
+	 * @param integer $feed id
+	 * @return integer
+	 */
+	public function calculateEntryAveragePerFeedPerMonth($feed = null) {
+		return $this->calculateEntryAveragePerFeedPerPeriod(30, $feed);
+	}
+	
+	/**
+	 * Calculates the average number of article per feed
+	 * 
+	 * @param float $period number used to divide the number of day in the period
+	 * @param integer $feed id
+	 * @return integer
+	 */
+	protected function calculateEntryAveragePerFeedPerPeriod($period, $feed = null) {
+		if ($feed) {
+			$restrict = "WHERE e.id_feed = {$feed}";
+		} else {
+			$restrict = '';
+		}
+		$sql = <<<SQL
+SELECT COUNT(1) AS count
+, MIN(date) AS date_min
+, MAX(date) AS date_max
+FROM {$this->prefix}entry AS e
+{$restrict}
+SQL;
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetch(PDO::FETCH_NAMED);
+		$date_min = new \DateTime();
+		$date_min->setTimestamp($res['date_min']);
+		$date_max = new \DateTime();
+		$date_max->setTimestamp($res['date_max']);
+		$interval = $date_max->diff($date_min, true);
+		$interval_in_days = $interval->format('%a');
+		if ($interval_in_days <= 0) {
+			// Surely only one article.
+			// We will return count / (period/period) == count.
+			$interval_in_days = $period;
+		}
+
+		return round($res['count'] / ($interval_in_days / $period), 2);
+	}
+
 	/**
 	 * Initialize an array for statistics depending on a range
 	 *
@@ -247,6 +315,7 @@ SQL;
 SELECT MAX(f.id) as id
 , MAX(f.name) AS name
 , MAX(date) AS last_date
+, COUNT(*) AS nb_articles
 FROM {$this->prefix}feed AS f,
 {$this->prefix}entry AS e
 WHERE f.id = e.id_feed

+ 1 - 0
app/Models/StatsDAOSQLite.php

@@ -53,6 +53,7 @@ SQL;
 		$stm->execute();
 		$res = $stm->fetchAll(PDO::FETCH_NAMED);
 
+		$repartition = array();
 		foreach ($res as $value) {
 			$repartition[(int) $value['period']] = (int) $value['count'];
 		}

+ 1 - 0
app/Models/Themes.php

@@ -96,6 +96,7 @@ class FreshRSS_Themes extends Minz_Model {
 			'search' => '🔍',
 			'share' => '♺',
 			'starred' => '★',
+			'stats' => '%',
 			'tag' => '⚐',
 			'up' => '△',
 			'view-normal' => '☰',

+ 45 - 8
app/i18n/en.php

@@ -5,6 +5,7 @@ return array (
 	'login'				=> 'Login',
 	'keep_logged_in'		=> 'Keep me logged in <small>(1 month)</small>',
 	'login_with_persona'		=> 'Login with Persona',
+	'login_persona_problem'		=> 'Connection problem with Persona?',
 	'logout'			=> 'Logout',
 	'search'			=> 'Search words or #tags',
 	'search_short'			=> 'Search',
@@ -42,9 +43,11 @@ return array (
 	'query_state_15'		=> 'Display all articles',
 	'query_number'			=> 'Query n°%d',
 	'add_query'			=> 'Add a query',
+	'query_created'			=> 'Query "%s" has been created.',
 	'no_query'			=> 'You haven’t created any user query yet.',
 	'query_filter'			=> 'Filter applied:',
 	'no_query_filter'		=> 'No filter',
+	'query_deprecated'		=> 'This query is no longer valid. The referenced category or feed has been deleted.',
 	'about'				=> 'About',
 	'stats'				=> 'Statistics',
 	'stats_idle'			=> 'Idle feeds',
@@ -92,6 +95,7 @@ return array (
 	'rss_view'			=> 'RSS feed',
 	'show_all_articles'		=> 'Show all articles',
 	'show_not_reads'		=> 'Show only unread',
+	'show_adaptive'			=> 'Adjust showing',
 	'show_read'			=> 'Show only read',
 	'show_favorite'			=> 'Show only favorites',
 	'show_not_favorite'		=> 'Show all but favorites',
@@ -111,7 +115,7 @@ return array (
 	'access_denied'			=> 'You don’t have permission to access this page',
 	'page_not_found'		=> 'You are looking for a page which doesn’t exist',
 	'error_occurred'		=> 'An error occurred',
-	'error_occurred_update'	=> 'Nothing was changed',
+	'error_occurred_update'		=> 'Nothing was changed',
 
 	'default_category'		=> 'Uncategorized',
 	'categories_updated'		=> 'Categories have been updated',
@@ -152,10 +156,13 @@ return array (
 	'public'			=> 'Public',
 	'invalid_login'			=> 'Login is invalid',
 
+	'file_is_nok'			=> 'Check permissions on <em>%s</em> directory. HTTP server must have rights to write into.',
+
 	// VIEWS
 	'save'				=> 'Save',
 	'delete'			=> 'Delete',
 	'cancel'			=> 'Cancel',
+	'submit'			=> 'Submit',
 
 	'back_to_rss_feeds'		=> '← Go back to your RSS feeds',
 	'feeds_moved_category_deleted'	=> 'When you delete a category, their feeds are automatically classified under <em>%s</em>.',
@@ -180,6 +187,9 @@ return array (
 	'auto_share'			=> 'Share',
 	'auto_share_help'		=> 'If there is only one sharing mode, it is used. Else modes are accessible by their number.',
 	'focus_search'			=> 'Access search box',
+	'user_filter'			=> 'Access user filters',
+	'user_filter_help'		=> 'If there is only one user filter, it is used. Else filters are accessible by their number.',
+	'help'				=> 'Display documentation',
 
 	'file_to_import'		=> 'File to import<br />(OPML, Json or Zip)',
 	'file_to_import_no_zip'		=> 'File to import<br />(OPML or Json)',
@@ -197,13 +207,15 @@ return array (
 
 	'informations'			=> 'Information',
 	'damn'				=> 'Damn!',
+	'ok'				=> 'Ok!',
+	'attention'			=> 'Be careful!',
 	'feed_in_error'			=> 'This feed has encountered a problem. Please verify that it is always reachable then actualize it.',
 	'feed_empty'			=> 'This feed is empty. Please verify that it is still maintained.',
 	'feed_description'		=> 'Description',
 	'website_url'			=> 'Website URL',
 	'feed_url'			=> 'Feed URL',
 	'articles'			=> 'articles',
-	'number_articles'		=> 'Number of articles',
+	'number_articles'		=> '%d articles',
 	'by_feed'			=> 'by feed',
 	'by_default'			=> 'By default',
 	'keep_history'			=> 'Minimum number of articles to keep',
@@ -225,7 +237,7 @@ return array (
 	'not_yet_implemented'		=> 'Not yet implemented',
 	'access_protected_feeds'	=> 'Connection allows to access HTTP protected RSS feeds',
 	'no_selected_feed'		=> 'No feed selected.',
-	'think_to_add'			=> '<a href="./?c=configure&amp;a=feed">You may add some feeds</a>.',
+	'think_to_add'			=> 'You may add some feeds.',
 
 	'current_user'			=> 'Current user',
 	'default_user'			=> 'Username of the default user <small>(maximum 16 alphanumeric characters)</small>',
@@ -237,7 +249,7 @@ return array (
 	'unsafe_autologin'		=> 'Allow unsafe automatic login using the format: ',
 	'api_enabled'			=> 'Allow <abbr>API</abbr> access <small>(required for mobile apps)</small>',
 	'auth_token'			=> 'Authentication token',
-	'explain_token'			=> 'Allows to access RSS output of the default user without authentication.<br /><kbd>%s?output=rss&token=%s</kbd>',
+	'explain_token'			=> 'Allows to access RSS output of the default user without authentication.<br /><kbd>%s?output=rss&amp;token=%s</kbd>',
 	'login_configuration'		=> 'Login',
 	'is_admin'			=> 'is administrator',
 	'auth_type'			=> 'Authentication method',
@@ -248,6 +260,7 @@ return array (
 	'users_list'			=> 'List of users',
 	'create_user'			=> 'Create new user',
 	'username'			=> 'Username',
+	'username_admin'		=> 'Administrator username',
 	'password'			=> 'Password',
 	'create'			=> 'Create',
 	'user_created'			=> 'User %s has been created',
@@ -256,31 +269,35 @@ return array (
 	'language'			=> 'Language',
 	'month'				=> 'months',
 	'archiving_configuration'	=> 'Archiving',
-	'delete_articles_every'	=> 'Remove articles after',
+	'delete_articles_every'		=> 'Remove articles after',
 	'purge_now'			=> 'Purge now',
 	'purge_completed'		=> 'Purge completed (%d articles deleted)',
 	'archiving_configuration_help'	=> 'More options are available in the individual stream settings',
 	'reading_configuration'		=> 'Reading',
 	'display_configuration'		=> 'Display',
 	'articles_per_page'		=> 'Number of articles per page',
+	'number_divided_when_reader'	=> 'Divided by 2 in the reading view.',
 	'default_view'			=> 'Default view',
+	'articles_to_display'		=> 'Articles to display',
 	'sort_order'			=> 'Sort order',
 	'auto_load_more'		=> 'Load next articles at the page bottom',
 	'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 (does not work with “Show all articles” configuration)',
 	'after_onread'			=> 'After “mark all as read”,',
 	'jump_next'			=> 'jump to next unread sibling (feed or category)',
 	'article_icons'			=> 'Article icons',
 	'top_line'			=> 'Top line',
 	'bottom_line'			=> 'Bottom line',
+	'html5_notif_timeout'		=> 'HTML5 notification timeout',
+	'seconds_(0_means_no_timeout)'	=> 'seconds (0 means no timeout)',
 	'img_with_lazyload'		=> 'Use "lazy load" mode to load pictures',
 	'sticky_post'			=> 'Stick the article to the top when opened',
 	'reading_confirm'		=> 'Display a confirmation dialog on “mark all as read” actions',
 	'auto_read_when'		=> 'Mark article as read…',
 	'article_viewed'		=> 'when article is viewed',
 	'article_open_on_website'	=> 'when article is opened on its original website',
-	'scroll'			=> 'during page scrolls',
+	'scroll'			=> 'while scrolling',
 	'upon_reception'		=> 'upon reception of the article',
 	'your_shaarli'			=> 'Your Shaarli',
 	'your_wallabag'			=> 'Your wallabag',
@@ -340,7 +357,7 @@ return array (
 	'agpl3'				=> '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
 	'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.',
 	'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 used 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://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.',
 	'version'			=> 'Version',
 
 	'logs'				=> 'Logs',
@@ -351,6 +368,7 @@ return array (
 	'login_required'		=> 'Login required:',
 
 	'confirm_action'		=> 'Are you sure you want to perform this action? It cannot be cancelled!',
+	'confirm_action_feed_cat'	=> 'Are you sure you want to perform this action? You may lost related favorites and user queries. It cannot be cancelled!',
 	'notif_title_new_articles'	=> 'FreshRSS: new articles!',
 	'notif_body_new_articles'	=> 'There are \d new articles to read on FreshRSS.',
 
@@ -414,4 +432,23 @@ return array (
 	'stats_entry_per_category'	=> 'Entries per category',
 	'stats_top_feed'		=> 'Top ten feeds',
 	'stats_entry_count'		=> 'Entry count',
+	'stats_no_idle'			=> 'There is no idle feed!',
+
+	'update'			=> 'Update',
+	'update_system'			=> 'Update system',
+	'update_check'			=> 'Check for new updates',
+	'update_last'			=> 'Last verification: %s',
+	'update_can_apply'		=> 'An update is available.',
+	'update_apply'			=> 'Apply',
+	'update_server_not_found'	=> 'Update server cannot be found. [%s]',
+	'no_update'			=> 'No update to apply',
+	'update_problem'		=> 'The update process has encountered an error: %s',
+	'update_finished'		=> 'Update completed!',
+
+	'auth_reset'			=> 'Authentication reset',
+	'auth_will_reset'		=> 'Authentication system will be reset: a form will be used instead of Persona.',
+	'auth_not_persona'		=> 'Only Persona system can be reset.',
+	'auth_no_password_set'		=> 'Administrator password hasn’t been set. This feature isn’t available.',
+	'auth_form_set'			=> 'Form is now your default authentication system.',
+	'auth_form_not_set'		=> 'A problem occured during authentication system configuration. Please retry later.',
 );

+ 46 - 9
app/i18n/fr.php

@@ -5,6 +5,7 @@ return array (
 	'login'				=> 'Connexion',
 	'keep_logged_in'		=> 'Rester connecté <small>(1 mois)</small>',
 	'login_with_persona'		=> 'Connexion avec Persona',
+	'login_persona_problem'		=> 'Problème de connexion à Persona ?',
 	'logout'			=> 'Déconnexion',
 	'search'			=> 'Rechercher des mots ou des #tags',
 	'search_short'			=> 'Rechercher',
@@ -42,9 +43,11 @@ return array (
 	'query_state_15'		=> 'Afficher tous les articles',
 	'query_number'			=> 'Filtre n°%d',
 	'add_query'			=> 'Créer un filtre',
+	'query_created'			=> 'Le filtre "%s" a bien été créé.',
 	'no_query'			=> 'Vous n’avez pas encore créé de filtre.',
 	'query_filter'			=> 'Filtres appliqués :',
 	'no_query_filter'		=> 'Aucun filtre appliqué',
+	'query_deprecated'		=> 'Ce filtre n’est plus valide. La catégorie ou le flux concerné a été supprimé.',
 	'about'				=> 'À propos',
 	'stats'				=> 'Statistiques',
 	'stats_idle'			=> 'Flux inactifs',
@@ -54,11 +57,11 @@ return array (
 	'stats_entry_per_day_of_week'	=> 'Par jour de la semaine',
 	'stats_entry_per_month'		=> 'Par mois',
 
-	'last_week'			=> 'La dernière semaine',
-	'last_month'			=> 'Le dernier mois',
-	'last_3_month'			=> 'Les derniers trois mois',
-	'last_6_month'			=> 'Les derniers six mois',
-	'last_year'			=> 'La dernière année',
+	'last_week'			=> 'Depuis la semaine dernière',
+	'last_month'			=> 'Depuis le mois dernier',
+	'last_3_month'			=> 'Depuis les trois derniers mois',
+	'last_6_month'			=> 'Depuis les six derniers mois',
+	'last_year'			=> 'Depuis l’année dernière',
 
 	'your_rss_feeds'		=> 'Vos flux RSS',
 	'add_rss_feed'			=> 'Ajouter un flux RSS',
@@ -92,6 +95,7 @@ return array (
 	'rss_view'			=> 'Flux RSS',
 	'show_all_articles'		=> 'Afficher tous les articles',
 	'show_not_reads'		=> 'Afficher les non lus',
+	'show_adaptive'			=> 'Adapter l’affichage',
 	'show_read'			=> 'Afficher les lus',
 	'show_favorite'			=> 'Afficher les favoris',
 	'show_not_favorite'		=> 'Afficher tout sauf les favoris',
@@ -152,10 +156,13 @@ return array (
 	'public'			=> 'Public',
 	'invalid_login'			=> 'L’identifiant est invalide !',
 
+	'file_is_nok'			=> 'Veuillez vérifier les droits sur le répertoire <em>%s</em>. Le serveur HTTP doit être capable d’écrire dedans.',
+
 	// VIEWS
 	'save'				=> 'Enregistrer',
 	'delete'			=> 'Supprimer',
 	'cancel'			=> 'Annuler',
+	'submit'			=> 'Valider',
 
 	'back_to_rss_feeds'		=> '← Retour à vos flux RSS',
 	'feeds_moved_category_deleted'	=> 'Lors de la suppression d’une catégorie, ses flux seront automatiquement classés dans <em>%s</em>.',
@@ -180,6 +187,9 @@ return array (
 	'auto_share'			=> 'Partager',
 	'auto_share_help'		=> 'S’il n’y a qu’un mode de partage, celui ci est utilisé automatiquement. Sinon ils sont accessibles par leur numéro.',
 	'focus_search'			=> 'Accéder à la recherche',
+	'user_filter'			=> 'Accéder aux filtres utilisateur',
+	'user_filter_help'		=> 'S’il n’y a qu’un filtre utilisateur, celui ci est utilisé automatiquement. Sinon ils sont accessibles par leur numéro.',
+	'help'				=> 'Afficher la documentation',
 
 	'file_to_import'		=> 'Fichier à importer<br />(OPML, Json ou Zip)',
 	'file_to_import_no_zip'		=> 'Fichier à importer<br />(OPML ou Json)',
@@ -197,13 +207,15 @@ return array (
 
 	'informations'			=> 'Informations',
 	'damn'				=> 'Arf !',
+	'ok'				=> 'Ok !',
+	'attention'			=> 'Attention !',
 	'feed_in_error'			=> 'Ce flux a rencontré un problème. Veuillez vérifier qu’il est toujours accessible puis actualisez-le.',
 	'feed_empty'			=> 'Ce flux est vide. Veuillez vérifier qu’il est toujours maintenu.',
 	'feed_description'		=> 'Description',
 	'website_url'			=> 'URL du site',
 	'feed_url'			=> 'URL du flux',
 	'articles'			=> 'articles',
-	'number_articles'		=> 'Nombre d’articles',
+	'number_articles'		=> '%d articles',
 	'by_feed'			=> 'par flux',
 	'by_default'			=> 'Par défaut',
 	'keep_history'			=> 'Nombre minimum d’articles à conserver',
@@ -225,7 +237,7 @@ return array (
 	'not_yet_implemented'		=> 'Pas encore implémenté',
 	'access_protected_feeds'	=> 'La connexion permet d’accéder aux flux protégés par une authentification HTTP.',
 	'no_selected_feed'		=> 'Aucun flux sélectionné.',
-	'think_to_add'			=> '<a href="./?c=configure&amp;a=feed">Vous pouvez ajouter des flux</a>.',
+	'think_to_add'			=> 'Vous pouvez ajouter des flux.',
 
 	'current_user'			=> 'Utilisateur actuel',
 	'password_form'			=> 'Mot de passe<br /><small>(pour connexion par formulaire)</small>',
@@ -237,7 +249,7 @@ return array (
 	'unsafe_autologin'		=> 'Autoriser les connexions automatiques non-sûres au format : ',
 	'api_enabled'			=> 'Autoriser l’accès par <abbr>API</abbr> <small>(nécessaire pour les applis mobiles)</small>',
 	'auth_token'			=> 'Jeton d’identification',
-	'explain_token'			=> 'Permet d’accéder à la sortie RSS de l’utilisateur par défaut sans besoin de s’authentifier.<br /><kbd>%s?output=rss&token=%s</kbd>',
+	'explain_token'			=> 'Permet d’accéder à la sortie RSS de l’utilisateur par défaut sans besoin de s’authentifier.<br /><kbd>%s?output=rss&amp;token=%s</kbd>',
 	'login_configuration'		=> 'Identification',
 	'is_admin'			=> 'est administrateur',
 	'auth_type'			=> 'Méthode d’authentification',
@@ -248,6 +260,7 @@ return array (
 	'users_list'			=> 'Liste des utilisateurs',
 	'create_user'			=> 'Créer un nouvel utilisateur',
 	'username'			=> 'Nom d’utilisateur',
+	'username_admin'		=> 'Nom d’utilisateur administrateur',
 	'password'			=> 'Mot de passe',
 	'create'			=> 'Créer',
 	'user_created'			=> 'L’utilisateur %s a été créé.',
@@ -263,17 +276,21 @@ return array (
 	'reading_configuration'		=> 'Lecture',
 	'display_configuration'		=> 'Affichage',
 	'articles_per_page'		=> 'Nombre d’articles par page',
+	'number_divided_when_reader'	=> 'Divisé par 2 dans la vue de lecture.',
 	'default_view'			=> 'Vue par défaut',
+	'articles_to_display'		=> 'Articles à afficher',
 	'sort_order'			=> 'Ordre de tri',
 	'auto_load_more'		=> 'Charger les articles suivants en bas de page',
 	'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 (ne fonctionne pas avec la configuration “Afficher tous les articles”)',
 	'after_onread'			=> 'Après “marquer tout comme lu”,',
 	'jump_next'			=> 'sauter au prochain voisin non lu (flux ou catégorie)',
 	'article_icons'			=> 'Icônes d’article',
 	'top_line'			=> 'Ligne du haut',
 	'bottom_line'			=> 'Ligne du bas',
+	'html5_notif_timeout'		=> 'Temps d’affichage de la notification HTML5',
+	'seconds_(0_means_no_timeout)'	=> 'secondes (0 signifie aucun timeout ) ',
 	'img_with_lazyload'		=> 'Utiliser le mode “chargement différé” pour les images',
 	'sticky_post'			=> 'Aligner l’article en haut quand il est ouvert',
 	'reading_confirm'		=> 'Afficher une confirmation lors des actions “marquer tout comme lu”',
@@ -351,6 +368,7 @@ return array (
 	'login_required'		=> 'Accès protégé par mot de passe :',
 
 	'confirm_action'		=> 'Êtes-vous sûr(e) de vouloir continuer ? Cette action ne peut être annulée !',
+	'confirm_action_feed_cat'	=> 'Êtes-vous sûr(e) de vouloir continuer ? Vous pourriez perdre les favoris et les filtres associés. Cette action ne peut être annulée !',
 	'notif_title_new_articles'	=> 'FreshRSS : nouveaux articles !',
 	'notif_body_new_articles'	=> 'Il y a \d nouveaux articles à lire sur FreshRSS.',
 
@@ -414,4 +432,23 @@ return array (
 	'stats_entry_per_category'	=> 'Articles par catégorie',
 	'stats_top_feed'		=> 'Les dix plus gros flux',
 	'stats_entry_count'		=> 'Nombre d’articles',
+	'stats_no_idle'			=> 'Il n’y a aucun flux inactif !',
+
+	'update'			=> 'Mise à jour',
+	'update_system'			=> 'Système de mise à jour',
+	'update_check'			=> 'Vérifier les mises à jour',
+	'update_last'			=> 'Dernière vérification : %s',
+	'update_can_apply'		=> 'Une mise à jour est disponible.',
+	'update_apply'			=> 'Appliquer la mise à jour',
+	'update_server_not_found'	=> 'Le serveur de mise à jour n’a pas été trouvé. [%s]',
+	'no_update'			=> 'Aucune mise à jour à appliquer',
+	'update_problem'		=> 'La mise à jour a rencontré un problème : %s',
+	'update_finished'		=> 'La mise à jour est terminée !',
+
+	'auth_reset'			=> 'Réinitialisation de l’authentification',
+	'auth_will_reset'		=> 'Le système d’authentification va être réinitialisé : un formulaire sera utilisé à la place de Persona.',
+	'auth_not_persona'		=> 'Seul le système d’authentification Persona peut être réinitialisé.',
+	'auth_no_password_set'		=> 'Aucun mot de passe administrateur n’a été précisé. Cette fonctionnalité n’est pas disponible.',
+	'auth_form_set'			=> 'Le formulaire est désormais votre système d’authentification.',
+	'auth_form_not_set'		=> 'Un problème est survenu lors de la configuration de votre système d’authentification. Veuillez réessayer plus tard.',
 );

+ 2 - 0
app/i18n/install.en.php

@@ -42,6 +42,8 @@ return array (
 	'data_is_ok'			=> 'Permissions on data directory are good',
 	'persona_is_ok'			=> 'Permissions on Mozilla Persona directory are good',
 	'file_is_nok'			=> 'Check permissions on <em>%s</em> directory. HTTP server must have rights to write into',
+	'http_referer_is_ok'		=> 'Your HTTP REFERER is known and corresponds to your server.',
+	'http_referer_is_nok'		=> 'Please check that you are not altering your HTTP REFERER.',
 	'fix_errors_before'		=> 'Fix errors before skip to the next step.',
 
 	'general_conf_is_ok'		=> 'General configuration has been saved.',

+ 2 - 0
app/i18n/install.fr.php

@@ -42,6 +42,8 @@ return array (
 	'data_is_ok'			=> 'Les droits sur le répertoire de data sont bons',
 	'persona_is_ok'			=> 'Les droits sur le répertoire de Mozilla Persona sont bons',
 	'file_is_nok'			=> 'Veuillez vérifier les droits sur le répertoire <em>%s</em>. Le serveur HTTP doit être capable d’écrire dedans',
+	'http_referer_is_ok'		=> 'Le HTTP REFERER est connu et semble correspondre à votre serveur.',
+	'http_referer_is_nok'		=> 'Veuillez vérifier que vous ne modifiez pas votre HTTP REFERER.',
 	'fix_errors_before'		=> 'Veuillez corriger les erreurs avant de passer à l’étape suivante.',
 
 	'general_conf_is_ok'		=> 'La configuration générale a été enregistrée.',

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 246 - 509
app/install.php


+ 23 - 15
app/layout/aside_configure.phtml

@@ -1,25 +1,33 @@
 <ul class="nav nav-list aside">
-	<li class="nav-header"><?php echo Minz_Translate::t ('configuration'); ?></li>
-	<li class="item<?php echo Minz_Request::actionName () == 'display' ? ' active' : ''; ?>">
-		<a href="<?php echo _url ('configure', 'display'); ?>"><?php echo Minz_Translate::t ('display_configuration'); ?></a>
+	<li class="nav-header"><?php echo _t('configuration'); ?></li>
+	<li class="item<?php echo Minz_Request::actionName() === 'display' ? ' active' : ''; ?>">
+		<a href="<?php echo _url('configure', 'display'); ?>"><?php echo _t('display_configuration'); ?></a>
 	</li>
-	<li class="item<?php echo Minz_Request::actionName () == 'reading' ? ' active' : ''; ?>">
-		<a href="<?php echo _url ('configure', 'reading'); ?>"><?php echo Minz_Translate::t ('reading_configuration'); ?></a>
+	<li class="item<?php echo Minz_Request::actionName() === 'reading' ? ' active' : ''; ?>">
+		<a href="<?php echo _url('configure', 'reading'); ?>"><?php echo _t('reading_configuration'); ?></a>
 	</li>
-	<li class="item<?php echo Minz_Request::actionName () == 'archiving' ? ' active' : ''; ?>">
-		<a href="<?php echo _url ('configure', 'archiving'); ?>"><?php echo Minz_Translate::t ('archiving_configuration'); ?></a>
+	<li class="item<?php echo Minz_Request::actionName() === 'archiving' ? ' active' : ''; ?>">
+		<a href="<?php echo _url('configure', 'archiving'); ?>"><?php echo _t('archiving_configuration'); ?></a>
 	</li>
-	<li class="item<?php echo Minz_Request::actionName () == 'sharing' ? ' active' : ''; ?>">
-		<a href="<?php echo _url ('configure', 'sharing'); ?>"><?php echo Minz_Translate::t ('sharing'); ?></a>
+	<li class="item<?php echo Minz_Request::actionName() === 'sharing' ? ' active' : ''; ?>">
+		<a href="<?php echo _url('configure', 'sharing'); ?>"><?php echo _t('sharing'); ?></a>
 	</li>
-	<li class="item<?php echo Minz_Request::actionName () == 'shortcut' ? ' active' : ''; ?>">
-		<a href="<?php echo _url ('configure', 'shortcut'); ?>"><?php echo Minz_Translate::t ('shortcuts'); ?></a>
+	<li class="item<?php echo Minz_Request::actionName() === 'shortcut' ? ' active' : ''; ?>">
+		<a href="<?php echo _url('configure', 'shortcut'); ?>"><?php echo _t('shortcuts'); ?></a>
 	</li>
-	<li class="item<?php echo Minz_Request::actionName () == 'queries' ? ' active' : ''; ?>">
-		<a href="<?php echo _url ('configure', 'queries'); ?>"><?php echo Minz_Translate::t ('queries'); ?></a>
+	<li class="item<?php echo Minz_Request::actionName() === 'queries' ? ' active' : ''; ?>">
+		<a href="<?php echo _url('configure', 'queries'); ?>"><?php echo _t('queries'); ?></a>
 	</li>
 	<li class="separator"></li>
-	<li class="item<?php echo Minz_Request::actionName () == 'users' ? ' active' : ''; ?>">
-		<a href="<?php echo _url ('configure', 'users'); ?>"><?php echo Minz_Translate::t ('users'); ?></a>
+	<li class="item<?php echo Minz_Request::actionName() === 'users' ? ' active' : ''; ?>">
+		<a href="<?php echo _url('configure', 'users'); ?>"><?php echo _t('users'); ?></a>
 	</li>
+	<?php
+		$current_user = Minz_Session::param('currentUser', '');
+		if (Minz_Configuration::isAdmin($current_user)) {
+	?>
+	<li class="item<?php echo Minz_Request::controllerName() === 'update' ? ' active' : ''; ?>">
+		<a href="<?php echo _url('update', 'index'); ?>"><?php echo _t('update'); ?></a>
+	</li>
+	<?php } ?>
 </ul>

+ 41 - 33
app/layout/aside_flux.phtml

@@ -1,16 +1,18 @@
 <div class="aside aside_flux<?php if ($this->conf->hide_read_feeds && ($this->state & FreshRSS_Entry::STATE_NOT_READ) && !($this->state & FreshRSS_Entry::STATE_READ)) echo ' state_unread'; ?>" id="aside_flux">
-	<a class="toggle_aside" href="#close"><?php echo FreshRSS_Themes::icon('close'); ?></a>
+	<a class="toggle_aside" href="#close"><?php echo _i('close'); ?></a>
 
 	<ul class="categories">
 		<?php if ($this->loginOk) { ?>
+		<form id="mark-read-aside" method="post" style="display: none"></form>
+
 		<li>
-			<div class="stick">
-				<a class="btn btn-important" href="<?php echo _url ('configure', 'feed'); ?>"><?php echo Minz_Translate::t ('subscription_management'); ?></a>
-				<a class="btn btn-important" href="<?php echo _url ('configure', 'categorize'); ?>" title="<?php echo Minz_Translate::t ('categories_management'); ?>"><?php echo FreshRSS_Themes::icon('category-white'); ?></a>
+			<div class="stick configure-feeds">
+				<a class="btn btn-important" href="<?php echo _url('configure', 'feed'); ?>"><?php echo _t('subscription_management'); ?></a>
+				<a class="btn btn-important" href="<?php echo _url('configure', 'categorize'); ?>" title="<?php echo _t('categories_management'); ?>"><?php echo _i('category-white'); ?></a>
 			</div>
 		</li>
 		<?php } elseif (Minz_Configuration::needsLogin()) { ?>
-		<li><a href="<?php echo _url ('index', 'about'); ?>"><?php echo Minz_Translate::t ('about_freshrss'); ?></a></li>
+		<li><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('about_freshrss'); ?></a></li>
 		<?php } ?>
 
 		<?php
@@ -22,8 +24,8 @@
 		<li>
 			<div class="category all<?php echo $this->get_c == 'a' ? ' active' : ''; ?>">
 				<a data-unread="<?php echo formatNumber($this->nb_not_read); ?>" class="btn<?php echo $this->get_c == 'a' ? ' active' : ''; ?>" href="<?php echo Minz_Url::display($arUrl); ?>">
-					<?php echo FreshRSS_Themes::icon('all'); ?>
-					<?php echo Minz_Translate::t ('main_stream'); ?>
+					<?php echo _i('all'); ?>
+					<?php echo _t('main_stream'); ?>
 				</a>
 			</div>
 		</li>
@@ -31,44 +33,42 @@
 		<li>
 			<div class="category favorites<?php echo $this->get_c == 's' ? ' active' : ''; ?>">
 				<a data-unread="<?php echo formatNumber($this->nb_favorites['unread']); ?>" class="btn<?php echo $this->get_c == 's' ? ' active' : ''; ?>" href="<?php $arUrl['params']['get'] = 's'; echo Minz_Url::display($arUrl); ?>">
-					<?php echo FreshRSS_Themes::icon('bookmark'); ?>
-					<?php echo Minz_Translate::t('favorite_feeds', formatNumber($this->nb_favorites['all'])); ?>
+					<?php echo _i('bookmark'); ?>
+					<?php echo _t('favorite_feeds', formatNumber($this->nb_favorites['all'])); ?>
 				</a>
 			</div>
 		</li>
 
 		<?php
 		foreach ($this->cat_aside as $cat) {
-			$feeds = $cat->feeds ();
-			if (!empty ($feeds)) {
+			$feeds = $cat->feeds();
+			if (!empty($feeds)) {
 				$c_active = false;
-				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;
+				$c_show = false;
+				if ($this->get_c == $cat->id()) {
+					$c_active = true;
+					if (!$this->conf->display_categories || $this->get_f) {
+						$c_show = true;
 					}
 				}
 				?><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
-					?><a data-unread="<?php echo formatNumber($cat->nbNotRead()); ?>" class="btn<?php echo $c_active ? ' active' : ''; ?>" href="<?php $arUrl['params']['get'] = 'c_' . $cat->id(); echo Minz_Url::display($arUrl); ?>"><?php echo $cat->name (); ?></a><?php
-					?><a class="btn dropdown-toggle" href="#"><?php echo FreshRSS_Themes::icon($c_active ? 'up' : 'down'); ?></a><?php
+					?><a data-unread="<?php echo formatNumber($cat->nbNotRead()); ?>" class="btn<?php echo $c_active ? ' active' : ''; ?>" href="<?php $arUrl['params']['get'] = 'c_' . $cat->id(); echo Minz_Url::display($arUrl); ?>"><?php echo $cat->name(); ?></a><?php
+					?><a class="btn dropdown-toggle" href="#"><?php echo _i($c_show ? 'up' : 'down'); ?></a><?php
 				?></div><?php
-				?><ul class="feeds<?php echo $c_active ? ' active' : ''; ?>"><?php
+				?><ul class="feeds<?php echo $c_show ? ' active' : ''; ?>"><?php
 				foreach ($feeds as $feed) {
-					$feed_id = $feed->id ();
-					$nbEntries = $feed->nbEntries ();
+					$feed_id = $feed->id();
+					$nbEntries = $feed->nbEntries();
 					$f_active = ($this->get_f == $feed_id);
-					?><li id="f_<?php echo $feed_id; ?>" class="item<?php echo $f_active ? ' active' : ''; ?><?php echo $feed->inError () ? ' error' : ''; ?><?php echo $nbEntries == 0 ? ' empty' : ''; ?>" data-unread="<?php echo $feed->nbNotRead(); ?>"><?php
+					?><li id="f_<?php echo $feed_id; ?>" class="item<?php echo $f_active ? ' active' : ''; ?><?php echo $feed->inError() ? ' error' : ''; ?><?php echo $nbEntries == 0 ? ' empty' : ''; ?>" data-unread="<?php echo $feed->nbNotRead(); ?>"><?php
 						?><div class="dropdown"><?php
 							?><div class="dropdown-target"></div><?php
-							?><a class="dropdown-toggle" data-fweb="<?php echo $feed->website (); ?>"><?php echo FreshRSS_Themes::icon('configure'); ?></a><?php
+							?><a class="dropdown-toggle" data-fweb="<?php echo $feed->website(); ?>"><?php echo _i('configure'); ?></a><?php
 							/* feed_config_template */
 						?></div><?php
-						?> <img class="favicon" src="<?php echo $feed->favicon (); ?>" alt="✇" /> <?php
-						?><a class="feed" data-unread="<?php echo formatNumber($feed->nbNotRead()); ?>" data-priority="<?php echo $feed->priority (); ?>" href="<?php $arUrl['params']['get'] = 'f_' . $feed_id; echo Minz_Url::display($arUrl); ?>"><?php echo $feed->name(); ?></a><?php
+						?> <img class="favicon" src="<?php echo $feed->favicon(); ?>" alt="✇" /> <?php
+						?><a class="feed" data-unread="<?php echo formatNumber($feed->nbNotRead()); ?>" data-priority="<?php echo $feed->priority(); ?>" href="<?php $arUrl['params']['get'] = 'f_' . $feed_id; echo Minz_Url::display($arUrl); ?>"><?php echo $feed->name(); ?></a><?php
 					?></li><?php
 				}
 				?></ul><?php
@@ -82,14 +82,22 @@
 <script id="feed_config_template" type="text/html">
 	<ul class="dropdown-menu">
 		<li class="dropdown-close"><a href="#close">❌</a></li>
-		<li class="item"><a href="<?php echo _url ('index', 'index', 'get', 'f_!!!!!!'); ?>"><?php echo Minz_Translate::t ('filter'); ?></a></li>
-		<li class="item"><a href="<?php echo _url ('stats', 'repartition', 'id', '!!!!!!'); ?>"><?php echo Minz_Translate::t ('stats'); ?></a></li>
-		<li class="item"><a target="_blank" href="http://example.net/"><?php echo Minz_Translate::t ('see_website'); ?></a></li>
+		<li class="item"><a href="<?php echo _url('index', 'index', 'get', 'f_!!!!!!'); ?>"><?php echo _t('filter'); ?></a></li>
+		<?php if ($this->loginOk) { ?>
+		<li class="item"><a href="<?php echo _url('stats', 'repartition', 'id', '!!!!!!'); ?>"><?php echo _t('stats'); ?></a></li>
+		<?php } ?>
+		<li class="item"><a target="_blank" href="http://example.net/"><?php echo _t('see_website'); ?></a></li>
 		<?php if ($this->loginOk) { ?>
 		<li class="separator"></li>
-		<li class="item"><a href="<?php echo _url ('configure', 'feed', 'id', '!!!!!!'); ?>"><?php echo Minz_Translate::t ('administration'); ?></a></li>
-		<li class="item"><a href="<?php echo _url ('feed', 'actualize', 'id', '!!!!!!'); ?>"><?php echo Minz_Translate::t ('actualize'); ?></a></li>
-		<li class="item"><a href="<?php echo _url ('entry', 'read', 'get', 'f_!!!!!!'); ?>"><?php echo Minz_Translate::t ('mark_read'); ?></a></li>
+		<li class="item"><a href="<?php echo _url('configure', 'feed', 'id', '!!!!!!'); ?>"><?php echo _t('administration'); ?></a></li>
+		<li class="item"><a href="<?php echo _url('feed', 'actualize', 'id', '!!!!!!'); ?>"><?php echo _t('actualize'); ?></a></li>
+		<li class="item">
+			<?php $confirm = $this->conf->reading_confirm ? 'confirm' : ''; ?>
+			<button class="read_all as-link <?php echo $confirm; ?>"
+			        form="mark-read-aside"
+			        formaction="<?php echo _url('entry', 'read', 'get', 'f_!!!!!!'); ?>"
+			        type="submit"><?php echo _t('mark_read'); ?></button>
+		</li>
 		<?php } ?>
 	</ul>
 </script>

+ 6 - 0
app/layout/header.phtml

@@ -75,6 +75,12 @@ if (Minz_Configuration::canLogIn()) {
 				<li class="item"><a href="<?php echo _url('configure', 'queries'); ?>"><?php echo _t('queries'); ?></a></li>
 				<li class="separator"></li>
 				<li class="item"><a href="<?php echo _url('configure', 'users'); ?>"><?php echo _t('users'); ?></a></li>
+				<?php
+					$current_user = Minz_Session::param('currentUser', '');
+					if (Minz_Configuration::isAdmin($current_user)) {
+				?>
+				<li class="item"><a href="<?php echo _url('update', 'index'); ?>"><?php echo _t('update'); ?></a></li>
+				<?php } ?>
 				<li class="separator"></li>
 				<li class="item"><a href="<?php echo _url('stats', 'index'); ?>"><?php echo _t('stats'); ?></a></li>
 				<li class="item"><a href="<?php echo _url('index', 'logs'); ?>"><?php echo _t('logs'); ?></a></li>

+ 1 - 0
app/layout/layout.phtml

@@ -13,6 +13,7 @@
 	if (!empty($this->nextId)) {
 		$params = Minz_Request::params();
 		$params['next'] = $this->nextId;
+		$params['ajax'] = 1;
 ?>
 		<link id="prefetch" rel="next prefetch" href="<?php echo Minz_Url::display(array('c' => Minz_Request::controllerName(), 'a' => Minz_Request::actionName(), 'params' => $params)); ?>" />
 <?php } ?>

+ 101 - 74
app/layout/nav_menu.phtml

@@ -3,11 +3,11 @@
 ?>
 <div class="nav_menu">
 	<?php if ($actual_view === 'normal') { ?>
-	<a class="btn toggle_aside" href="#aside_flux"><?php echo FreshRSS_Themes::icon('category'); ?></a>
+	<a class="btn toggle_aside" href="#aside_flux"><?php echo _i('category'); ?></a>
 	<?php } ?>
 
 	<?php if ($this->loginOk) { ?>
-	<div class="stick">
+	<div id="nav_menu_actions" class="stick">
 		<?php
 			$url_state = $this->url;
 
@@ -24,9 +24,9 @@
 		<a id="toggle-read"
 		   class="btn <?php echo $class; ?>"
 		   aria-checked="<?php echo $checked; ?>"
-		   href="<?php echo Minz_Url::display ($url_state); ?>"
-		   title="<?php echo Minz_Translate::t ('show_read'); ?>">
-			<?php echo FreshRSS_Themes::icon('read'); ?>
+		   href="<?php echo Minz_Url::display($url_state); ?>"
+		   title="<?php echo _t('show_read'); ?>">
+			<?php echo _i('read'); ?>
 		</a>
 
 		<?php
@@ -43,13 +43,13 @@
 		<a id="toggle-unread"
 		   class="btn <?php echo $class; ?>"
 		   aria-checked="<?php echo $checked; ?>"
-		   href="<?php echo Minz_Url::display ($url_state); ?>"
-		   title="<?php echo Minz_Translate::t ('show_not_reads'); ?>">
-			<?php echo FreshRSS_Themes::icon('unread'); ?>
+		   href="<?php echo Minz_Url::display($url_state); ?>"
+		   title="<?php echo _t('show_not_reads'); ?>">
+			<?php echo _i('unread'); ?>
 		</a>
 
 		<?php
-			if ($this->state & FreshRSS_Entry::STATE_FAVORITE) {
+			if ($this->state & FreshRSS_Entry::STATE_FAVORITE || $this->get_c == 's') {
 				$url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_FAVORITE;
 				$checked = 'true';
 				$class = 'active';
@@ -62,9 +62,9 @@
 		<a id="toggle-favorite"
 		   class="btn <?php echo $class; ?>"
 		   aria-checked="<?php echo $checked; ?>"
-		   href="<?php echo Minz_Url::display ($url_state); ?>"
-		   title="<?php echo Minz_Translate::t ('show_favorite'); ?>">
-			<?php echo FreshRSS_Themes::icon('starred'); ?>
+		   href="<?php echo Minz_Url::display($url_state); ?>"
+		   title="<?php echo _t('show_favorite'); ?>">
+			<?php echo _i('starred'); ?>
 		</a>
 
 		<?php
@@ -81,22 +81,25 @@
 		<a id="toggle-not-favorite"
 		   class="btn <?php echo $class; ?>"
 		   aria-checked="<?php echo $checked; ?>"
-		   href="<?php echo Minz_Url::display ($url_state); ?>"
-		   title="<?php echo Minz_Translate::t ('show_not_favorite'); ?>">
-			<?php echo FreshRSS_Themes::icon('non-starred'); ?>
+		   href="<?php echo Minz_Url::display($url_state); ?>"
+		   title="<?php echo _t('show_not_favorite'); ?>">
+			<?php echo _i('non-starred'); ?>
 		</a>
 
 		<div class="dropdown">
 			<div id="dropdown-query" class="dropdown-target"></div>
 
-			<a class="dropdown-toggle btn" href="#dropdown-query"><?php echo FreshRSS_Themes::icon('down'); ?></a>
+			<a class="dropdown-toggle btn" href="#dropdown-query"><?php echo _i('down'); ?></a>
 			<ul class="dropdown-menu">
 				<li class="dropdown-close"><a href="#close">❌</a></li>
 
-				<li class="dropdown-header"><?php echo Minz_Translate::t('queries'); ?> <a class="no-mobile" href="<?php echo _url('configure', 'queries'); ?>"><?php echo FreshRSS_Themes::icon('configure'); ?></a></li>
+				<li class="dropdown-header">
+					<?php echo _t('queries'); ?>
+					<a class="no-mobile" href="<?php echo _url('configure', 'queries'); ?>"><?php echo _i('configure'); ?></a>
+				</li>
 
 				<?php foreach ($this->conf->queries as $query) { ?>
-				<li class="item">
+				<li class="item query">
 					<a href="<?php echo $query['url']; ?>"><?php echo $query['name']; ?></a>
 				</li>
 				<?php } ?>
@@ -110,58 +113,58 @@
 					$url_query['c'] = 'configure';
 					$url_query['a'] = 'addQuery';
 				?>
-				<li class="item no-mobile"><a href="<?php echo Minz_Url::display($url_query); ?>"><?php echo FreshRSS_Themes::icon('bookmark-add'); ?> <?php echo Minz_Translate::t('add_query'); ?></a></li>
+				<li class="item no-mobile"><a href="<?php echo Minz_Url::display($url_query); ?>"><?php echo _i('bookmark-add'); ?> <?php echo _t('add_query'); ?></a></li>
 			</ul>
 		</div>
 	</div>
 	<?php
 		$get = false;
-		$string_mark = Minz_Translate::t ('mark_all_read');
+		$string_mark = _t('mark_all_read');
 		if ($this->get_f) {
 			$get = 'f_' . $this->get_f;
-			$string_mark = Minz_Translate::t ('mark_feed_read');
+			$string_mark = _t('mark_feed_read');
 		} elseif ($this->get_c && $this->get_c != 'a') {
 			if ($this->get_c === 's') {
 				$get = 's';
 			} else {
 				$get = 'c_' . $this->get_c;
 			}
-			$string_mark = Minz_Translate::t ('mark_cat_read');
+			$string_mark = _t('mark_cat_read');
 		}
 		$nextGet = $get;
-		if ($this->conf->onread_jump_next && (strlen ($get) > 2)) {
+		if ($this->conf->onread_jump_next && strlen($get) > 2) {
 			$anotherUnreadId = '';
 			$foundCurrent = false;
 			switch ($get[0]) {
-				case 'c':
-					foreach ($this->cat_aside as $cat) {
-						if ($cat->id () == $this->get_c) {
-							$foundCurrent = true;
-							continue;
-						}
-						if ($cat->nbNotRead () <= 0) continue;
-						$anotherUnreadId = $cat->id ();
-						if ($foundCurrent) break;
+			case 'c':
+				foreach ($this->cat_aside as $cat) {
+					if ($cat->id() == $this->get_c) {
+						$foundCurrent = true;
+						continue;
 					}
-					$nextGet = empty ($anotherUnreadId) ? 'a' : 'c_' . $anotherUnreadId;
-					break;
-				case 'f':
-					foreach ($this->cat_aside as $cat) {
-						if ($cat->id () == $this->get_c) {
-							foreach ($cat->feeds () as $feed) {
-								if ($feed->id () == $this->get_f) {
-									$foundCurrent = true;
-									continue;
-								}
-								if ($feed->nbNotRead () <= 0) continue;
-								$anotherUnreadId = $feed->id ();
-								if ($foundCurrent) break;
+					if ($cat->nbNotRead() <= 0) continue;
+					$anotherUnreadId = $cat->id();
+					if ($foundCurrent) break;
+				}
+				$nextGet = empty($anotherUnreadId) ? 'a' : 'c_' . $anotherUnreadId;
+				break;
+			case 'f':
+				foreach ($this->cat_aside as $cat) {
+					if ($cat->id() == $this->get_c) {
+						foreach ($cat->feeds() as $feed) {
+							if ($feed->id() == $this->get_f) {
+								$foundCurrent = true;
+								continue;
 							}
-							break;
+							if ($feed->nbNotRead() <= 0) continue;
+							$anotherUnreadId = $feed->id();
+							if ($foundCurrent) break;
 						}
+						break;
 					}
-					$nextGet = empty ($anotherUnreadId) ? 'c_' . $this->get_c : 'f_' . $anotherUnreadId;
-					break;
+				}
+				$nextGet = empty($anotherUnreadId) ? 'c_' . $this->get_c : 'f_' . $anotherUnreadId;
+				break;
 			}
 		}
 
@@ -177,30 +180,54 @@
 
 		$arUrl = array('c' => 'entry', 'a' => 'read', 'params' => array('get' => $get, 'nextGet' => $nextGet, 'idMax' => $idMax));
 		$output = Minz_Request::param('output', '');
-		if (($output != '') && ($this->conf->view_mode !== $output)) {
+		if ($output != '' && $this->conf->view_mode !== $output) {
 			$arUrl['params']['output'] = $output;
 		}
 		$markReadUrl = Minz_Url::display($arUrl);
-		Minz_Session::_param ('markReadUrl', $markReadUrl);
+		Minz_Session::_param('markReadUrl', $markReadUrl);
 	?>
 
+	<form id="mark-read-menu" method="post" style="display: none"></form>
+
 	<div class="stick" id="nav_menu_read_all">
-		<a class="read_all btn<?php if ($this->conf->reading_confirm) {echo ' confirm';} ?>" href="<?php echo $markReadUrl; ?>"><?php echo Minz_Translate::t ('mark_read'); ?></a>
+		<?php $confirm = $this->conf->reading_confirm ? 'confirm' : ''; ?>
+		<button class="read_all btn <?php echo $confirm; ?>"
+		        form="mark-read-menu"
+		        formaction="<?php echo $markReadUrl; ?>"
+		        type="submit"><?php echo _t('mark_read'); ?></button>
+
 		<div class="dropdown">
 			<div id="dropdown-read" class="dropdown-target"></div>
 
-			<a class="dropdown-toggle btn" href="#dropdown-read"><?php echo FreshRSS_Themes::icon('down'); ?></a>
+			<a class="dropdown-toggle btn" href="#dropdown-read"><?php echo _i('down'); ?></a>
 			<ul class="dropdown-menu">
 				<li class="dropdown-close"><a href="#close">❌</a></li>
 
-				<li class="item"><a href="<?php echo $markReadUrl; ?>"><?php echo $string_mark; ?></a></li>
+				<li class="item">
+					<button class="as-link <?php echo $confirm; ?>"
+					        form="mark-read-menu"
+					        formaction="<?php echo $markReadUrl; ?>"
+					        type="submit"><?php echo $string_mark; ?></button>
+				</li>
 				<li class="separator"></li>
 <?php
-	$today = $this->today;
-	$one_week = $today - 604800;
+	$mark_before_today = $arUrl;
+	$mark_before_today['params']['idMax'] = $this->today . '000000';
+	$mark_before_one_week = $arUrl;
+	$mark_before_one_week['params']['idMax'] = ($this->today - 604800) . '000000';
 ?>
-				<li class="item"><a href="<?php echo _url ('entry', 'read', 'is_read', 1, 'get', $get, 'idMax', $today . '000000'); ?>"><?php echo Minz_Translate::t ('before_one_day'); ?></a></li>
-				<li class="item"><a href="<?php echo _url ('entry', 'read', 'is_read', 1, 'get', $get, 'idMax', $one_week . '000000'); ?>"><?php echo Minz_Translate::t ('before_one_week'); ?></a></li>
+				<li class="item">
+					<button class="as-link <?php echo $confirm; ?>"
+					        form="mark-read-menu"
+					        formaction="<?php echo Minz_Url::display($mark_before_today); ?>"
+					        type="submit"><?php echo _t('before_one_day'); ?></button>
+				</li>
+				<li class="item">
+					<button class="as-link <?php echo $confirm; ?>"
+					        form="mark-read-menu"
+					        formaction="<?php echo Minz_Url::display($mark_before_one_week); ?>"
+					        type="submit"><?php echo _t('before_one_week'); ?></button>
+				</li>
 			</ul>
 		</div>
 	</div>
@@ -209,18 +236,18 @@
 	<?php $url_output = $this->url; ?>
 	<div class="stick" id="nav_menu_views">
 		<?php $url_output['params']['output'] = 'normal'; ?>
-		<a class="view_normal btn <?php echo $actual_view == 'normal'? 'active' : ''; ?>" title="<?php echo Minz_Translate::t('normal_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>">
-			<?php echo FreshRSS_Themes::icon("view-normal"); ?>
+		<a class="view_normal btn <?php echo $actual_view == 'normal'? 'active' : ''; ?>" title="<?php echo _t('normal_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>">
+			<?php echo _i("view-normal"); ?>
 		</a>
 
 		<?php $url_output['params']['output'] = 'global'; ?>
-		<a class="view_global btn <?php echo $actual_view == 'global'? 'active' : ''; ?>" title="<?php echo Minz_Translate::t('global_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>">
-			<?php echo FreshRSS_Themes::icon("view-global"); ?>
+		<a class="view_global btn <?php echo $actual_view == 'global'? 'active' : ''; ?>" title="<?php echo _t('global_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>">
+			<?php echo _i("view-global"); ?>
 		</a>
 
 		<?php $url_output['params']['output'] = 'reader'; ?>
-		<a class="view_reader btn <?php echo $actual_view == 'reader'? 'active' : ''; ?>" title="<?php echo Minz_Translate::t('reader_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>">
-			<?php echo FreshRSS_Themes::icon("view-reader"); ?>
+		<a class="view_reader btn <?php echo $actual_view == 'reader'? 'active' : ''; ?>" title="<?php echo _t('reader_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>">
+			<?php echo _i("view-reader"); ?>
 		</a>
 
 		<?php
@@ -229,27 +256,27 @@
 				$url_output['params']['token'] = $this->conf->token;
 			}
 		?>
-		<a class="view_rss btn" target="_blank" title="<?php echo Minz_Translate::t ('rss_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>">
-			<?php echo FreshRSS_Themes::icon('rss'); ?>
+		<a class="view_rss btn" target="_blank" title="<?php echo _t('rss_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>">
+			<?php echo _i('rss'); ?>
 		</a>
 	</div>
 
 	<div class="item search">
-		<form action="<?php echo _url ('index', 'index'); ?>" method="get">
-			<?php $search = Minz_Request::param ('search', ''); ?>
-			<input type="search" name="search" class="extend" value="<?php echo $search; ?>" placeholder="<?php echo Minz_Translate::t ('search_short'); ?>" />
+		<form action="<?php echo _url('index', 'index'); ?>" method="get">
+			<?php $search = Minz_Request::param('search', ''); ?>
+			<input type="search" name="search" class="extend" value="<?php echo $search; ?>" placeholder="<?php echo _t('search_short'); ?>" />
 
-			<?php $get = Minz_Request::param ('get', ''); ?>
+			<?php $get = Minz_Request::param('get', ''); ?>
 			<?php if($get != '') { ?>
 			<input type="hidden" name="get" value="<?php echo $get; ?>" />
 			<?php } ?>
 
-			<?php $order = Minz_Request::param ('order', ''); ?>
+			<?php $order = Minz_Request::param('order', ''); ?>
 			<?php if($order != '') { ?>
 			<input type="hidden" name="order" value="<?php echo $order; ?>" />
 			<?php } ?>
 
-			<?php $state = Minz_Request::param ('state', ''); ?>
+			<?php $state = Minz_Request::param('state', ''); ?>
 			<?php if($state != '') { ?>
 			<input type="hidden" name="state" value="<?php echo $state; ?>" />
 			<?php } ?>
@@ -269,11 +296,11 @@
 		$url_order = $this->url;
 		$url_order['params']['order'] = $order;
 	?>
-	<a class="btn" href="<?php echo Minz_Url::display ($url_order); ?>" title="<?php echo Minz_Translate::t ($title); ?>">
-		<?php echo FreshRSS_Themes::icon($icon); ?>
+	<a id="toggle-order" class="btn" href="<?php echo Minz_Url::display($url_order); ?>" title="<?php echo _t($title); ?>">
+		<?php echo _i($icon); ?>
 	</a>
 	
 	<?php if ($this->loginOk || Minz_Configuration::allowAnonymousRefresh()) { ?>
-	<a id="actualize" class="btn" href="<?php echo _url ('feed', 'actualize'); ?>"><?php echo FreshRSS_Themes::icon('refresh'); ?></a>
+	<a id="actualize" class="btn" href="<?php echo _url('feed', 'actualize'); ?>"><?php echo _i('refresh'); ?></a>
 	<?php } ?>
 </div>

+ 7 - 1
app/views/configure/categorize.phtml

@@ -18,7 +18,13 @@
 					<input type="text" id="cat_<?php echo $cat->id (); ?>" name="categories[]" value="<?php echo $cat->name (); ?>" />
 
 					<?php if ($cat->nbFeed () > 0) { ?>
-					<button type="submit" class="btn btn-attention confirm" formaction="<?php echo _url ('feed', 'delete', 'id', $cat->id (), 'type', 'category'); ?>"><?php echo Minz_Translate::t ('ask_empty'); ?></button>
+					<a class="btn" href="<?php echo _url('index', 'index', 'get', 'c_' . $cat->id ()); ?>">
+						<?php echo _i('link'); ?>
+					</a>
+					<button formaction="<?php echo _url('feed', 'delete', 'id', $cat->id (), 'type', 'category'); ?>"
+					        class="btn btn-attention confirm"
+					        data-str-confirm="<?php echo _t('confirm_action_feed_cat'); ?>"
+					        type="submit"><?php echo _t('ask_empty'); ?></button>
 					<?php } ?>
 				</div>
 				(<?php echo Minz_Translate::t ('number_feeds', $cat->nbFeed ()); ?>)

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

@@ -91,6 +91,13 @@
 				</tbody>
 			</table><br />
 		</div>
+		
+		<div class="form-group">
+			<label class="group-name" for="posts_per_page"><?php echo Minz_Translate::t ('html5_notif_timeout'); ?></label>
+			<div class="group-controls">
+				<input type="number" id="html5_notif_timeout" name="html5_notif_timeout" value="<?php echo $this->conf->html5_notif_timeout; ?>" /> <?php echo Minz_Translate::t ('seconds_(0_means_no_timeout)'); ?>
+			</div>
+		</div>
 
 		<div class="form-group form-actions">
 			<div class="group-controls">

+ 18 - 12
app/views/configure/feed.phtml

@@ -70,27 +70,33 @@
 				</label>
 			</div>
 		</div>
+		<div class="form-group">
+			<div class="group-controls">
+				<a href="<?php echo _url('stats', 'repartition', 'id', $this->flux->id()); ?>">
+					<?php echo _i('stats'); ?> <?php echo _t('stats'); ?>
+				</a>
+			</div>
+		</div>
 		<div class="form-group form-actions">
 			<div class="group-controls">
-				<button class="btn btn-important"><?php echo Minz_Translate::t ('save'); ?></button>
-				<button class="btn btn-attention confirm" formmethod="post" formaction="<?php echo Minz_Url::display (array ('c' => 'feed', 'a' => 'delete', 'params' => array ('id' => $this->flux->id ()))); ?>"><?php echo Minz_Translate::t ('delete'); ?></button>
+				<button class="btn btn-important"><?php echo _t('save'); ?></button>
+				<button class="btn btn-attention confirm"
+				        data-str-confirm="<?php echo _t('confirm_action_feed_cat'); ?>"
+				        formaction="<?php echo _url('feed', 'delete', 'id', $this->flux->id ()); ?>"
+				        formmethod="post"><?php echo _t('delete'); ?></button>
 			</div>
 		</div>
 
 		<legend><?php echo Minz_Translate::t ('archiving_configuration'); ?></legend>
 
 		<div class="form-group">
-			<label class="group-name"></label>
 			<div class="group-controls">
-				<a class="btn" href="<?php echo _url ('feed', 'actualize', 'id', $this->flux->id ()); ?>">
-					<?php echo FreshRSS_Themes::icon('refresh'); ?> <?php echo Minz_Translate::t('actualize'); ?>
-				</a>
-			</div>
-		</div>
-		<div class="form-group">
-			<label class="group-name"><?php echo Minz_Translate::t ('number_articles'); ?></label>
-			<div class="group-controls">
-				<span class="control"><?php echo $nbEntries; ?></span>
+				<div class="stick">
+					<input type="text" value="<?php echo _t('number_articles', $nbEntries); ?>" disabled="disabled" />
+					<a class="btn" href="<?php echo _url('feed', 'actualize', 'id', $this->flux->id ()); ?>">
+						<?php echo _i('refresh'); ?> <?php echo _t('actualize'); ?>
+					</a>
+				</div>
 			</div>
 		</div>
 		<div class="form-group">

+ 11 - 4
app/views/configure/queries.phtml

@@ -42,30 +42,37 @@
 						   + (isset($query['get']) ? 1 : 0);
 					// If the only filter is "all" articles, we consider there is no filter
 					$exist = ($exist === 1 && isset($query['get']) && $query['get'] === 'a') ? 0 : $exist;
+
+					$deprecated = (isset($this->query_get[$key]) &&
+					               $this->query_get[$key]['deprecated']);
 				?>
 
 				<?php if ($exist === 0) { ?>
 				<div class="alert alert-warn">
 					<div class="alert-head"><?php echo _t('no_query_filter'); ?></div>
 				</div>
+				<?php } elseif ($deprecated) { ?>
+				<div class="alert alert-error">
+					<div class="alert-head"><?php echo _t('query_deprecated'); ?></div>
+				</div>
 				<?php } else { ?>
 				<div class="alert alert-success">
 					<div class="alert-head"><?php echo _t('query_filter'); ?></div>
 
 					<ul>
-						<?php if (isset($query['search'])) { $exist = true; ?>
+						<?php if (isset($query['search'])) { ?>
 						<li class="item"><?php echo _t('query_search', $query['search']); ?></li>
 						<?php } ?>
 
-						<?php if (isset($query['state'])) { $exist = true; ?>
+						<?php if (isset($query['state'])) { ?>
 						<li class="item"><?php echo _t('query_state_' . $query['state']); ?></li>
 						<?php } ?>
 
-						<?php if (isset($query['order'])) { $exist = true; ?>
+						<?php if (isset($query['order'])) { ?>
 						<li class="item"><?php echo _t('query_order_' . strtolower($query['order'])); ?></li>
 						<?php } ?>
 
-						<?php if (isset($query['get'])) { $exist = true; ?>
+						<?php if (isset($query['get'])) { ?>
 						<li class="item"><?php echo _t('query_get_' . $this->query_get[$key]['type'], $this->query_get[$key]['name']); ?></li>
 						<?php } ?>
 					</ul>

+ 12 - 8
app/views/configure/reading.phtml

@@ -10,6 +10,7 @@
 			<label class="group-name" for="posts_per_page"><?php echo Minz_Translate::t ('articles_per_page'); ?></label>
 			<div class="group-controls">
 				<input type="number" id="posts_per_page" name="posts_per_page" value="<?php echo $this->conf->posts_per_page; ?>" min="5" max="50" />
+				<?php echo _i('help'); ?> <?php echo _t('number_divided_when_reader'); ?>
 			</div>
 		</div>
 
@@ -31,14 +32,17 @@
 					<option value="reader"<?php echo $this->conf->view_mode === 'reader' ? ' selected="selected"' : ''; ?>><?php echo Minz_Translate::t ('reader_view'); ?></option>
 					<option value="global"<?php echo $this->conf->view_mode === 'global' ? ' selected="selected"' : ''; ?>><?php echo Minz_Translate::t ('global_view'); ?></option>
 				</select>
-				<label class="radio" for="radio_all">
-					<input type="radio" name="default_view" id="radio_all" value="<?php echo FreshRSS_Entry::STATE_ALL; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_ALL ? ' checked="checked"' : ''; ?> />
-					<?php echo Minz_Translate::t ('show_all_articles'); ?>
-				</label>
-				<label class="radio" for="radio_not_read">
-					<input type="radio" name="default_view" id="radio_not_read" value="<?php echo FreshRSS_Entry::STATE_NOT_READ; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_NOT_READ ? ' checked="checked"' : ''; ?> />
-					<?php echo Minz_Translate::t ('show_not_reads'); ?>
-				</label>
+			</div>
+		</div>
+
+		<div class="form-group">
+			<label class="group-name" for="view_mode"><?php echo _t('articles_to_display'); ?></label>
+			<div class="group-controls">
+				<select name="default_view" id="default_view">
+					<option value="<?php echo FreshRSS_Entry::STATE_NOT_READ; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_NOT_READ ? ' selected="selected"' : ''; ?>><?php echo _t('show_adaptive'); ?></option>
+					<option value="<?php echo FreshRSS_Entry::STATE_ALL; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_ALL ? ' selected="selected"' : ''; ?>><?php echo _t('show_all_articles'); ?></option>
+					<option value="<?php echo FreshRSS_Entry::STATE_STRICT + FreshRSS_Entry::STATE_NOT_READ; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_STRICT + FreshRSS_Entry::STATE_NOT_READ ? ' selected="selected"' : ''; ?>><?php echo _t('show_not_reads'); ?></option>
+				</select>
 			</div>
 		</div>
 

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

@@ -103,6 +103,21 @@
 			</div>
 		</div>
 
+		<div class="form-group">
+			<label class="group-name" for="user_filter_shortcut"><?php echo Minz_Translate::t ('user_filter'); ?></label>
+			<div class="group-controls">
+				<input type="text" id="user_filter_shortcut" name="shortcuts[user_filter]" list="keys" value="<?php echo $s['user_filter']; ?>" />
+				<?php echo Minz_Translate::t ('user_filter_help'); ?>
+			</div>
+		</div>
+
+		<div class="form-group">
+			<label class="group-name" for="help_shortcut"><?php echo Minz_Translate::t ('help'); ?></label>
+			<div class="group-controls">
+				<input type="text" id="help_shortcut" name="shortcuts[help]" list="keys" value="<?php echo $s['help']; ?>" />
+			</div>
+		</div>
+
 		<div class="form-group form-actions">
 			<div class="group-controls">
 				<button type="submit" class="btn btn-important"><?php echo Minz_Translate::t ('save'); ?></button>

+ 8 - 3
app/views/helpers/javascript_vars.phtml

@@ -4,7 +4,8 @@ echo '"use strict";', "\n";
 
 $mark = $this->conf->mark_when;
 echo 'var ',
-	'hide_posts=', ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'false' : 'true',
+	'help_url="', FRESHRSS_WIKI, '"',
+	',hide_posts=', ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'false' : 'true',
 	',display_order="', Minz_Request::param('order', $this->conf->sort_order), '"',
 	',auto_mark_article=', $mark['article'] ? 'true' : 'false',
 	',auto_mark_site=', $mark['site'] ? 'true' : 'false',
@@ -25,7 +26,9 @@ echo ',shortcuts={',
 	'collapse_entry:"', $s['collapse_entry'], '",',
 	'load_more:"', $s['load_more'], '",',
 	'auto_share:"', $s['auto_share'], '",',
-	'focus_search:"', $s['focus_search'], '"',
+	'focus_search:"', $s['focus_search'], '",',
+	'user_filter:"', $s['user_filter'], '",',
+	'help:"', $s['help'], '"',
 "},\n";
 
 if (Minz_Request::param ('output') === 'global') {
@@ -48,9 +51,11 @@ echo 'authType="', $authType, '",',
 	'url_login="', _url ('index', 'login'), '",',
 	'url_logout="', _url ('index', 'logout'), '",';
 
-echo 'str_confirmation="', Minz_Translate::t('confirm_action'), '"', ",\n";
+echo 'str_confirmation_default="', Minz_Translate::t('confirm_action'), '"', ",\n";
 echo 'str_notif_title_articles="', Minz_Translate::t('notif_title_new_articles'), '"', ",\n";
 echo 'str_notif_body_articles="', Minz_Translate::t('notif_body_new_articles'), '"', ",\n";
+echo 'html5_notif_timeout=', $this->conf->html5_notif_timeout,",\n";
+
 
 $autoActualise = Minz_Session::param('actualize_feeds', false);
 echo 'auto_actualize_feeds=', $autoActualise ? 'true' : 'false', ";\n";

+ 26 - 15
app/views/helpers/pagination.phtml

@@ -1,26 +1,37 @@
 <?php
-	$c = Minz_Request::controllerName ();
-	$a = Minz_Request::actionName ();
-	$params = Minz_Request::params ();
-	$markReadUrl = Minz_Session::param ('markReadUrl');
-	Minz_Session::_param ('markReadUrl', false);
+	$c = Minz_Request::controllerName();
+	$a = Minz_Request::actionName();
+	$params = Minz_Request::params();
+	$markReadUrl = Minz_Session::param('markReadUrl');
+	Minz_Session::_param('markReadUrl', false);
 ?>
 
+<form id="mark-read-pagination" method="post" style="display: none"></form>
+
 <ul class="pagination">
 	<li class="item pager-next">
 	<?php if (!empty($this->nextId)) { ?>
-	<?php $params['next'] = $this->nextId; ?>
-	<a id="load_more" href="<?php echo Minz_Url::display (array ('c' => $c, 'a' => $a, 'params' => $params)); ?>"><?php echo Minz_Translate::t ('load_more'); ?></a>
+		<?php
+			$params['next'] = $this->nextId;
+			$params['ajax'] = 1;
+		?>
+		<a id="load_more" href="<?php echo Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)); ?>">
+			<?php echo _t('load_more'); ?>
+		</a>
 	<?php } elseif ($markReadUrl) { ?>
-	<a id="bigMarkAsRead" href="<?php echo $markReadUrl; ?>"<?php if ($this->conf->reading_confirm) { echo ' class="confirm"';} ?>>
-		<?php echo Minz_Translate::t ('nothing_to_load'); ?><br />
-		<span class="bigTick">✓</span><br />
-		<?php echo Minz_Translate::t ('mark_all_read'); ?>
-	</a>
+		<button id="bigMarkAsRead"
+		        class="as-link <?php echo $this->conf->reading_confirm ? 'confirm' : ''; ?>"
+		        form="mark-read-pagination"
+		        formaction="<?php echo $markReadUrl; ?>"
+		        type="submit">
+			<?php echo _t('nothing_to_load'); ?><br />
+			<span class="bigTick">✓</span><br />
+			<?php echo _t('mark_all_read'); ?>
+		</button>
 	<?php } else { ?>
-	<a id="bigMarkAsRead" href=".">
-		<?php echo Minz_Translate::t ('nothing_to_load'); ?><br />
-	</a>
+		<a id="bigMarkAsRead" href=".">
+			<?php echo _t('nothing_to_load'); ?><br />
+		</a>
 	<?php } ?>
 	</li>
 </ul>

+ 8 - 0
app/views/helpers/view/global_view.phtml

@@ -1,5 +1,6 @@
 <?php $this->partial ('nav_menu'); ?>
 
+<?php if (!empty($this->entries)) { ?>
 <div id="stream" class="global categories">
 <?php
 	$arUrl = array('c' => 'index', 'a' => 'index', 'params' => array());
@@ -43,3 +44,10 @@
 <div id="panel"<?php echo $this->conf->display_posts ? '' : ' class="hide_posts"'; ?>>
 	<a class="close" href="#"><?php echo FreshRSS_Themes::icon('close'); ?></a>
 </div>
+
+<?php } else { ?>
+<div id="stream" class="prompt alert alert-warn global">
+	<h2><?php echo _t('no_feed_to_display'); ?></h2>
+	<a href="<?php echo _url('configure', 'feed'); ?>"><?php echo _t('think_to_add'); ?></a><br /><br />
+</div>
+<?php } ?>

+ 3 - 3
app/views/helpers/view/normal_view.phtml

@@ -183,8 +183,8 @@ if (!empty($this->entries)) {
 <?php $this->partial ('nav_entries'); ?>
 
 <?php } else { ?>
-<div id="stream" class="alert alert-warn normal">
-	<span class="alert-head"><?php echo Minz_Translate::t ('no_feed_to_display'); ?></span>
-	<?php echo Minz_Translate::t ('think_to_add'); ?>
+<div id="stream" class="prompt alert alert-warn normal">
+	<h2><?php echo _t('no_feed_to_display'); ?></h2>
+	<a href="<?php echo _url('configure', 'feed'); ?>"><?php echo _t('think_to_add'); ?></a><br /><br />
 </div>
 <?php } ?>

+ 3 - 3
app/views/helpers/view/reader_view.phtml

@@ -37,8 +37,8 @@ if (!empty($this->entries)) {
 </div>
 
 <?php } else { ?>
-<div id="stream" class="alert alert-warn reader">
-	<span class="alert-head"><?php echo Minz_Translate::t ('no_feed_to_display'); ?></span>
-	<?php echo Minz_Translate::t ('think_to_add'); ?>
+<div id="stream" class="prompt alert alert-warn reader">
+	<h2><?php echo _t('no_feed_to_display'); ?></h2>
+	<a href="<?php echo _url('configure', 'feed'); ?>"><?php echo _t('think_to_add'); ?></a><br /><br />
 </div>
 <?php } ?>

+ 10 - 3
app/views/index/formLogin.phtml

@@ -3,7 +3,7 @@
 
 	switch (Minz_Configuration::authType()) {
 	case 'form':
-	?><form id="loginForm" method="post" action="<?php echo _url('index', 'formLogin'); ?>">
+	?><form id="crypto-form" method="post" action="<?php echo _url('index', 'formLogin'); ?>">
 		<div>
 			<label for="username"><?php echo _t('username'); ?></label>
 			<input type="text" id="username" name="username" size="16" required="required" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" autofocus="autofocus" />
@@ -29,8 +29,15 @@
 
 	case 'persona':
 		?><p>
-			<?php echo _i('login'); ?>
-			<a class="signin" href="#"><?php echo _t('login_with_persona'); ?></a>
+			<a class="signin btn btn-important" href="#">
+				<?php echo _i('login'); ?>
+				<?php echo _t('login_with_persona'); ?>
+			</a><br /><br />
+
+			<?php echo _i('help'); ?>
+			<small>
+				<a href="<?php echo _url('index', 'resetAuth'); ?>"><?php echo _t('login_persona_problem'); ?></a>
+			</small>
 		</p><?php
 		break;
 	} ?>

+ 33 - 0
app/views/index/resetAuth.phtml

@@ -0,0 +1,33 @@
+<div class="prompt">
+	<h1><?php echo _t('auth_reset'); ?></h1>
+
+	<?php if (!empty($this->message)) { ?>
+	<p class="alert <?php echo $this->message['status'] === 'bad' ? 'alert-error' : 'alert-warn'; ?>">
+		<span class="alert-head"><?php echo $this->message['title']; ?></span><br />
+		<?php echo $this->message['body']; ?>
+	</p>
+	<?php } ?>
+
+	<?php if (!$this->no_form) { ?>
+	<form id="crypto-form" method="post" action="<?php echo _url('index', 'resetAuth'); ?>">
+		<p class="alert alert-warn">
+			<span class="alert-head"><?php echo _t('attention'); ?></span><br />
+			<?php echo _t('auth_will_reset'); ?>
+		</p>
+
+		<div>
+			<label for="username"><?php echo _t('username_admin'); ?></label>
+			<input type="text" id="username" name="username" size="16" required="required" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" autofocus="autofocus" />
+		</div>
+		<div>
+			<label for="passwordPlain"><?php echo _t('password'); ?></label>
+				<input type="password" id="passwordPlain" required="required" />
+				<input type="hidden" id="challenge" name="challenge" /><br />
+				<noscript><strong><?php echo _t('javascript_should_be_activated'); ?></strong></noscript>
+		</div>
+		<div>
+			<button id="loginButton" type="submit" class="btn btn-important"><?php echo _t('submit'); ?></button>
+		</div>
+	</form>
+	<?php } ?>
+</div>

+ 28 - 5
app/views/stats/idle.phtml

@@ -1,25 +1,48 @@
 <?php $this->partial('aside_stats'); ?>
 
-<div class="post content">
+<div class="post">
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a>
 
 	<h1><?php echo _t('stats_idle'); ?></h1>
 
 	<?php
+		$current_url = urlencode(Minz_Url::display(
+			array('c' => 'stats', 'a' => 'idle'),
+			'php', true
+		));
+		$nothing = true;
 		foreach ($this->idleFeeds as $period => $feeds) {
 			if (!empty($feeds)) {
+				$nothing = false;
 	?>
 		<div class="stat">
 			<h2><?php echo _t($period); ?></h2>
 
-			<ul>
-				<?php foreach ($feeds as $feed) { ?>
-					<li><a href="<?php echo _url('configure', 'feed', 'id', $feed['id']); ?>" title="<?php echo date('Y-m-d', $feed['last_date']); ?>"><?php echo $feed['name']; ?></a></li>
-				<?php } ?>
+			<form id="form-delete" method="post" style="display: none"></form>
+
+			<?php foreach ($feeds as $feed) { ?>
+			<ul class="horizontal-list">
+				<li class="item">
+					<div class="stick">
+						<a class="btn" href="<?php echo _url('index', 'index', 'get', 'f_' . $feed['id']); ?>"><?php echo _i('link'); ?> <?php echo _t('filter'); ?></a>
+						<a class="btn" href="<?php echo _url('configure', 'feed', 'id', $feed['id']); ?>"><?php echo _i('configure'); ?> <?php echo _t('administration'); ?></a>
+						<button class="btn btn-attention confirm" form="form-delete" formaction="<?php echo _url('feed', 'delete', 'id', $feed['id'], 'r', $current_url); ?>"><?php echo _t('delete'); ?></button>
+					</div>
+				</li>
+				<li class="item">
+					<span title="<?php echo timestamptodate($feed['last_date'], false); ?>"><?php echo $feed['name']; ?> (<?php echo _t('number_articles', $feed['nb_articles']); ?>)</span>
+				</li>
 			</ul>
+			<?php } ?>
 		</div>
 	<?php
 			}
 		}
+
+		if ($nothing) {
 	?>
+	<p class="alert alert-warn">
+		<span class="alert-head"><?php echo _t('stats_no_idle'); ?></span>
+	</p>
+	<?php } ?>
 </div>

+ 24 - 24
app/views/stats/index.phtml

@@ -1,11 +1,11 @@
 <?php $this->partial('aside_stats'); ?>
 
-<div class="post content">
+<div class="post">
 	<a href="<?php echo _url ('index', 'index'); ?>"><?php echo _t ('back_to_rss_feeds'); ?></a>
-	
+
 	<h1><?php echo _t ('stats_main'); ?></h1>
 
-	<div class="stat">
+	<div class="stat half">
 		<h2><?php echo _t ('stats_entry_repartition'); ?></h2>
 		<table>
 			<thead>
@@ -38,26 +38,9 @@
 				</tr>
 			</tbody>
 		</table>
-	</div>
-	
-	<div class="stat">
-		<h2><?php echo _t ('stats_entry_per_day'); ?></h2>
-		<div id="statsEntryPerDay" style="height: 300px"></div>
-	</div>
-	
-	<div class="stat">
-		<h2><?php echo _t ('stats_feed_per_category'); ?></h2>
-		<div id="statsFeedPerCategory" style="height: 300px"></div>
-		<div id="statsFeedPerCategoryLegend"></div>
-	</div>
-	
-	<div class="stat">
-		<h2><?php echo _t ('stats_entry_per_category'); ?></h2>
-		<div id="statsEntryPerCategory" style="height: 300px"></div>
-		<div id="statsEntryPerCategoryLegend"></div>
-	</div>
-	
-	<div class="stat">
+	</div><!--
+
+	--><div class="stat half">
 		<h2><?php echo _t ('stats_top_feed'); ?></h2>
 		<table>
 			<thead>
@@ -70,7 +53,7 @@
 			<tbody>
 				<?php foreach ($this->topFeed as $feed): ?>
 					<tr>
-						<td><?php echo $feed['name']; ?></td>
+						<td><a href="<?php echo _url('stats', 'repartition', 'id', $feed['id']); ?>"><?php echo $feed['name']; ?></a></td>
 						<td><?php echo $feed['category']; ?></td>
 						<td class="numeric"><?php echo formatNumber($feed['count']); ?></td>
 					</tr>
@@ -78,6 +61,23 @@
 			</tbody>
 		</table>
 	</div>
+
+	<div class="stat">
+		<h2><?php echo _t ('stats_entry_per_day'); ?></h2>
+		<div id="statsEntryPerDay" style="height: 300px"></div>
+	</div>
+
+	<div class="stat half">
+		<h2><?php echo _t ('stats_feed_per_category'); ?></h2>
+		<div id="statsFeedPerCategory" style="height: 300px"></div>
+		<div id="statsFeedPerCategoryLegend"></div>
+	</div><!--
+
+	--><div class="stat half">
+		<h2><?php echo _t ('stats_entry_per_category'); ?></h2>
+		<div id="statsEntryPerCategory" style="height: 300px"></div>
+		<div id="statsEntryPerCategoryLegend"></div>
+	</div>
 </div>
 
 <script>

+ 51 - 15
app/views/stats/repartition.phtml

@@ -1,6 +1,6 @@
 <?php $this->partial('aside_stats'); ?>
 
-<div class="post content">
+<div class="post ">
 	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a>
 
 	<h1><?php echo _t('stats_repartition'); ?></h1>
@@ -10,12 +10,12 @@
 	<?php foreach ($this->categories as $category) {
 		$feeds = $category->feeds();
 		if (!empty($feeds)) {
-			echo '<optgroup label=', $category->name(), '>';
+			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>';
+					echo '<option value="', $feed->id(), '" selected="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 '<option value="', $feed->id(), '" data-url="', _url('stats', 'repartition', 'id', $feed->id()), '">', $feed->name(), '</option>';
 				}
 			}
 			echo '</optgroup>';
@@ -24,8 +24,8 @@
 	</select>
 
 	<?php if ($this->feed) {?>
-		<a href="<?php echo _url('configure', 'feed', 'id', $this->feed->id()); ?>">
-			<?php echo _t('administration'); ?>
+		<a class="btn" href="<?php echo _url('configure', 'feed', 'id', $this->feed->id()); ?>">
+			<?php echo _i('configure'); ?> <?php echo _t('administration'); ?>
 		</a>
 	<?php }?>
 
@@ -34,12 +34,12 @@
 		<div id="statsEntryPerHour" style="height: 300px"></div>
 	</div>
 
-	<div class="stat">
+	<div class="stat half">
 		<h2><?php echo _t('stats_entry_per_day_of_week'); ?></h2>
 		<div id="statsEntryPerDayOfWeek" style="height: 300px"></div>
-	</div>
+	</div><!--
 
-	<div class="stat">
+	--><div class="stat half">
 		<h2><?php echo _t('stats_entry_per_month'); ?></h2>
 		<div id="statsEntryPerMonth" style="height: 300px"></div>
 	</div>
@@ -56,11 +56,22 @@ function initStats() {
 		return;
 	}
 	// Entry per hour
+	var avg_h = [];
+	for (var i = -1; i <= 24; i++) {
+		avg_h.push([i, <?php echo $this->averageHour?>]);
+	}
 	Flotr.draw(document.getElementById('statsEntryPerHour'),
-		[<?php echo $this->repartitionHour ?>],
+		[{
+			data: <?php echo $this->repartitionHour ?>,
+			bars: {horizontal: false, show: true}
+		}, {
+			data: avg_h,
+			lines: {show: true},
+			label: <?php echo $this->averageHour?>,
+			yaxis: 2
+		}],
 		{
 			grid: {verticalLines: false},
-			bars: {horizontal: false, show: true},
 			xaxis: {noTicks: 23,
 				tickFormatter: function(x) {
 					var x = parseInt(x);
@@ -70,14 +81,26 @@ function initStats() {
 				max: 23.9,
 				tickDecimals: 0},
 			yaxis: {min: 0},
+			y2axis: {showLabels: false},
 			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
 		});
 	// Entry per day of week
+	var avg_dow = [];
+	for (var i = -1; i <= 7; i++) {
+		avg_dow.push([i, <?php echo $this->averageDayOfWeek?>]);
+	}
 	Flotr.draw(document.getElementById('statsEntryPerDayOfWeek'),
-		[<?php echo $this->repartitionDayOfWeek ?>],
+		[{
+			data: <?php echo $this->repartitionDayOfWeek ?>,
+			bars: {horizontal: false, show: true}
+		}, {
+			data: avg_dow,
+			lines: {show: true},
+			label: <?php echo $this->averageDayOfWeek?>,
+			yaxis: 2
+		}],
 		{
 			grid: {verticalLines: false},
-			bars: {horizontal: false, show: true},
 			xaxis: {noTicks: 6,
 				tickFormatter: function(x) {
 					var x = parseInt(x),
@@ -88,14 +111,26 @@ function initStats() {
 				max: 6.9,
 				tickDecimals: 0},
 			yaxis: {min: 0},
+			y2axis: {showLabels: false},
 			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
 		});
 	// Entry per month
+	var avg_m = [];
+	for (var i = 0; i <= 13; i++) {
+		avg_m.push([i, <?php echo $this->averageMonth?>]);
+	}
 	Flotr.draw(document.getElementById('statsEntryPerMonth'),
-		[<?php echo $this->repartitionMonth ?>],
+		[{
+			data: <?php echo $this->repartitionMonth ?>,
+			bars: {horizontal: false, show: true}
+		}, {
+			data: avg_m,
+			lines: {show: true},
+			label: <?php echo $this->averageMonth?>,
+			yaxis: 2
+		}],
 		{
 			grid: {verticalLines: false},
-			bars: {horizontal: false, show: true},
 			xaxis: {noTicks: 12,
 				tickFormatter: function(x) {
 					var x = parseInt(x),
@@ -106,6 +141,7 @@ function initStats() {
 				max: 12.9,
 				tickDecimals: 0},
 			yaxis: {min: 0},
+			y2axis: {showLabels: false},
 			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
 		});
 

+ 9 - 0
app/views/update/apply.phtml

@@ -0,0 +1,9 @@
+<?php $this->partial('aside_configure'); ?>
+
+<div class="post">
+	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a>
+
+	<h1><?php echo _t('update_system'); ?></h1>
+
+	<?php ask_info_update(); ?>
+</div>

+ 36 - 0
app/views/update/index.phtml

@@ -0,0 +1,36 @@
+<?php $this->partial('aside_configure'); ?>
+
+<div class="post">
+	<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a>
+
+	<h1><?php echo _t('update_system'); ?></h1>
+
+	<p>
+		<?php echo _i('help'); ?> <?php echo _t('update_last', $this->last_update_time); ?>
+	</p>
+
+	<?php if (!empty($this->message)) { ?>
+	<p class="alert <?php echo $this->message['status'] === 'bad' ? 'alert-error' : 'alert-warn'; ?>">
+		<span class="alert-head"><?php echo $this->message['title']; ?></span>
+		<?php echo $this->message['body']; ?>
+	</p>
+	<?php } elseif ($this->check_last_hour) { ?>
+	<p class="alert alert-warn">
+		<span class="alert-head"><?php echo _t('damn'); ?></span>
+		<?php echo _t('no_update'); ?>
+	</p>
+	<?php } ?>
+
+	<?php
+		if (!$this->check_last_hour &&
+				(empty($this->message) || $this->message['status'] !== 'good')) {
+	?>
+	<p>
+		<a href="<?php echo _url('update', 'check'); ?>" class="btn"><?php echo _t('update_check'); ?></a>
+	</p>
+	<?php } ?>
+
+	<?php if ($this->update_to_apply) { ?>
+	<a class="btn btn-important" href="<?php echo _url('update', 'apply'); ?>"><?php echo _t('update_apply'); ?></a>
+	<?php } ?>
+</div>

+ 3 - 0
constants.php

@@ -1,6 +1,8 @@
 <?php
 define('FRESHRSS_VERSION', '0.7.4');
 define('FRESHRSS_WEBSITE', 'http://freshrss.org');
+define('FRESHRSS_UPDATE_WEBSITE', 'https://update.freshrss.org?v=' . FRESHRSS_VERSION);
+define('FRESHRSS_WIKI', 'http://doc.freshrss.org');
 
 // PHP text output compression http://php.net/ob_gzhandler (better to do it at Web server level)
 define('PHP_COMPRESSION', false);
@@ -13,6 +15,7 @@ define('FRESHRSS_PATH', dirname(__FILE__));
 		define('PUBLIC_RELATIVE', '..');
 
 	define('DATA_PATH', FRESHRSS_PATH . '/data');
+		define('UPDATE_FILENAME', DATA_PATH . '/update.php');
 		define('LOG_PATH', DATA_PATH . '/log');
 		define('CACHE_PATH', DATA_PATH . '/cache');
 

+ 2 - 0
data/.gitignore

@@ -6,3 +6,5 @@ touch.txt
 no-cache.txt
 *.bak.php
 *.lock.txt
+last_update.txt
+update.php

+ 3 - 0
lib/Minz/ModelPdo.php

@@ -77,6 +77,9 @@ class Minz_ModelPdo {
 				$db['password'],
 				$driver_options
 			);
+			if ($type === 'sqlite') {
+				$this->bd->exec('PRAGMA foreign_keys = ON;');
+			}
 			self::$sharedBd = $this->bd;
 		} catch (Exception $e) {
 			throw new Minz_PDOConnectionException(

+ 5 - 0
lib/Minz/Request.php

@@ -124,6 +124,11 @@ class Minz_Request {
 	 *                > sinon, le dispatcher recharge en interne
 	 */
 	public static function forward($url = array(), $redirect = false) {
+		if (!is_array($url)) {
+			header('Location: ' . $url);
+			exit();
+		}
+
 		$url = Minz_Url::checkUrl($url);
 
 		if ($redirect) {

+ 11 - 4
lib/Minz/View.php

@@ -26,12 +26,19 @@ class Minz_View {
 	 * Détermine si on utilise un layout ou non
 	 */
 	public function __construct () {
+		$this->change_view(Minz_Request::controllerName(),
+		                   Minz_Request::actionName());
+		self::$title = Minz_Configuration::title ();
+	}
+
+	/**
+	 * Change le fichier de vue en fonction d'un controller / action
+	 */
+	public function change_view($controller_name, $action_name) {
 		$this->view_filename = APP_PATH
 		                     . self::VIEWS_PATH_NAME . '/'
-		                     . Minz_Request::controllerName () . '/'
-		                     . Minz_Request::actionName () . '.phtml';
-
-		self::$title = Minz_Configuration::title ();
+		                     . $controller_name . '/'
+		                     . $action_name . '.phtml';
 	}
 
 	/**

+ 1 - 1
lib/SimplePie/SimplePie/Parser.php

@@ -142,7 +142,7 @@ class SimplePie_Parser
 				$dom = new DOMDocument();
 				$dom->recover = true;
 				$dom->strictErrorChecking = false;
-				$dom->loadXML($data);
+				@$dom->loadXML($data);
 				$this->encoding = $encoding = $dom->encoding = 'UTF-8';
 				$data2 = $dom->saveXML();
 				if (function_exists('mb_convert_encoding'))

+ 14 - 0
lib/lib_rss.php

@@ -230,3 +230,17 @@ function cryptAvailable() {
 	}
 	return false;
 }
+
+function is_referer_from_same_domain() {
+	if (empty($_SERVER['HTTP_REFERER'])) {
+		return false;
+	}
+	$host = parse_url(((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https://' : 'http://') .
+		(empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST']));
+	$referer = parse_url($_SERVER['HTTP_REFERER']);
+	if (empty($host['scheme']) || empty($referer['scheme']) || $host['scheme'] !== $referer['scheme'] ||
+	    empty($host['host']) || empty($referer['host']) || $host['host'] !== $referer['host']) {
+		return false;
+	}
+	return (isset($host['port']) ? $host['port'] : 0) === (isset($referer['port']) ? $referer['port'] : 0);
+}

+ 131 - 40
p/scripts/main.js

@@ -251,9 +251,14 @@ function toggleContent(new_active, old_active) {
 	}
 
 	if (sticky_post) {
-		var new_pos = new_active.position().top - new_active.children('.flux_header').outerHeight(),
+		var prev_article = new_active.prevAll('.flux'),
+		    new_pos = new_active.position().top,
 			old_scroll = $(box_to_move).scrollTop();
 
+		if (prev_article.length > 0 && new_pos - prev_article.position().top <= 150) {
+			new_pos = prev_article.position().top;
+		}
+
 		if (hide_posts) {
 			if (relative_move) {
 				new_pos += old_scroll;
@@ -297,7 +302,7 @@ function next_entry() {
 function prev_feed() {
 	var active_feed = $("#aside_flux .feeds li.active");
 	if (active_feed.length > 0) {
-		active_feed.prev().find('a.feed').each(function(){this.click();});
+		active_feed.prevAll(':visible:first').find('a.feed').each(function(){this.click();});
 	} else {
 		last_feed();
 	}
@@ -306,21 +311,21 @@ function prev_feed() {
 function next_feed() {
 	var active_feed = $("#aside_flux .feeds li.active");
 	if (active_feed.length > 0) {
-		active_feed.next().find('a.feed').each(function(){this.click();});
+		active_feed.nextAll(':visible:first').find('a.feed').each(function(){this.click();});
 	} else {
 		first_feed();
 	}
 }
 
 function first_feed() {
-	var feed = $("#aside_flux .feeds.active li:first");
+	var feed = $("#aside_flux .feeds.active li:visible:first");
 	if (feed.length > 0) {
 		feed.find('a')[1].click();
 	}
 }
 
 function last_feed() {
-	var feed = $("#aside_flux .feeds.active li:last");
+	var feed = $("#aside_flux .feeds.active li:visible:last");
 	if (feed.length > 0) {
 		feed.find('a')[1].click();
 	}
@@ -330,7 +335,7 @@ function prev_category() {
 	var active_cat = $("#aside_flux .category.stick.active");
 
 	if (active_cat.length > 0) {
-		var prev_cat = active_cat.parent('li').prev().find('.category.stick a.btn');
+		var prev_cat = active_cat.parent('li').prevAll(':visible:first').find('.category.stick a.btn');
 		if (prev_cat.length > 0) {
 			prev_cat[0].click();
 		}
@@ -344,7 +349,7 @@ function next_category() {
 	var active_cat = $("#aside_flux .category.stick.active");
 
 	if (active_cat.length > 0) {
-		var next_cat = active_cat.parent('li').next().find('.category.stick a.btn');
+		var next_cat = active_cat.parent('li').nextAll(':visible:first').find('.category.stick a.btn');
 		if (next_cat.length > 0) {
 			next_cat[0].click();
 		}
@@ -355,14 +360,14 @@ function next_category() {
 }
 
 function first_category() {
-	var cat = $("#aside_flux .category.stick:first");
+	var cat = $("#aside_flux .category.stick:visible:first");
 	if (cat.length > 0) {
 		cat.find('a.btn')[0].click();
 	}
 }
 
 function last_category() {
-	var cat = $("#aside_flux .category.stick:last");
+	var cat = $("#aside_flux .category.stick:visible:last");
 	if (cat.length > 0) {
 		cat.find('a.btn')[0].click();
 	}
@@ -373,11 +378,41 @@ function collapse_entry() {
 
 	var flux_current = $(".flux.current");
 	flux_current.toggleClass("active");
-	if (isCollapsed) {
+	if (isCollapsed && auto_mark_article) {
 		mark_read(flux_current, true);
 	}
 }
 
+function user_filter(key) {
+	console.log('user filter');
+	console.warn(key);
+	var filter = $('#dropdown-query');
+	var filters = filter.siblings('.dropdown-menu').find('.item.query a');
+	if (typeof key === "undefined") {
+		if (!filter.length) {
+			return;
+		}
+		// Display the filter div
+		window.location.hash = filter.attr('id');
+		// Force scrolling to the filter div
+		var scroll = needsScroll($('.header'));
+		if (scroll !== 0) {
+			$('html,body').scrollTop(scroll);
+		}
+		// Force the key value if there is only one action, so we can trigger it automatically
+		if (filters.length === 1) {
+			key = 1;
+		} else {
+			return;
+		}
+	}
+	// Trigger selected share action
+	key = parseInt(key);
+	if (key <= filters.length) {
+		filters[key - 1].click();
+	}
+}
+
 function auto_share(key) {
 	var share = $(".flux.current.active").find('.dropdown-target[id^="dropdown-share"]');
 	var shares = share.siblings('.dropdown-menu').find('.item a');
@@ -503,13 +538,13 @@ function init_shortcuts() {
 	});
 	shortcut.add("shift+" + shortcuts.mark_read, function () {
 		// on marque tout comme lu
-		var url = $(".nav_menu a.read_all").attr("href");
-		if ($(".nav_menu a.read_all").hasClass('confirm')) {
+		var btn = $(".nav_menu .read_all");
+		if (btn.hasClass('confirm')) {
 			if (confirm(str_confirmation)) {
-				redirect(url, false);
+				btn.click();
 			}
 		} else {
-			redirect(url, false);
+			btn.click();
 		}
 	}, {
 		'disable_in_input': true
@@ -531,9 +566,19 @@ function init_shortcuts() {
 	}, {
 		'disable_in_input': true
 	});
+
+	shortcut.add(shortcuts.user_filter, function () {
+		user_filter();
+	}, {
+		'disable_in_input': true
+	});
 	for(var i = 1; i < 10; i++){
 		shortcut.add(i.toString(), function (e) {
-			auto_share(String.fromCharCode(e.keyCode));
+			if ($('#dropdown-query').siblings('.dropdown-menu').is(':visible')) {
+				user_filter(String.fromCharCode(e.keyCode));
+			} else {
+				auto_share(String.fromCharCode(e.keyCode));
+			}
 		}, {
 			'disable_in_input': true
 		});
@@ -618,6 +663,13 @@ function init_shortcuts() {
 	}, {
 		'disable_in_input': true
 	});
+
+	shortcut.add(shortcuts.help, function () {
+		redirect(help_url, true);
+	}, {
+		'disable_in_input': true
+	});
+
 }
 
 function init_stream(divStream) {
@@ -650,11 +702,25 @@ function init_stream(divStream) {
 	});
 
 	divStream.on('click', '.item.title > a', function (e) {
+		// Allow default control-click behaviour such as open in backround-tab.
+		return e.ctrlKey;
+	});
+	divStream.on('mouseup', '.item.title > a', function (e) {
+		// Mouseup enables us to catch middle click.
 		if (e.ctrlKey) {
-			return true;	//Allow default control-click behaviour such as open in backround-tab
+			// CTRL+click, it will be manage by previous rule.
+			return;
+		}
+
+		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);
+		} else if(e.which == 1) {
+			// Normal click, just toggle article.
+			$(this).parent().click();
 		}
-		$(this).parent().click();	//Will perform toggle flux_content
-		return false;
 	});
 
 	divStream.on('click', '.flux .content a', function () {
@@ -662,7 +728,13 @@ function init_stream(divStream) {
 	});
 
 	if (auto_mark_site) {
-		divStream.on('click', '.flux .link > a', function () {
+		// 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) {
+			if (e.which == 3) {
+				return;
+			}
+
 			mark_read($(this).parents(".flux"), true);
 		});
 	}
@@ -740,7 +812,7 @@ function openNotification(msg, status) {
 	notification.find(".msg").html(msg);
 	notification.fadeIn(300);
 
-	notification_interval = window.setInterval(closeNotification, 4000);
+	notification_interval = window.setTimeout(closeNotification, 4000);
 }
 
 function closeNotification() {
@@ -763,7 +835,7 @@ function init_notifications() {
 
 	if (notification.find(".msg").html().length > 0) {
 		notification_working = true;
-		notification_interval = window.setInterval(closeNotification, 4000);
+		notification_interval = window.setTimeout(closeNotification, 4000);
 	}
 }
 // </notification>
@@ -788,12 +860,19 @@ function notifs_html5_show(nb) {
 
 	var notification = new window.Notification(str_notif_title_articles, {
 		icon: "../themes/icons/favicon-256.png",
-		body: str_notif_body_articles.replace("\d", nb)
+		body: str_notif_body_articles.replace("\d", nb),
+		tag: "freshRssNewArticles"
 	});
 
 	notification.onclick = function() {
 		window.location.reload();
 	}
+
+	if (html5_notif_timeout !== 0){
+		setTimeout(function() {
+					notification.close();
+				}, html5_notif_timeout * 1000);
+	}
 }
 
 function init_notifs_html5() {
@@ -847,9 +926,13 @@ function load_more_posts() {
 		box_load_more.children('.flux:last').after($('#stream', data).children('.flux, .day'));
 		$('.pagination').replaceWith($('.pagination', data));
 		if (display_order === 'ASC') {
-			$('#nav_menu_read_all>a').attr('href', $('#bigMarkAsRead').attr('href'));
+			$('#nav_menu_read_all > .read_all').attr(
+				'formaction', $('#bigMarkAsRead').attr('formaction')
+			);
 		} else {
-			$('#bigMarkAsRead').attr('href', $('#nav_menu_read_all>a').attr('href'));
+			$('#bigMarkAsRead').attr(
+				'formaction', $('#nav_menu_read_all > .read_all').attr('formaction')
+			);
 		}
 
 		$('[id^=day_]').each(function (i) {
@@ -901,7 +984,7 @@ function init_load_more(box) {
 }
 //</endless_mode>
 
-//<Web login form>
+//<crypto form (Web login)>
 function poormanSalt() {	//If crypto.getRandomValues is not available
 	var text = '$2a$04$',
 		base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz';
@@ -911,20 +994,24 @@ function poormanSalt() {	//If crypto.getRandomValues is not available
 	return text;
 }
 
-function init_loginForm() {
-	var $loginForm = $('#loginForm');
-	if ($loginForm.length === 0) {
+function init_crypto_form() {
+	var $crypto_form = $('#crypto-form');
+	if ($crypto_form.length === 0) {
 		return;
 	}
+
 	if (!(window.dcodeIO)) {
 		if (window.console) {
 			console.log('FreshRSS waiting for bcrypt.js…');
 		}
-		window.setTimeout(init_loginForm, 100);
+		window.setTimeout(init_crypto_form, 100);
 		return;
 	}
-	$loginForm.on('submit', function() {
-		$('#loginButton').attr('disabled', '');
+
+	$crypto_form.on('submit', function() {
+		var $submit_button = $(this).find('button[type="submit"]');
+		$submit_button.attr('disabled', '');
+
 		var success = false;
 		$.ajax({
 			url: './?c=javascript&a=nonce&user=' + $('#username').val(),
@@ -932,7 +1019,7 @@ function init_loginForm() {
 			async: false
 		}).done(function (data) {
 			if (data.salt1 == '' || data.nonce == '') {
-				alert('Invalid user!');
+				openNotification('Invalid user!', 'bad');
 			} else {
 				try {
 					var strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'),
@@ -940,22 +1027,23 @@ function init_loginForm() {
 						c = dcodeIO.bcrypt.hashSync(data.nonce + s, strong ? 4 : poormanSalt());
 					$('#challenge').val(c);
 					if (s == '' || c == '') {
-						alert('Crypto error!');
+						openNotification('Crypto error!', 'bad');
 					} else {
 						success = true;
 					}
 				} catch (e) {
-					alert('Crypto exception! ' + e);
+					openNotification('Crypto exception! ' + e, 'bad');
 				}
 			}
 		}).fail(function() {
-			alert('Communication error!');
+			openNotification('Communication error!', 'bad');
 		});
-		$('#loginButton').removeAttr('disabled');
+
+		$submit_button.removeAttr('disabled');
 		return success;
 	});
 }
-//</Web login form>
+//</crypto form (Web login)>
 
 //<persona>
 function init_persona() {
@@ -1021,6 +1109,11 @@ function init_persona() {
 
 function init_confirm_action() {
 	$('body').on('click', '.confirm', function () {
+		var str_confirmation = $(this).attr('data-str-confirm');
+		if (!str_confirmation) {
+			str_confirmation = str_confirmation_default;
+		}
+
 		return confirm(str_confirmation);
 	});
 }
@@ -1157,9 +1250,6 @@ function init_all() {
 	}
 	init_notifications();
 	switch (authType) {
-		case 'form':
-			init_loginForm();
-			break;
 		case 'persona':
 			init_persona();
 			break;
@@ -1179,6 +1269,7 @@ function init_all() {
 		init_notifs_html5();
 		window.setInterval(refreshUnreads, 120000);
 	} else {
+		init_crypto_form();
 		init_share_observers();
 		init_remove_observers();
 		init_feed_observers();

+ 17 - 5
p/themes/Dark/dark.css

@@ -16,9 +16,9 @@ html, body {
 }
 
 /*=== Links */
-a {
-	outline: none;
+a, button.as-link {
 	color: #6986B2;
+	outline: none;
 }
 
 /*=== Images */
@@ -338,8 +338,9 @@ a.btn {
 	padding: 0 25px;
 	line-height: 2.5em;
 }
-.dropdown-menu > .item > span {
-	padding: 0 25px;
+.dropdown-menu > .item > span,
+.dropdown-menu > .item > .as-link {
+	padding: 0 22px;
 	line-height: 2em;
 }
 .dropdown-menu > .item:hover {
@@ -871,7 +872,18 @@ a.btn {
 .stat > table td,
 .stat > table th {
 	border-bottom: 1px solid #333;
-	text-align: center;
+}
+
+.stat > .horizontal-list {
+	margin: 0 0 5px;
+}
+.stat > .horizontal-list .item {
+	overflow: hidden;
+	white-space: nowrap;
+	text-overflow: ellipsis;
+}
+.stat > .horizontal-list .item:first-child {
+	width: 270px;
 }
 
 /*=== LOGS */

+ 1 - 1
p/themes/Dark/metadata.json

@@ -3,5 +3,5 @@
 	"author": "AD",
 	"description": "Le coté obscur du thème “Origine”",
 	"version": 0.2,
-	"files": ["template.css", "dark.css"]
+	"files": ["_template.css", "dark.css"]
 }

+ 0 - 698
p/themes/Dark/template.css

@@ -1,698 +0,0 @@
-@charset "UTF-8";
-
-/*=== GENERAL */
-/*============*/
-html, body {
-	margin: 0;
-	padding: 0;
-	font-size: 100%;
-}
-
-/*=== Links */
-a {
-	text-decoration: none;
-}
-a:hover {
-	text-decoration: underline;
-}
-
-/*=== Lists */
-ul, ol, dd {
-	margin: 0;
-	padding: 0;
-}
-
-/*=== Titles */
-h1 {
-	margin: 0.6em 0 0.3em;
-	font-size: 1.5em;
-	line-height: 1.6em;
-}
-h2 {
-	margin: 0.5em 0 0.25em;
-	font-size: 1.3em;
-	line-height: 2em;
-}
-h3 {
-	margin: 0.5em 0 0.25em;
-	font-size: 1.1em;
-	line-height: 2em;
-}
-
-/*=== Paragraphs */
-p {
-	margin: 1em 0 0.5em;
-	font-size: 1em;
-}
-
-/*=== Images */
-img {
-	height: auto;
-	max-width: 100%;
-}
-img.favicon {
-	height: 16px;
-	width: 16px;
-	vertical-align: middle;
-}
-
-/*=== Videos */
-iframe, embed, object, video {
-	max-width: 100%;
-}
-
-/*=== Forms */
-legend {
-	display: block;
-	width: 100%;
-	clear: both;
-}
-label {
-	display: block;
-}
-input {
-	width: 180px;
-}
-textarea {
-	width: 300px;
-}
-input, select, textarea {
-	display: inline-block;
-	max-width: 100%;
-}
-input[type="radio"],
-input[type="checkbox"] {
-	width: 15px !important;
-	min-height: 15px !important;
-}
-input.extend:focus {
-	width: 300px;
-}
-
-/*=== COMPONENTS */
-/*===============*/
-/*=== Forms */
-.form-group:after {
-	content: "";
-	display: block;
-	clear: both;
-}
-.form-group.form-actions {
-	min-width: 250px;
-}
-.form-group .group-name {
-	display: block;
-	float: left;
-	width: 200px;
-}
-.form-group .group-controls {
-	min-width: 250px;
-	margin: 0 0 0 220px;
-}
-.form-group .group-controls .control {
-	display: block;
-}
-
-/*=== Buttons */
-.stick {
-	display: inline-block;
-	white-space: nowrap;
-}
-.btn,
-a.btn {
-	display: inline-block;
-	cursor: pointer;
-	overflow: hidden;
-}
-.btn-important {
-	font-weight: bold;
-}
-
-/*=== Navigation */
-.nav-list .nav-header,
-.nav-list .item {
-	display: block;
-}
-.nav-list .item,
-.nav-list .item > a {
-	display: block;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-}
-.nav-head {
-	display: block;
-}
-.nav-head .item {
-	display: inline-block;
-}
-
-/*=== Horizontal-list */
-.horizontal-list {
-	display: table;
-	table-layout: fixed;
-	width: 100%;
-}
-.horizontal-list .item {
-	display: table-cell;
-}
-
-/*=== Dropdown */
-.dropdown {
-	position: relative;
-	display: inline-block;
-}
-.dropdown-target {
-	display: none;
-}
-.dropdown-menu {
-	display: none;
-	min-width: 200px;
-	margin: 0;
-	position: absolute;
-	right: 0;
-	background: #fff;
-	border: 1px solid #aaa;
-}
-.dropdown-header {
-	display: block;
-}
-.dropdown-menu > .item {
-	display: block;
-}
-.dropdown-menu > .item > a,
-.dropdown-menu > .item > span {
-	display: block;
-}
-.dropdown-menu > .item[aria-checked="true"] > a:before {
-	content: '✓';
-}
-.dropdown-menu .input {
-	display: block;
-}
-.dropdown-menu .input select,
-.dropdown-menu .input input {
-	display: block;
-	max-width: 95%;
-}
-.dropdown-target:target ~ .dropdown-menu {
-	display: block;
-	z-index: 10;
-}
-.dropdown-close {
-	display: inline;
-}
-.dropdown-close a {
-	font-size: 0;
-	position: fixed;
-	top: 0; bottom: 0;
-	left: 0; right: 0;
-	display: block;
-	z-index: -10;
-}
-.separator {
-	display: block;
-	height: 0;
-	border-bottom: 1px solid #aaa;
-}
-
-/*=== Alerts */
-.alert {
-	display: block;
-	width: 90%;
-}
-.group-controls .alert {
-	width: 100%
-}
-.alert-head {
-	margin: 0;
-	font-weight: bold;
-}
-.alert ul {
-	margin: 5px 20px;
-}
-
-/*=== Icons */
-.icon {
-	display: inline-block;
-	width: 16px;
-	height: 16px;
-	vertical-align: middle;
-	line-height: 16px;
-}
-
-/*=== Pagination */
-.pagination {
-	display: table;
-	width: 100%;
-	margin: 0;
-	padding: 0;
-	table-layout: fixed;
-}
-.pagination .item {
-	display: table-cell;
-}
-.pagination .pager-first,
-.pagination .pager-previous,
-.pagination .pager-next,
-.pagination .pager-last {
-	width: 100px;
-}
-
-/*=== STRUCTURE */
-/*===============*/
-/*=== Header */
-.header {
-	display: table;
-	width: 100%;
-	table-layout: fixed;
-}
-.header > .item {
-	display: table-cell;
-}
-.header > .item.title {
-	width: 250px;
-	white-space: nowrap;
-}
-.header > .item.title h1 {
-	display: inline-block;
-}
-.header > .item.title .logo {
-	display: inline-block;
-	height: 32px;
-	width: 32px;
-	vertical-align: middle;
-}
-.header > .item.configure {
-	width: 100px;
-}
-
-/*=== Body */
-#global {
-	display: table;
-	width: 100%;
-	height: 100%;
-	table-layout: fixed;
-}
-.aside {
-	display: table-cell;
-	height: 100%;
-	width: 250px;
-	vertical-align: top;
-}
-.aside.aside_flux {
-	background: #fff;
-}
-
-/*=== Aside main page (categories) */
-.categories {
-	list-style: none;
-	margin: 0;
-}
-.state_unread li:not(.active)[data-unread="0"] {
-	display: none;
-}
-.category {
-	display: block;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-}
-.category .btn:not([data-unread="0"]):after {
-	content: attr(data-unread);
-}
-
-/*=== Aside main page (feeds) */
-.categories .feeds {
-	width: 100%;
-	list-style: none;
-}
-.categories .feeds:not(.active) {
-	display: none;
-}
-.categories .feeds .feed {
-	display: inline-block;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-	vertical-align: middle;
-}
-.categories .feeds .feed:not([data-unread="0"]):before {
-	content: "(" attr(data-unread) ") ";
-}
-.categories .feeds .dropdown-menu {
-	left: 0;
-}
-.categories .feeds .item .dropdown-toggle > .icon {
-	visibility: hidden;
-	cursor: pointer;
-	vertical-align: top;
-}
-.categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon,
-.categories .feeds .item:hover .dropdown-toggle > .icon,
-.categories .feeds .item.active .dropdown-toggle > .icon {
-	visibility: visible;
-}
-
-/*=== New article notification */
-#new-article {
-	display: none;
-}
-#new-article > a {
-	display: block;
-}
-
-/*=== Day indication */
-.day .name {
-	position: absolute;
-	right: 0;
-	width: 50%;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-}
-
-/*=== Feed article header and footer */
-.flux_header {
-	position: relative;
-}
-.flux .item {
-	line-height: 40px;
-	white-space: nowrap;
-}
-.flux .item.manage,
-.flux .item.link {
-	width: 40px;
-	text-align: center;
-}
-.flux .item.website {
-	width: 200px;
-}
-.flux.not_read .item.title,
-.flux.current .item.title {
-	font-weight: bold;
-}
-.flux:not(.current):hover .item.title {
-	position: absolute;
-	max-width: calc(100% - 320px);
-	background: #fff;
-}
-.flux .item.title a {
-	color: #000;
-	text-decoration: none;
-}
-.flux .item.date {
-	width: 145px;
-	text-align: right;
-}
-.flux .item > a {
-	display: block;
-}
-.flux .item > a {
-	display: block;
-	text-decoration: none;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-	overflow: hidden;
-}
-.flux .item.share > a {
-	display: list-item;
-	list-style-position: inside;
-	list-style-type: decimal;
-}
-
-/*=== Feed article content */
-.hide_posts > .flux:not(.active) > .flux_content {
-	display: none;
-}
-.content {
-	min-height: 20em;
-	margin: auto;
-	line-height: 1.7em;
-	word-wrap: break-word;
-}
-.content.large {
-	max-width: 1000px;
-}
-.content.medium {
-	max-width: 800px;
-}
-.content.thin {
-	max-width: 550px;
-}
-.content ul,
-.content ol,
-.content dd {
-	margin: 0 0 0 15px;
-	padding: 0 0 5px 15px;
-}
-.content pre {
-	overflow: auto;
-}
-
-/*=== Notification and actualize notification */
-.notification {
-	position: absolute;
-	top: 1em;
-	left: 25%; right: 25%;
-	z-index: 10;
-	background: #fff;
-	border: 1px solid #aaa;
-}
-.notification.closed {
-	display: none;
-}
-.notification a.close {
-	position: absolute;
-	top: 0; bottom: 0;
-	right: 0;
-	display: inline-block;
-}
-
-#actualizeProgress {
-	position: fixed;
-}
-#actualizeProgress progress {
-	max-width: 100%;
-	vertical-align: middle;
-}
-#actualizeProgress .progress {
-	vertical-align: middle;
-}
-
-/*=== Navigation menu (for articles) */
-#nav_entries {
-	position: fixed;
-	bottom: 0; left: 0;
-	display: table;
-	width: 250px;
-	background: #fff;
-	table-layout: fixed;
-}
-#nav_entries .item {
-	display: table-cell;
-	width: 30%;
-}
-#nav_entries a {
-	display: block;
-}
-
-/*=== "Load more" part */
-#load_more {
-	min-height: 40px;
-}
-.loading {
-	background: url("loader.gif") center center no-repeat;
-	font-size: 0;
-}
-#bigMarkAsRead {
-	display: block;
-	padding: 3em 0;
-	text-align: center;
-}
-.bigTick {
-	font-size: 7em;
-	line-height: 1.6em;
-}
-
-/*=== Statistiques */
-.stat > table {
-	width: 100%;
-}
-
-/*=== GLOBAL VIEW */
-/*================*/
-/*=== Category boxes */
-#stream.global .box-category {
-	display: inline-block;
-	width: 19em;
-	max-width: 95%;
-	margin: 20px 10px;
-	border: 1px solid #ccc;
-	vertical-align: top;
-}
-#stream.global .category {
-	width: 100%;
-}
-#stream.global .btn {
-	display: block;
-}
-#stream.global .box-category .feeds {
-	display: block;
-	overflow: auto;
-}
-#stream.global .box-category .feed {
-	width: 19em;
-	max-width: 90%;
-}
-
-/*=== Panel */
-#overlay {
-	display: none;
-	position: fixed;
-	top: 0; bottom: 0;
-	left: 0; right: 0;
-	background: rgba(0, 0, 0, 0.9);
-}
-#panel {
-	display: none;
-	position: fixed;
-	top: 1em; bottom: 1em;
-	left: 2em; right: 2em;
-	overflow: auto;
-	background: #fff;
-}
-#panel .close {
-	position: fixed;
-	top: 0; bottom: 0;
-	left: 0; right: 0;
-	display: block;
-}
-#panel .close img {
-	display: none;
-}
-
-/*=== DIVERS */
-/*===========*/
-.nav-login,
-.nav_menu .search,
-.nav_menu .toggle_aside {
-	display: none;
-}
-
-.aside .toggle_aside {
-	position: absolute;
-	right: 0;
-	display: none;
-	width: 30px;
-	height: 30px;
-	line-height: 30px;
-	text-align: center;
-}
-
-/*=== MOBILE */
-/*===========*/
-@media(max-width: 840px) {
-	.header,
-	.aside .btn-important,
-	.aside .feeds .dropdown,
-	.flux_header .item.website span,
-	.item.date, .day .date,
-	.dropdown-menu > .no-mobile,
-	.no-mobile {
-		display: none;
-	}
-	.nav-login {
-		display: block;
-	}
-	.nav_menu .toggle_aside,
-	.aside .toggle_aside,
-	.nav_menu .search,
-	#panel .close img {
-		display: inline-block;
-	}
-
-	.aside {
-		position: fixed;
-		top: 0; bottom: 0;
-		left: 0;
-		width: 0;
-		overflow: hidden;
-		z-index: 100;
-	}
-	.aside:target {
-		width: 90%;
-		overflow: auto;
-	}
-	.aside .categories {
-		margin: 10px 0 75px;
-	}
-
-	.flux_header .item.website {
-		width: 40px;
-	}
-
-	.flux:not(.current):hover .item.title {
-		position: relative;
-		width: auto;
-		white-space: nowrap;
-	}
-
-	.notification {
-		top: 0;
-		left: 0;
-		right: 0;
-	}
-
-	#nav_entries {
-		width: 100%;
-	}
-
-	#stream.global .box-category {
-		margin: 10px 0;
-	}
-
-	#panel {
-		top: 0; bottom: 0;
-		left: 0; right: 0;
-	}
-	#panel .close {
-		top: 0; right: 0;
-		left: auto; bottom: auto;
-		display: inline-block;
-		width: 30px;
-		height: 30px;
-	}
-}
-
-/*=== PRINTER */
-/*============*/
-@media print {
-	.header, .aside,
-	.nav_menu, .day,
-	.flux_header,
-	.flux_content .bottom,
-	.pagination,
-	#nav_entries {
-		display: none;
-	}
-	html, body {
-		background: #fff;
-		color: #000;
-		font-family: Serif;
-	}
-	#global,
-	.flux_content {
-		display: block !important;
-	}
-	.flux_content .content {
-		width: 100% !important;
-	}
-	.flux_content .content a {
-		color: #000;
-	}
-	.flux_content .content a:after {
-		content: " [" attr(href) "] ";
-		font-style: italic;
-	}
-}

+ 33 - 7
p/themes/Flat/flat.css

@@ -15,7 +15,7 @@ html, body {
 }
 
 /*=== Links */
-a {
+a, button.as-link {
 	color: #2980b9;
 	outline: none;
 }
@@ -49,6 +49,7 @@ input, select, textarea {
 	background: #fff;
 	border: none;
 	border-bottom: 3px solid #ddd;
+	border-left-color: #ddd;
 	color: #666;
 	border-radius: 5px;
 }
@@ -155,10 +156,17 @@ form th {
 .stick .btn + .dropdown > .btn {
 	border-radius: 0 5px 5px 0;
 }
+.stick .btn + .btn,
 .stick .btn + input,
+.stick .btn + .dropdown > .btn,
+.stick input + .btn,
 .stick input + input,
-.stick .dropdown + input {
-	border-left: 1px solid #ddd;
+.stick input + .dropdown > .btn,
+.stick .dropdown + .btn,
+.stick .dropdown + input,
+.stick .dropdown + .dropdown > .btn {
+	border-left-width: 1px;
+	border-left-style: solid;
 }
 
 .btn {
@@ -175,6 +183,7 @@ form th {
 	border-radius: 5px;
 	border: none;
 	border-bottom: 3px solid #2980b9;
+	border-left-color: #2980b9;
 	color: #fff;
 }
 a.btn {
@@ -196,6 +205,7 @@ a.btn {
 	background: #e67e22;
 	color: #fff;
 	border-bottom: 3px solid #d35400;
+	border-left-color: #d35400;
 }
 .btn-important:hover,
 .btn-important:active {
@@ -206,6 +216,7 @@ a.btn {
 	background: #e74c3c;
 	color: #fff;
 	border-bottom: 3px solid #c0392b;
+	border-left-color: #c0392b;
 }
 .btn-attention:hover,
 .btn-attention:active {
@@ -327,17 +338,21 @@ a.btn {
 	padding: 0 25px;
 	line-height: 2.5em;
 }
-.dropdown-menu > .item > span {
-	padding: 0 25px;
+.dropdown-menu > .item > span,
+.dropdown-menu > .item > .as-link {
+	padding: 0 22px;
 	line-height: 2em;
 }
+.dropdown-menu > .item:hover {
+	background: #2980b9;
+	color: #fff;
+}
 .dropdown-menu > .item[aria-checked="true"] > a:before {
 	font-weight: bold;
 	margin: 0 0 0 -14px;
 }
 .dropdown-menu > .item:hover > a {
 	text-decoration: none;
-	background: #2980b9;
 	color: #fff;
 }
 .dropdown-menu .input select,
@@ -844,7 +859,18 @@ a.btn {
 .stat > table td,
 .stat > table th {
 	border-bottom: 1px solid #ddd;
-	text-align: center;
+}
+
+.stat > .horizontal-list {
+	margin: 0 0 5px;
+}
+.stat > .horizontal-list .item {
+	overflow: hidden;
+	white-space: nowrap;
+	text-overflow: ellipsis;
+}
+.stat > .horizontal-list .item:first-child {
+	width: 270px;
 }
 
 /*=== LOGS */

+ 1 - 1
p/themes/Flat/metadata.json

@@ -3,5 +3,5 @@
   "author": "Marien Fressinaud",
   "description": "Thème plat pour FreshRSS",
   "version": 0.2,
-  "files": ["template.css", "flat.css"]
+  "files": ["_template.css", "flat.css"]
 }

+ 0 - 698
p/themes/Flat/template.css

@@ -1,698 +0,0 @@
-@charset "UTF-8";
-
-/*=== GENERAL */
-/*============*/
-html, body {
-	margin: 0;
-	padding: 0;
-	font-size: 100%;
-}
-
-/*=== Links */
-a {
-	text-decoration: none;
-}
-a:hover {
-	text-decoration: underline;
-}
-
-/*=== Lists */
-ul, ol, dd {
-	margin: 0;
-	padding: 0;
-}
-
-/*=== Titles */
-h1 {
-	margin: 0.6em 0 0.3em;
-	font-size: 1.5em;
-	line-height: 1.6em;
-}
-h2 {
-	margin: 0.5em 0 0.25em;
-	font-size: 1.3em;
-	line-height: 2em;
-}
-h3 {
-	margin: 0.5em 0 0.25em;
-	font-size: 1.1em;
-	line-height: 2em;
-}
-
-/*=== Paragraphs */
-p {
-	margin: 1em 0 0.5em;
-	font-size: 1em;
-}
-
-/*=== Images */
-img {
-	height: auto;
-	max-width: 100%;
-}
-img.favicon {
-	height: 16px;
-	width: 16px;
-	vertical-align: middle;
-}
-
-/*=== Videos */
-iframe, embed, object, video {
-	max-width: 100%;
-}
-
-/*=== Forms */
-legend {
-	display: block;
-	width: 100%;
-	clear: both;
-}
-label {
-	display: block;
-}
-input {
-	width: 180px;
-}
-textarea {
-	width: 300px;
-}
-input, select, textarea {
-	display: inline-block;
-	max-width: 100%;
-}
-input[type="radio"],
-input[type="checkbox"] {
-	width: 15px !important;
-	min-height: 15px !important;
-}
-input.extend:focus {
-	width: 300px;
-}
-
-/*=== COMPONENTS */
-/*===============*/
-/*=== Forms */
-.form-group:after {
-	content: "";
-	display: block;
-	clear: both;
-}
-.form-group.form-actions {
-	min-width: 250px;
-}
-.form-group .group-name {
-	display: block;
-	float: left;
-	width: 200px;
-}
-.form-group .group-controls {
-	min-width: 250px;
-	margin: 0 0 0 220px;
-}
-.form-group .group-controls .control {
-	display: block;
-}
-
-/*=== Buttons */
-.stick {
-	display: inline-block;
-	white-space: nowrap;
-}
-.btn,
-a.btn {
-	display: inline-block;
-	cursor: pointer;
-	overflow: hidden;
-}
-.btn-important {
-	font-weight: bold;
-}
-
-/*=== Navigation */
-.nav-list .nav-header,
-.nav-list .item {
-	display: block;
-}
-.nav-list .item,
-.nav-list .item > a {
-	display: block;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-}
-.nav-head {
-	display: block;
-}
-.nav-head .item {
-	display: inline-block;
-}
-
-/*=== Horizontal-list */
-.horizontal-list {
-	display: table;
-	table-layout: fixed;
-	width: 100%;
-}
-.horizontal-list .item {
-	display: table-cell;
-}
-
-/*=== Dropdown */
-.dropdown {
-	position: relative;
-	display: inline-block;
-}
-.dropdown-target {
-	display: none;
-}
-.dropdown-menu {
-	display: none;
-	min-width: 200px;
-	margin: 0;
-	position: absolute;
-	right: 0;
-	background: #fff;
-	border: 1px solid #aaa;
-}
-.dropdown-header {
-	display: block;
-}
-.dropdown-menu > .item {
-	display: block;
-}
-.dropdown-menu > .item > a,
-.dropdown-menu > .item > span {
-	display: block;
-}
-.dropdown-menu > .item[aria-checked="true"] > a:before {
-	content: '✓';
-}
-.dropdown-menu .input {
-	display: block;
-}
-.dropdown-menu .input select,
-.dropdown-menu .input input {
-	display: block;
-	max-width: 95%;
-}
-.dropdown-target:target ~ .dropdown-menu {
-	display: block;
-	z-index: 10;
-}
-.dropdown-close {
-	display: inline;
-}
-.dropdown-close a {
-	font-size: 0;
-	position: fixed;
-	top: 0; bottom: 0;
-	left: 0; right: 0;
-	display: block;
-	z-index: -10;
-}
-.separator {
-	display: block;
-	height: 0;
-	border-bottom: 1px solid #aaa;
-}
-
-/*=== Alerts */
-.alert {
-	display: block;
-	width: 90%;
-}
-.group-controls .alert {
-	width: 100%
-}
-.alert-head {
-	margin: 0;
-	font-weight: bold;
-}
-.alert ul {
-	margin: 5px 20px;
-}
-
-/*=== Icons */
-.icon {
-	display: inline-block;
-	width: 16px;
-	height: 16px;
-	vertical-align: middle;
-	line-height: 16px;
-}
-
-/*=== Pagination */
-.pagination {
-	display: table;
-	width: 100%;
-	margin: 0;
-	padding: 0;
-	table-layout: fixed;
-}
-.pagination .item {
-	display: table-cell;
-}
-.pagination .pager-first,
-.pagination .pager-previous,
-.pagination .pager-next,
-.pagination .pager-last {
-	width: 100px;
-}
-
-/*=== STRUCTURE */
-/*===============*/
-/*=== Header */
-.header {
-	display: table;
-	width: 100%;
-	table-layout: fixed;
-}
-.header > .item {
-	display: table-cell;
-}
-.header > .item.title {
-	width: 250px;
-	white-space: nowrap;
-}
-.header > .item.title h1 {
-	display: inline-block;
-}
-.header > .item.title .logo {
-	display: inline-block;
-	height: 32px;
-	width: 32px;
-	vertical-align: middle;
-}
-.header > .item.configure {
-	width: 100px;
-}
-
-/*=== Body */
-#global {
-	display: table;
-	width: 100%;
-	height: 100%;
-	table-layout: fixed;
-}
-.aside {
-	display: table-cell;
-	height: 100%;
-	width: 250px;
-	vertical-align: top;
-}
-.aside.aside_flux {
-	background: #fff;
-}
-
-/*=== Aside main page (categories) */
-.categories {
-	list-style: none;
-	margin: 0;
-}
-.state_unread li:not(.active)[data-unread="0"] {
-	display: none;
-}
-.category {
-	display: block;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-}
-.category .btn:not([data-unread="0"]):after {
-	content: attr(data-unread);
-}
-
-/*=== Aside main page (feeds) */
-.categories .feeds {
-	width: 100%;
-	list-style: none;
-}
-.categories .feeds:not(.active) {
-	display: none;
-}
-.categories .feeds .feed {
-	display: inline-block;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-	vertical-align: middle;
-}
-.categories .feeds .feed:not([data-unread="0"]):before {
-	content: "(" attr(data-unread) ") ";
-}
-.categories .feeds .dropdown-menu {
-	left: 0;
-}
-.categories .feeds .item .dropdown-toggle > .icon {
-	visibility: hidden;
-	cursor: pointer;
-	vertical-align: top;
-}
-.categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon,
-.categories .feeds .item:hover .dropdown-toggle > .icon,
-.categories .feeds .item.active .dropdown-toggle > .icon {
-	visibility: visible;
-}
-
-/*=== New article notification */
-#new-article {
-	display: none;
-}
-#new-article > a {
-	display: block;
-}
-
-/*=== Day indication */
-.day .name {
-	position: absolute;
-	right: 0;
-	width: 50%;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-}
-
-/*=== Feed article header and footer */
-.flux_header {
-	position: relative;
-}
-.flux .item {
-	line-height: 40px;
-	white-space: nowrap;
-}
-.flux .item.manage,
-.flux .item.link {
-	width: 40px;
-	text-align: center;
-}
-.flux .item.website {
-	width: 200px;
-}
-.flux.not_read .item.title,
-.flux.current .item.title {
-	font-weight: bold;
-}
-.flux:not(.current):hover .item.title {
-	position: absolute;
-	max-width: calc(100% - 320px);
-	background: #fff;
-}
-.flux .item.title a {
-	color: #000;
-	text-decoration: none;
-}
-.flux .item.date {
-	width: 145px;
-	text-align: right;
-}
-.flux .item > a {
-	display: block;
-}
-.flux .item > a {
-	display: block;
-	text-decoration: none;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-	overflow: hidden;
-}
-.flux .item.share > a {
-	display: list-item;
-	list-style-position: inside;
-	list-style-type: decimal;
-}
-
-/*=== Feed article content */
-.hide_posts > .flux:not(.active) > .flux_content {
-	display: none;
-}
-.content {
-	min-height: 20em;
-	margin: auto;
-	line-height: 1.7em;
-	word-wrap: break-word;
-}
-.content.large {
-	max-width: 1000px;
-}
-.content.medium {
-	max-width: 800px;
-}
-.content.thin {
-	max-width: 550px;
-}
-.content ul,
-.content ol,
-.content dd {
-	margin: 0 0 0 15px;
-	padding: 0 0 5px 15px;
-}
-.content pre {
-	overflow: auto;
-}
-
-/*=== Notification and actualize notification */
-.notification {
-	position: absolute;
-	top: 1em;
-	left: 25%; right: 25%;
-	z-index: 10;
-	background: #fff;
-	border: 1px solid #aaa;
-}
-.notification.closed {
-	display: none;
-}
-.notification a.close {
-	position: absolute;
-	top: 0; bottom: 0;
-	right: 0;
-	display: inline-block;
-}
-
-#actualizeProgress {
-	position: fixed;
-}
-#actualizeProgress progress {
-	max-width: 100%;
-	vertical-align: middle;
-}
-#actualizeProgress .progress {
-	vertical-align: middle;
-}
-
-/*=== Navigation menu (for articles) */
-#nav_entries {
-	position: fixed;
-	bottom: 0; left: 0;
-	display: table;
-	width: 250px;
-	background: #fff;
-	table-layout: fixed;
-}
-#nav_entries .item {
-	display: table-cell;
-	width: 30%;
-}
-#nav_entries a {
-	display: block;
-}
-
-/*=== "Load more" part */
-#load_more {
-	min-height: 40px;
-}
-.loading {
-	background: url("loader.gif") center center no-repeat;
-	font-size: 0;
-}
-#bigMarkAsRead {
-	display: block;
-	padding: 3em 0;
-	text-align: center;
-}
-.bigTick {
-	font-size: 7em;
-	line-height: 1.6em;
-}
-
-/*=== Statistiques */
-.stat > table {
-	width: 100%;
-}
-
-/*=== GLOBAL VIEW */
-/*================*/
-/*=== Category boxes */
-#stream.global .box-category {
-	display: inline-block;
-	width: 19em;
-	max-width: 95%;
-	margin: 20px 10px;
-	border: 1px solid #ccc;
-	vertical-align: top;
-}
-#stream.global .category {
-	width: 100%;
-}
-#stream.global .btn {
-	display: block;
-}
-#stream.global .box-category .feeds {
-	display: block;
-	overflow: auto;
-}
-#stream.global .box-category .feed {
-	width: 19em;
-	max-width: 90%;
-}
-
-/*=== Panel */
-#overlay {
-	display: none;
-	position: fixed;
-	top: 0; bottom: 0;
-	left: 0; right: 0;
-	background: rgba(0, 0, 0, 0.9);
-}
-#panel {
-	display: none;
-	position: fixed;
-	top: 1em; bottom: 1em;
-	left: 2em; right: 2em;
-	overflow: auto;
-	background: #fff;
-}
-#panel .close {
-	position: fixed;
-	top: 0; bottom: 0;
-	left: 0; right: 0;
-	display: block;
-}
-#panel .close img {
-	display: none;
-}
-
-/*=== DIVERS */
-/*===========*/
-.nav-login,
-.nav_menu .search,
-.nav_menu .toggle_aside {
-	display: none;
-}
-
-.aside .toggle_aside {
-	position: absolute;
-	right: 0;
-	display: none;
-	width: 30px;
-	height: 30px;
-	line-height: 30px;
-	text-align: center;
-}
-
-/*=== MOBILE */
-/*===========*/
-@media(max-width: 840px) {
-	.header,
-	.aside .btn-important,
-	.aside .feeds .dropdown,
-	.flux_header .item.website span,
-	.item.date, .day .date,
-	.dropdown-menu > .no-mobile,
-	.no-mobile {
-		display: none;
-	}
-	.nav-login {
-		display: block;
-	}
-	.nav_menu .toggle_aside,
-	.aside .toggle_aside,
-	.nav_menu .search,
-	#panel .close img {
-		display: inline-block;
-	}
-
-	.aside {
-		position: fixed;
-		top: 0; bottom: 0;
-		left: 0;
-		width: 0;
-		overflow: hidden;
-		z-index: 100;
-	}
-	.aside:target {
-		width: 90%;
-		overflow: auto;
-	}
-	.aside .categories {
-		margin: 10px 0 75px;
-	}
-
-	.flux_header .item.website {
-		width: 40px;
-	}
-
-	.flux:not(.current):hover .item.title {
-		position: relative;
-		width: auto;
-		white-space: nowrap;
-	}
-
-	.notification {
-		top: 0;
-		left: 0;
-		right: 0;
-	}
-
-	#nav_entries {
-		width: 100%;
-	}
-
-	#stream.global .box-category {
-		margin: 10px 0;
-	}
-
-	#panel {
-		top: 0; bottom: 0;
-		left: 0; right: 0;
-	}
-	#panel .close {
-		top: 0; right: 0;
-		left: auto; bottom: auto;
-		display: inline-block;
-		width: 30px;
-		height: 30px;
-	}
-}
-
-/*=== PRINTER */
-/*============*/
-@media print {
-	.header, .aside,
-	.nav_menu, .day,
-	.flux_header,
-	.flux_content .bottom,
-	.pagination,
-	#nav_entries {
-		display: none;
-	}
-	html, body {
-		background: #fff;
-		color: #000;
-		font-family: Serif;
-	}
-	#global,
-	.flux_content {
-		display: block !important;
-	}
-	.flux_content .content {
-		width: 100% !important;
-	}
-	.flux_content .content a {
-		color: #000;
-	}
-	.flux_content .content a:after {
-		content: " [" attr(href) "] ";
-		font-style: italic;
-	}
-}

+ 1 - 1
p/themes/Origine/metadata.json

@@ -3,5 +3,5 @@
   "author": "Marien Fressinaud",
   "description": "Le thème par défaut pour FreshRSS",
   "version": 0.2,
-  "files": ["template.css", "origine.css"]
+  "files": ["_template.css", "origine.css"]
 }

+ 20 - 12
p/themes/Origine/origine.css

@@ -15,7 +15,7 @@ html, body {
 }
 
 /*=== Links */
-a {
+a, button.as-link {
 	color: #0062be;
 	outline: none;
 }
@@ -364,8 +364,9 @@ a.btn {
 	padding: 0 25px;
 	line-height: 2.5em;
 }
-.dropdown-menu > .item > span {
-	padding: 0 25px;
+.dropdown-menu > .item > span,
+.dropdown-menu > .item > .as-link {
+	padding: 0 22px;
 	line-height: 2em;
 }
 .dropdown-menu > .item:hover {
@@ -807,16 +808,12 @@ a.btn {
 	background: #fafafa;
 }
 #bigMarkAsRead:hover {
-	color: #000;
-	background: #ccc;
-	background: radial-gradient(circle at 50% -25% , #ccc 0%, #fafafa 50%);
-	background: -moz-radial-gradient(circle at 50% -25% , #ccc 0%, #fafafa 50%);
-	background: -webkit-radial-gradient(circle at 50% -25% , #ccc 0%, #fafafa 50%);
-	background: -o-radial-gradient(circle at 50% -25% , #ccc 0%, #fafafa 50%);
-	background: -ms-radial-gradient(circle at 50% -25% , #ccc 0%, #fafafa 50%);
+	color: #0062be;
+	background: #fff;
+	box-shadow: 0 -5px 10px #eee inset;
 }
 #bigMarkAsRead:hover .bigTick {
-	text-shadow: 0 0 10px #666;
+	text-shadow: 0 0 5px #0062be;
 }
 
 /*=== Navigation menu (for articles) */
@@ -916,7 +913,18 @@ a.btn {
 .stat > table td,
 .stat > table th {
 	border-bottom: 1px solid #ddd;
-	text-align: center;
+}
+
+.stat > .horizontal-list {
+	margin: 0 0 5px;
+}
+.stat > .horizontal-list .item {
+	overflow: hidden;
+	white-space: nowrap;
+	text-overflow: ellipsis;
+}
+.stat > .horizontal-list .item:first-child {
+	width: 270px;
 }
 
 /*=== LOGS */

+ 0 - 698
p/themes/Origine/template.css

@@ -1,698 +0,0 @@
-@charset "UTF-8";
-
-/*=== GENERAL */
-/*============*/
-html, body {
-	margin: 0;
-	padding: 0;
-	font-size: 100%;
-}
-
-/*=== Links */
-a {
-	text-decoration: none;
-}
-a:hover {
-	text-decoration: underline;
-}
-
-/*=== Lists */
-ul, ol, dd {
-	margin: 0;
-	padding: 0;
-}
-
-/*=== Titles */
-h1 {
-	margin: 0.6em 0 0.3em;
-	font-size: 1.5em;
-	line-height: 1.6em;
-}
-h2 {
-	margin: 0.5em 0 0.25em;
-	font-size: 1.3em;
-	line-height: 2em;
-}
-h3 {
-	margin: 0.5em 0 0.25em;
-	font-size: 1.1em;
-	line-height: 2em;
-}
-
-/*=== Paragraphs */
-p {
-	margin: 1em 0 0.5em;
-	font-size: 1em;
-}
-
-/*=== Images */
-img {
-	height: auto;
-	max-width: 100%;
-}
-img.favicon {
-	height: 16px;
-	width: 16px;
-	vertical-align: middle;
-}
-
-/*=== Videos */
-iframe, embed, object, video {
-	max-width: 100%;
-}
-
-/*=== Forms */
-legend {
-	display: block;
-	width: 100%;
-	clear: both;
-}
-label {
-	display: block;
-}
-input {
-	width: 180px;
-}
-textarea {
-	width: 300px;
-}
-input, select, textarea {
-	display: inline-block;
-	max-width: 100%;
-}
-input[type="radio"],
-input[type="checkbox"] {
-	width: 15px !important;
-	min-height: 15px !important;
-}
-input.extend:focus {
-	width: 300px;
-}
-
-/*=== COMPONENTS */
-/*===============*/
-/*=== Forms */
-.form-group:after {
-	content: "";
-	display: block;
-	clear: both;
-}
-.form-group.form-actions {
-	min-width: 250px;
-}
-.form-group .group-name {
-	display: block;
-	float: left;
-	width: 200px;
-}
-.form-group .group-controls {
-	min-width: 250px;
-	margin: 0 0 0 220px;
-}
-.form-group .group-controls .control {
-	display: block;
-}
-
-/*=== Buttons */
-.stick {
-	display: inline-block;
-	white-space: nowrap;
-}
-.btn,
-a.btn {
-	display: inline-block;
-	cursor: pointer;
-	overflow: hidden;
-}
-.btn-important {
-	font-weight: bold;
-}
-
-/*=== Navigation */
-.nav-list .nav-header,
-.nav-list .item {
-	display: block;
-}
-.nav-list .item,
-.nav-list .item > a {
-	display: block;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-}
-.nav-head {
-	display: block;
-}
-.nav-head .item {
-	display: inline-block;
-}
-
-/*=== Horizontal-list */
-.horizontal-list {
-	display: table;
-	table-layout: fixed;
-	width: 100%;
-}
-.horizontal-list .item {
-	display: table-cell;
-}
-
-/*=== Dropdown */
-.dropdown {
-	position: relative;
-	display: inline-block;
-}
-.dropdown-target {
-	display: none;
-}
-.dropdown-menu {
-	display: none;
-	min-width: 200px;
-	margin: 0;
-	position: absolute;
-	right: 0;
-	background: #fff;
-	border: 1px solid #aaa;
-}
-.dropdown-header {
-	display: block;
-}
-.dropdown-menu > .item {
-	display: block;
-}
-.dropdown-menu > .item > a,
-.dropdown-menu > .item > span {
-	display: block;
-}
-.dropdown-menu > .item[aria-checked="true"] > a:before {
-	content: '✓';
-}
-.dropdown-menu .input {
-	display: block;
-}
-.dropdown-menu .input select,
-.dropdown-menu .input input {
-	display: block;
-	max-width: 95%;
-}
-.dropdown-target:target ~ .dropdown-menu {
-	display: block;
-	z-index: 10;
-}
-.dropdown-close {
-	display: inline;
-}
-.dropdown-close a {
-	font-size: 0;
-	position: fixed;
-	top: 0; bottom: 0;
-	left: 0; right: 0;
-	display: block;
-	z-index: -10;
-}
-.separator {
-	display: block;
-	height: 0;
-	border-bottom: 1px solid #aaa;
-}
-
-/*=== Alerts */
-.alert {
-	display: block;
-	width: 90%;
-}
-.group-controls .alert {
-	width: 100%
-}
-.alert-head {
-	margin: 0;
-	font-weight: bold;
-}
-.alert ul {
-	margin: 5px 20px;
-}
-
-/*=== Icons */
-.icon {
-	display: inline-block;
-	width: 16px;
-	height: 16px;
-	vertical-align: middle;
-	line-height: 16px;
-}
-
-/*=== Pagination */
-.pagination {
-	display: table;
-	width: 100%;
-	margin: 0;
-	padding: 0;
-	table-layout: fixed;
-}
-.pagination .item {
-	display: table-cell;
-}
-.pagination .pager-first,
-.pagination .pager-previous,
-.pagination .pager-next,
-.pagination .pager-last {
-	width: 100px;
-}
-
-/*=== STRUCTURE */
-/*===============*/
-/*=== Header */
-.header {
-	display: table;
-	width: 100%;
-	table-layout: fixed;
-}
-.header > .item {
-	display: table-cell;
-}
-.header > .item.title {
-	width: 250px;
-	white-space: nowrap;
-}
-.header > .item.title h1 {
-	display: inline-block;
-}
-.header > .item.title .logo {
-	display: inline-block;
-	height: 32px;
-	width: 32px;
-	vertical-align: middle;
-}
-.header > .item.configure {
-	width: 100px;
-}
-
-/*=== Body */
-#global {
-	display: table;
-	width: 100%;
-	height: 100%;
-	table-layout: fixed;
-}
-.aside {
-	display: table-cell;
-	height: 100%;
-	width: 250px;
-	vertical-align: top;
-}
-.aside.aside_flux {
-	background: #fff;
-}
-
-/*=== Aside main page (categories) */
-.categories {
-	list-style: none;
-	margin: 0;
-}
-.state_unread li:not(.active)[data-unread="0"] {
-	display: none;
-}
-.category {
-	display: block;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-}
-.category .btn:not([data-unread="0"]):after {
-	content: attr(data-unread);
-}
-
-/*=== Aside main page (feeds) */
-.categories .feeds {
-	width: 100%;
-	list-style: none;
-}
-.categories .feeds:not(.active) {
-	display: none;
-}
-.categories .feeds .feed {
-	display: inline-block;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-	vertical-align: middle;
-}
-.categories .feeds .feed:not([data-unread="0"]):before {
-	content: "(" attr(data-unread) ") ";
-}
-.categories .feeds .dropdown-menu {
-	left: 0;
-}
-.categories .feeds .item .dropdown-toggle > .icon {
-	visibility: hidden;
-	cursor: pointer;
-	vertical-align: top;
-}
-.categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon,
-.categories .feeds .item:hover .dropdown-toggle > .icon,
-.categories .feeds .item.active .dropdown-toggle > .icon {
-	visibility: visible;
-}
-
-/*=== New article notification */
-#new-article {
-	display: none;
-}
-#new-article > a {
-	display: block;
-}
-
-/*=== Day indication */
-.day .name {
-	position: absolute;
-	right: 0;
-	width: 50%;
-	overflow: hidden;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-}
-
-/*=== Feed article header and footer */
-.flux_header {
-	position: relative;
-}
-.flux .item {
-	line-height: 40px;
-	white-space: nowrap;
-}
-.flux .item.manage,
-.flux .item.link {
-	width: 40px;
-	text-align: center;
-}
-.flux .item.website {
-	width: 200px;
-}
-.flux.not_read .item.title,
-.flux.current .item.title {
-	font-weight: bold;
-}
-.flux:not(.current):hover .item.title {
-	position: absolute;
-	max-width: calc(100% - 320px);
-	background: #fff;
-}
-.flux .item.title a {
-	color: #000;
-	text-decoration: none;
-}
-.flux .item.date {
-	width: 145px;
-	text-align: right;
-}
-.flux .item > a {
-	display: block;
-}
-.flux .item > a {
-	display: block;
-	text-decoration: none;
-	white-space: nowrap;
-	text-overflow: ellipsis;
-	overflow: hidden;
-}
-.flux .item.share > a {
-	display: list-item;
-	list-style-position: inside;
-	list-style-type: decimal;
-}
-
-/*=== Feed article content */
-.hide_posts > .flux:not(.active) > .flux_content {
-	display: none;
-}
-.content {
-	min-height: 20em;
-	margin: auto;
-	line-height: 1.7em;
-	word-wrap: break-word;
-}
-.content.large {
-	max-width: 1000px;
-}
-.content.medium {
-	max-width: 800px;
-}
-.content.thin {
-	max-width: 550px;
-}
-.content ul,
-.content ol,
-.content dd {
-	margin: 0 0 0 15px;
-	padding: 0 0 5px 15px;
-}
-.content pre {
-	overflow: auto;
-}
-
-/*=== Notification and actualize notification */
-.notification {
-	position: absolute;
-	top: 1em;
-	left: 25%; right: 25%;
-	z-index: 10;
-	background: #fff;
-	border: 1px solid #aaa;
-}
-.notification.closed {
-	display: none;
-}
-.notification a.close {
-	position: absolute;
-	top: 0; bottom: 0;
-	right: 0;
-	display: inline-block;
-}
-
-#actualizeProgress {
-	position: fixed;
-}
-#actualizeProgress progress {
-	max-width: 100%;
-	vertical-align: middle;
-}
-#actualizeProgress .progress {
-	vertical-align: middle;
-}
-
-/*=== Navigation menu (for articles) */
-#nav_entries {
-	position: fixed;
-	bottom: 0; left: 0;
-	display: table;
-	width: 250px;
-	background: #fff;
-	table-layout: fixed;
-}
-#nav_entries .item {
-	display: table-cell;
-	width: 30%;
-}
-#nav_entries a {
-	display: block;
-}
-
-/*=== "Load more" part */
-#load_more {
-	min-height: 40px;
-}
-.loading {
-	background: url("loader.gif") center center no-repeat;
-	font-size: 0;
-}
-#bigMarkAsRead {
-	display: block;
-	padding: 3em 0;
-	text-align: center;
-}
-.bigTick {
-	font-size: 7em;
-	line-height: 1.6em;
-}
-
-/*=== Statistiques */
-.stat > table {
-	width: 100%;
-}
-
-/*=== GLOBAL VIEW */
-/*================*/
-/*=== Category boxes */
-#stream.global .box-category {
-	display: inline-block;
-	width: 19em;
-	max-width: 95%;
-	margin: 20px 10px;
-	border: 1px solid #ccc;
-	vertical-align: top;
-}
-#stream.global .category {
-	width: 100%;
-}
-#stream.global .btn {
-	display: block;
-}
-#stream.global .box-category .feeds {
-	display: block;
-	overflow: auto;
-}
-#stream.global .box-category .feed {
-	width: 19em;
-	max-width: 90%;
-}
-
-/*=== Panel */
-#overlay {
-	display: none;
-	position: fixed;
-	top: 0; bottom: 0;
-	left: 0; right: 0;
-	background: rgba(0, 0, 0, 0.9);
-}
-#panel {
-	display: none;
-	position: fixed;
-	top: 1em; bottom: 1em;
-	left: 2em; right: 2em;
-	overflow: auto;
-	background: #fff;
-}
-#panel .close {
-	position: fixed;
-	top: 0; bottom: 0;
-	left: 0; right: 0;
-	display: block;
-}
-#panel .close img {
-	display: none;
-}
-
-/*=== DIVERS */
-/*===========*/
-.nav-login,
-.nav_menu .search,
-.nav_menu .toggle_aside {
-	display: none;
-}
-
-.aside .toggle_aside {
-	position: absolute;
-	right: 0;
-	display: none;
-	width: 30px;
-	height: 30px;
-	line-height: 30px;
-	text-align: center;
-}
-
-/*=== MOBILE */
-/*===========*/
-@media(max-width: 840px) {
-	.header,
-	.aside .btn-important,
-	.aside .feeds .dropdown,
-	.flux_header .item.website span,
-	.item.date, .day .date,
-	.dropdown-menu > .no-mobile,
-	.no-mobile {
-		display: none;
-	}
-	.nav-login {
-		display: block;
-	}
-	.nav_menu .toggle_aside,
-	.aside .toggle_aside,
-	.nav_menu .search,
-	#panel .close img {
-		display: inline-block;
-	}
-
-	.aside {
-		position: fixed;
-		top: 0; bottom: 0;
-		left: 0;
-		width: 0;
-		overflow: hidden;
-		z-index: 100;
-	}
-	.aside:target {
-		width: 90%;
-		overflow: auto;
-	}
-	.aside .categories {
-		margin: 10px 0 75px;
-	}
-
-	.flux_header .item.website {
-		width: 40px;
-	}
-
-	.flux:not(.current):hover .item.title {
-		position: relative;
-		width: auto;
-		white-space: nowrap;
-	}
-
-	.notification {
-		top: 0;
-		left: 0;
-		right: 0;
-	}
-
-	#nav_entries {
-		width: 100%;
-	}
-
-	#stream.global .box-category {
-		margin: 10px 0;
-	}
-
-	#panel {
-		top: 0; bottom: 0;
-		left: 0; right: 0;
-	}
-	#panel .close {
-		top: 0; right: 0;
-		left: auto; bottom: auto;
-		display: inline-block;
-		width: 30px;
-		height: 30px;
-	}
-}
-
-/*=== PRINTER */
-/*============*/
-@media print {
-	.header, .aside,
-	.nav_menu, .day,
-	.flux_header,
-	.flux_content .bottom,
-	.pagination,
-	#nav_entries {
-		display: none;
-	}
-	html, body {
-		background: #fff;
-		color: #000;
-		font-family: Serif;
-	}
-	#global,
-	.flux_content {
-		display: block !important;
-	}
-	.flux_content .content {
-		width: 100% !important;
-	}
-	.flux_content .content a {
-		color: #000;
-	}
-	.flux_content .content a:after {
-		content: " [" attr(href) "] ";
-		font-style: italic;
-	}
-}

+ 4 - 0
p/themes/Pafat/README.md

@@ -0,0 +1,4 @@
+Pafat
+=====
+
+Thème Pafat pour FreshRSS

+ 7 - 0
p/themes/Pafat/icons/all.svg

@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
+<g transform="translate(-40.0002,-746)" fill="#FFF">
+<rect style="color:#FFF;" height="2.0002" width="9.9996" y="749" x="43"/>
+<rect style="color:#FFF;" height="2.0002" width="9.9996" y="753" x="43"/>
+<rect style="color:#FFF;" height="2.0002" width="9.9996" y="757" x="43"/>
+</g>
+</svg>

+ 5 - 0
p/themes/Pafat/icons/bookmark.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
+<g transform="translate(-41.000202,-397)">
+<path style="enable-background:accumulate;color:#000000;" d="m530.95,186.71c-0.77941,0.55189-3.1576-1.906-4.1125-1.9179-0.95532-0.0119-3.3949,2.3858-4.161,1.8149-0.76573-0.57072,0.83698-3.592,0.55319-4.5039-0.2839-0.91223-3.3182-2.4915-3.0119-3.3965,0.30617-0.90461,3.6749-0.31399,4.4544-0.86567,0.77986-0.5519,1.3442-3.9257,2.2995-3.914,0.95494,0.0116,1.4342,3.398,2.1998,3.9689,0.76588,0.57114,4.1489,0.0653,4.4331,0.97746,0.28402,0.9118-2.7885,2.414-3.0949,3.3186-0.30652,0.90489,1.22,3.966,0.44027,4.5182z" fill-rule="nonzero" transform="matrix(1.0472113,-0.00871584,0.00871584,1.0472113,-504.35434,220.15425)" fill="#FFF"/>
+</g>
+</svg>

+ 1 - 1
p/themes/Screwdriver/icons/down.svg → p/themes/Pafat/icons/down.svg

@@ -1,5 +1,5 @@
 <svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
 <g transform="translate(-181.0002,-747)">
-<path style="block-progression:tb;color:#bebebe;direction:ltr;text-indent:0;text-align:start;enable-background:new;text-transform:none;" d="m195.03,751,0,1c-0.00091,0.0111,0.00059,0.021-0.00009,0.0312-0.0112,0.25496-0.12835,0.50994-0.31251,0.6875l-5.7188,6.2977-5.7188-6.2977c-0.18821-0.1881-0.28121-0.45346-0.28122-0.71875v-1h1c0.26531,0.00007,0.53059,0.0931,0.71873,0.28131l4.2812,4.829,4.2813-4.829c0.19464-0.21073,0.46925-0.30315,0.74998-0.2813z" fill-rule="nonzero" fill="#bebebe"/>
+<path style="block-progression:tb;color:#bebebe;direction:ltr;text-indent:0;text-align:start;enable-background:new;text-transform:none;" d="m195.03,751,0,1c-0.00091,0.0111,0.00059,0.021-0.00009,0.0312-0.0112,0.25496-0.12835,0.50994-0.31251,0.6875l-5.7188,6.2977-5.7188-6.2977c-0.18821-0.1881-0.28121-0.45346-0.28122-0.71875v-1h1c0.26531,0.00007,0.53059,0.0931,0.71873,0.28131l4.2812,4.829,4.2813-4.829c0.19464-0.21073,0.46925-0.30315,0.74998-0.2813z" fill-rule="nonzero" fill="#666"/>
 </g>
 </svg>

+ 2 - 2
p/themes/Screwdriver/icons/icon.svg → p/themes/Pafat/icons/icon.svg

@@ -1,7 +1,7 @@
 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
 	<title>Logo FreshRSS</title>
-	<circle fill="#0062BE" cx="128" cy="128" r="33"/>
-	<g fill="none" stroke="#0062BE" stroke-width="24">
+	<circle fill="#C5C6CA" cx="128" cy="128" r="33"/>
+	<g fill="none" stroke="#C5C6CA" stroke-width="24">
 		<g stroke-opacity="0.3">
 			<path d="M12,128 A116,116 0 1,1 128,244"/>
 			<path d="M54,128 A74,74 0 1,1 128,202"/>

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 4 - 0
p/themes/Pafat/icons/link.svg


+ 2 - 2
p/themes/Screwdriver/icons/login.svg → p/themes/Pafat/icons/login.svg

@@ -1,6 +1,6 @@
 <svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g transform="translate(-181.0002,-237)" fill="#bebebe">
-<path style="color:#bebebe;" d="m184,244c-0.554,0-1,0.446-1,1v0.53125,5.4688h12v-5.4688-0.53c0-0.554-0.446-1-1-1h-10z" fill-rule="nonzero"/>
+<g transform="translate(-181.0002,-237)" fill="#666">
+<path style="color:#666;" d="m184,244c-0.554,0-1,0.446-1,1v0.53125,5.4688h12v-5.4688-0.53c0-0.554-0.446-1-1-1h-10z" fill-rule="nonzero"/>
 <path style="baseline-shift:baseline;block-progression:tb;color:#bebebe;direction:ltr;text-indent:0;text-align:start;enable-background:accumulate;text-transform:none;" d="m188,238c-1.6447,0-3,1.3553-3,3v7c0,1.6447,1.3553,3,3,3h2c1.6447,0,3-1.3553,3-3v-7c0-1.6447-1.3553-3-3-3h-2zm0,2,2,0c0.5713,0,1,0.4287,1,1v7c0,0.5713-0.4287,1-1,1h-2c-0.5713,0-1-0.4287-1-1v-7c0-0.5713,0.4287-1,1-1z"/>
 </g>
 </svg>

+ 2 - 2
p/themes/Screwdriver/icons/logout.svg → p/themes/Pafat/icons/logout.svg

@@ -1,6 +1,6 @@
 <svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g transform="translate(-201.0002,-237)" fill="#bebebe">
-<path style="color:#bebebe;" d="m204,246c-0.554,0-1,0.446-1,1v0.53125,5.4688h12v-5.4688-0.53c0-0.554-0.446-1-1-1h-10z" fill-rule="nonzero"/>
+<g transform="translate(-201.0002,-237)" fill="#666">
+<path style="color:#666;" d="m204,246c-0.554,0-1,0.446-1,1v0.53125,5.4688h12v-5.4688-0.53c0-0.554-0.446-1-1-1h-10z" fill-rule="nonzero"/>
 <path style="baseline-shift:baseline;block-progression:tb;color:#bebebe;direction:ltr;text-indent:0;text-align:start;enable-background:accumulate;text-transform:none;" d="m208,237c-1.6447,0-3,1.3553-3,3v3h2v-3c0-0.57129,0.42873-1,1-1h2c0.57127,0,1,0.42871,1,1v7h2v-7c0-1.6447-1.3553-3-3-3h-2z"/>
 </g>
 </svg>

+ 1 - 1
p/themes/Screwdriver/icons/next.svg → p/themes/Pafat/icons/next.svg

@@ -1,5 +1,5 @@
 <svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
 <g transform="translate(-121.0002,-747)">
-<path style="block-progression:tb;color:#bebebe;direction:ltr;text-indent:0;text-align:start;enable-background:new;text-transform:none;" d="m125,749,1,0c0.0104-0.00012,0.0208-0.00046,0.0313,0,0.25495,0.0112,0.50987,0.12858,0.6875,0.3125l6.2977,5.7188-6.2977,5.7188c-0.18816,0.18819-0.45346,0.28125-0.71875,0.28125h-1v-1c0-0.26529,0.0931-0.53058,0.28125-0.71875l4.829-4.2812-4.829-4.2812c-0.21074-0.19463-0.30316-0.46925-0.28125-0.75z" fill-rule="nonzero" fill="#bebebe"/>
+<path style="block-progression:tb;color:#bebebe;direction:ltr;text-indent:0;text-align:start;enable-background:new;text-transform:none;" d="m125,749,1,0c0.0104-0.00012,0.0208-0.00046,0.0313,0,0.25495,0.0112,0.50987,0.12858,0.6875,0.3125l6.2977,5.7188-6.2977,5.7188c-0.18816,0.18819-0.45346,0.28125-0.71875,0.28125h-1v-1c0-0.26529,0.0931-0.53058,0.28125-0.71875l4.829-4.2812-4.829-4.2812c-0.21074-0.19463-0.30316-0.46925-0.28125-0.75z" fill-rule="nonzero" fill="#666"/>
 </g>
 </svg>

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 2 - 0
p/themes/Pafat/icons/non-starred.svg


+ 5 - 0
p/themes/Pafat/icons/prev.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
+<g transform="translate(-301.0002,-747)">
+<path style="block-progression:tb;color:#666;direction:ltr;text-indent:0;text-align:start;enable-background:new;text-transform:none;" d="m313.01,749-1,0c-0.0104-0.00012-0.0208-0.00046-0.0313,0-0.25495,0.0112-0.50987,0.12858-0.6875,0.3125l-6.2977,5.7188,6.2977,5.7188c0.18816,0.18819,0.45346,0.28125,0.71875,0.28125h1v-1c0-0.26529-0.0931-0.53058-0.28125-0.71875l-4.829-4.2812,4.829-4.2812c0.21074-0.19463,0.30316-0.46925,0.28125-0.75z" fill-rule="nonzero" fill="#666"/>
+</g>
+</svg>

+ 5 - 0
p/themes/Pafat/icons/read.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="16.001" width="16">
+<g transform="translate(-60.99995,-296.9989)">
+<path opacity="1" style="baseline-shift:baseline;block-progression:tb;color:#000000;direction:ltr;text-indent:0;text-align:start;enable-background:accumulate;text-transform:none;" fill="#666" d="m68.875,297a1.0001,1.0001,0,0,0,-0.5,0.25l-4.9062,4a1.0001,1.0001,0,0,0,-0.0625,0.0312s-0.32587,0.29728-0.65625,0.75c-0.22334,0.30605-0.3527,0.8316-0.5,1.3125a1.0001,1.0001,0,0,0,-0.03125,0.0312,1.0001,1.0001,0,0,0,-0.21875,0.5625c-0.00051,0.0118,0.00036,0.0195,0,0.0312a1.0001,1.0001,0,0,0,0,0.0312,1.0001,1.0001,0,0,0,0,0.15625v7.8438a1.0001,1.0001,0,0,0,1,1h12a1.0001,1.0001,0,0,0,1,-1v-7.8438a1.0001,1.0001,0,0,0,0,-0.15625,1.0001,1.0001,0,0,0,-0.21875,-0.65625,1.0001,1.0001,0,0,0,-0.03125,-0.0312c-0.32774-1.1879-1.125-2-1.125-2a1.0001,1.0001,0,0,0,-0.0312,-0.0312l-4.969-4.02a1.0001,1.0001,0,0,0,-0.65625,-0.25,1.0001,1.0001,0,0,0,-0.0937,0zm0.125,2.2812,4.3125,3.5312,0.0312,0.0312c0.021,0.0255,0.18032,0.24952,0.34375,0.5l-4.6874,3.5312-4.6875-3.5312c0.0259-0.0394,0.0349-0.0872,0.0625-0.125,0.1908-0.26146,0.31874-0.41421,0.34375-0.4375l0.03125-0.0312,4.25-3.4688zm-5,5.0938,4.6875,3.5312,0.3125,0.21875,0.3125-0.21875,4.6875-3.5312,0,6.625-10,0,0-6.625z"/>
+</g>
+</svg>

+ 1 - 1
p/themes/Screwdriver/icons/share.svg → p/themes/Pafat/icons/share.svg

@@ -1,5 +1,5 @@
 <svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g fill="#bebebe" transform="translate(-581.0002,-196)">
+<g fill="#666" transform="translate(-581.0002,-196)">
 <path style="enable-background:new;color:#000000;" d="m291,178.03c0,1.0873-0.88144,1.9688-1.9688,1.9688-1.0873,0-1.9688-0.88144-1.9688-1.9688,0-1.0873,0.88144-1.9688,1.9688-1.9688,1.0873,0,1.9688,0.88144,1.9688,1.9688z" fill-rule="nonzero" transform="matrix(1.5079365,0,0,1.5079365,148.15963,-64.49107)"/>
 <path style="enable-background:new;color:#000000;" d="m291,178.03c0,1.0873-0.88144,1.9688-1.9688,1.9688-1.0873,0-1.9688-0.88144-1.9688-1.9688,0-1.0873,0.88144-1.9688,1.9688-1.9688,1.0873,0,1.9688,0.88144,1.9688,1.9688z" fill-rule="nonzero" transform="matrix(1.5079365,0,0,1.5079365,158.12818,-59.49107)"/>
 <path style="enable-background:new;color:#000000;" d="m291,178.03c0,1.0873-0.88144,1.9688-1.9688,1.9688-1.0873,0-1.9688-0.88144-1.9688-1.9688,0-1.0873,0.88144-1.9688,1.9688-1.9688,1.0873,0,1.9688,0.88144,1.9688,1.9688z" fill-rule="nonzero" transform="matrix(1.5079365,0,0,1.5079365,158.12818,-69.49107)"/>

+ 5 - 0
p/themes/Pafat/icons/starred.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
+<g transform="translate(-41.000202,-397)">
+<path style="enable-background:accumulate;color:#000000;" d="m530.95,186.71c-0.77941,0.55189-3.1576-1.906-4.1125-1.9179-0.95532-0.0119-3.3949,2.3858-4.161,1.8149-0.76573-0.57072,0.83698-3.592,0.55319-4.5039-0.2839-0.91223-3.3182-2.4915-3.0119-3.3965,0.30617-0.90461,3.6749-0.31399,4.4544-0.86567,0.77986-0.5519,1.3442-3.9257,2.2995-3.914,0.95494,0.0116,1.4342,3.398,2.1998,3.9689,0.76588,0.57114,4.1489,0.0653,4.4331,0.97746,0.28402,0.9118-2.7885,2.414-3.0949,3.3186-0.30652,0.90489,1.22,3.966,0.44027,4.5182z" fill-rule="nonzero" transform="matrix(1.0472113,-0.00871584,0.00871584,1.0472113,-504.35434,220.15425)" fill="#666"/>
+</g>
+</svg>

+ 1 - 1
p/themes/Screwdriver/icons/tag.svg → p/themes/Pafat/icons/tag.svg

@@ -1,5 +1,5 @@
 <svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
 <g transform="translate(-141.0002,-807)">
-<path d="m149,809,0,13,4-4,4,4c0.0525-6.8494-0.0285-10.584,0-13z" fill="#bebebe"/>
+<path d="m149,809,0,13,4-4,4,4c0.0525-6.8494-0.0285-10.584,0-13z" fill="#666"/>
 </g>
 </svg>

+ 6 - 0
p/themes/Pafat/icons/unread.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
+<g transform="translate(-40.99995,-297)" fill="#666">
+<path style="block-progression:tb;color:#000000;direction:ltr;text-indent:0;text-align:start;enable-background:accumulate;text-transform:none;" d="m43.781,301a1.0001,1.0001,0,0,0,-0.40625,1.7812l5,4,0.625,0.5,0.625-0.5,5-4a1.0005,1.0005,0,1,0,-1.25,-1.5625l-4.375,3.5-4.375-3.5a1.0001,1.0001,0,0,0,-0.844,-0.22z"/>
+<path style="block-progression:tb;color:#000000;direction:ltr;text-indent:0;text-align:start;enable-background:accumulate;text-transform:none;" d="M42.906,300a1.0001,1.0001,0,0,0,-0.906,1v9a1.0001,1.0001,0,0,0,1,1h12a1.0001,1.0001,0,0,0,1,-1v-9a1.0001,1.0001,0,0,0,-1,-1h-12a1.0001,1.0001,0,0,0,-0.09375,0zm1.094,2h10v7h-10v-7z"/>
+</g>
+</svg>

+ 5 - 0
p/themes/Pafat/icons/up.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
+<g transform="translate(-201.0002,-747)">
+<path style="block-progression:tb;color:#666;direction:ltr;text-indent:0;text-align:start;enable-background:new;text-transform:none;" d="m215.03,759,0-1c-0.00091-0.0111,0.00059-0.021-0.00009-0.0312-0.0112-0.25496-0.12835-0.50994-0.31251-0.6875l-5.7188-6.2977-5.7188,6.2977c-0.18821,0.1881-0.28121,0.45346-0.28122,0.71875v1h1c0.26531-0.00007,0.53059-0.0931,0.71873-0.28131l4.2812-4.829,4.2813,4.829c0.19464,0.21073,0.46925,0.30315,0.74998,0.2813z" fill-rule="nonzero" fill="#666"/>
+</g>
+</svg>

BIN
p/themes/Pafat/loader.gif


+ 7 - 0
p/themes/Pafat/metadata.json

@@ -0,0 +1,7 @@
+{
+  "name": "Pafat",
+  "author": "Plopoyop",
+  "description": "Un thème pour FreshRSS",
+  "version": 0.2,
+  "files": ["_template.css", "pafat.css"]
+}

+ 1084 - 0
p/themes/Pafat/pafat.css

@@ -0,0 +1,1084 @@
+@charset "UTF-8";
+
+/*=== FONTS */
+@font-face {
+	font-family: "OpenSans";
+	src: url("../fonts/openSans.woff") format("woff");
+}
+
+/*=== GENERAL */
+/*============*/
+html, body {
+	height: 100%;
+	font-family: "OpenSans", "Cantarell", "Helvetica", "Arial", sans-serif;
+	background: #fafafa;
+	color : #666;
+}
+
+/*=== Links */
+a {
+	color: #2980b9;
+	outline: none;
+}
+
+/*=== Forms */
+legend {
+	margin: 20px 0 5px;
+	padding: 5px 0;
+	border-bottom: 1px solid #ddd;
+	font-size: 1.4em;
+}
+label {
+	min-height: 25px;
+	padding: 5px 0;
+	cursor: pointer;
+}
+textarea {
+	width: 360px;
+	height: 100px;
+}
+input, select, textarea {
+	min-height: 25px;
+	padding: 1px;
+	background: #fdfdfd;
+	border: 1px solid #bbb;
+	border-radius: 3px;
+	color: #666;
+	line-height: 21px;
+	vertical-align: middle;
+}
+
+select{
+	height:29px;
+}
+option {
+	padding: 0 .5em;
+}
+input:focus, select:focus, textarea:focus {
+	 outline-color: #aaa;
+}
+
+input:invalid, select:invalid {
+	border-color: #f00;
+	box-shadow: 0 0 2px 2px #fdd inset;
+	 outline-color: #fdd;
+}
+input:disabled, select:disabled {
+	background: #eee;
+}
+input.extend {
+	transition: width 200ms linear;
+	-moz-transition: width 200ms linear;
+	-webkit-transition: width 200ms linear;
+	-o-transition: width 200ms linear;
+	-ms-transition: width 200ms linear;
+}
+
+/*=== Tables */
+table {
+	border-collapse: collapse;
+}
+
+tr, th, td {
+	padding: 0.5em;
+	border: 1px solid #ddd;
+}
+th {
+	background: #f6f6f6;
+}
+form td,
+form th {
+	font-weight: normal;
+	text-align: center;
+}
+
+/*=== COMPONENTS */
+/*===============*/
+/*=== Forms */
+.form-group.form-actions {
+	padding: 5px 0;
+	background: #f4f4f4;
+	border-top: 1px solid #ddd;
+}
+.form-group.form-actions .btn {
+	margin: 0 10px;
+}
+.form-group .group-name {
+	padding: 10px 0;
+	text-align: right;
+}
+.form-group .group-controls {
+	min-height: 25px;
+	padding: 5px 0;
+	margin : 10px 0 10px 220px;
+
+}
+.form-group table {
+	margin: 10px 0 0 220px;
+}
+
+/*=== Buttons */
+.stick {
+	vertical-align: middle;
+	font-size: 0;
+	min-width: 215px;
+}
+.stick input,
+.stick .btn {
+	border-radius: 0;
+}
+.stick .btn:first-child,
+.stick input:first-child {
+	border-radius: 3px 0 0 3px;
+}
+.stick .btn-important:first-child {
+	width:176px;
+}
+.stick .btn:last-child,
+.stick input:last-child {
+	border-radius: 0 3px 3px 0;
+}
+.stick .btn + .btn,
+.stick .btn + input,
+.stick .btn + .dropdown > .btn,
+.stick input + .btn,
+.stick input + input,
+.stick input + .dropdown > .btn,
+.stick .dropdown + .btn,
+.stick .dropdown + input,
+.stick .dropdown + .dropdown > .btn {
+	border-left: none;
+
+}	
+	
+.stick .btn + .dropdown > .btn {
+	border-left: none;
+	border-radius: 0 3px 3px 0;
+}
+
+.btn {
+	display: inline-block;
+	min-height: 29px;
+	min-width: 15px;
+	margin: 0;
+	padding: 1px 5px;
+	background: #fff;
+	border-radius: 3px;
+	border: 1px solid #aaa;
+	color: #666;
+	font-size: 0.9rem;
+	vertical-align: middle;
+	cursor: pointer;
+	overflow: hidden;
+}
+
+a.btn {
+	min-height: 25px;
+	line-height: 25px;
+}
+
+
+a.btn {
+	min-height: 25px;
+	line-height: 25px;
+}
+
+.read_all.btn {
+	height:29px;
+}
+
+.btn:hover {
+	background: #f0f0f0;
+	text-decoration: none;
+}
+
+
+.category.stick .btn {
+	background:#5bc0de;
+	color : #FFF;
+	border-color :#5bc0de;
+}
+
+.category.stick .btn:first-child:hover, .category.stick .btn:last-child:hover, .category.stick .btn.active:first-child, .category.stick.active .btn:last-child {
+	background:#39b3d7;
+	border-color : #39b3d7;
+}
+
+
+.btn.active,
+.btn:active,
+.dropdown-target:target ~ .btn.dropdown-toggle {
+	background: #eee;
+}
+
+.category.all > .btn  {
+	background: #428bca;
+	color : #FFF;
+	border-color : #428bca;
+}
+
+.category.all > .btn:hover  {
+	background: #3276b1;
+	border-color : #3276b1;
+}
+
+.category.favorites > .btn {
+	background:#f0ad4e;
+	border-color: #f0ad4e;
+	color : #fff;
+}
+
+.category.favorites > .btn:hover {
+	background: #ed9c28;
+	border-color : #ed9c28;
+	color : white;
+}
+
+.btn-important {
+	background: #5cb85c;
+	color: #fff;
+	border-color: #5cb85c;
+	font-weight: normal;
+}
+.btn-important:hover, .btn-important:active {
+	background:#47a447;
+	border-color : #47a447;
+	box-shadow: none;
+}
+
+.btn-attention {
+	background: #d9534f;
+	color: #fff;
+	border: 1px solid #d9534f;
+	outline-color : #aaa;
+}
+.btn-attention:hover {
+	background: #d2322d;
+	border-color : #d2322d;
+}
+.btn-attention:active {
+	background: #d2322d;
+	box-shadow: none;
+}
+
+/*=== Navigation */
+.nav-list .nav-header,
+.nav-list .item {
+	height: 2.5em;
+	line-height: 2.5em;
+	font-size: 0.9rem;
+}
+.nav-list .item:hover {
+	background: #fafafa;
+}
+.nav-list .item:hover a {
+	color: #003388;
+}
+.nav-list .item.active {
+	background: #3498DB;
+	color: #fff;
+}
+.nav-list .item.active a {
+	color: #fff;
+}
+.nav-list .disable {
+	color: #aaa;
+	background: #fafafa;
+	text-align: center;
+}
+.nav-list .item > a {
+	padding: 0 10px;
+}
+.nav-list a:hover {
+	text-decoration: none;
+}
+.nav-list .item.empty a {
+	color: #f39c12;
+}
+.nav-list .item.active.empty a {
+	color: #fff;
+	background: #f39c12;
+}
+.nav-list .item.error a {
+	color: #BD362F;
+}
+.nav-list .item.active.error a {
+	color: #fff;
+	background: #BD362F;
+}
+
+.nav-list .nav-header {
+	padding: 0 10px;
+	color: #888;
+	background: #f4f4f4;
+	border-bottom: 1px solid #ddd;
+	font-weight: bold;
+}
+
+.nav-list .nav-form {
+	padding: 3px;
+	text-align: center;
+}
+
+.nav-head {
+	margin: 0;
+	background: #fff;
+	background: linear-gradient(to bottom, #fff, #f0f0f0);
+	background: -moz-linear-gradient(top, #fff 0%, #f0f0f0 100%);
+	background: -webkit-linear-gradient(top, #fff 0%, #f0f0f0 100%);
+	background: -o-linear-gradient(top, #fff 0%, #f0f0f0 100%);
+	background: -ms-linear-gradient(top, #fff 0%, #f0f0f0 100%);
+	border-bottom: 1px solid #ddd;
+	text-align: right;
+}
+.nav-head .item {
+	padding: 5px 10px;
+	font-size: 0.9rem;
+	line-height: 1.5rem;
+}
+
+/*=== Horizontal-list */
+.horizontal-list {
+	margin: 0;
+	padding: 0;
+}
+.horizontal-list .item {
+	vertical-align: middle;
+}
+
+/*=== Dropdown */
+.dropdown-menu {
+	margin: 5px 0 0;
+	padding: 5px 0;
+	border: 1px solid #aaa;
+	border-radius: 5px;
+	font-size: 0.8rem;
+	text-align: left;
+}
+.dropdown-menu:after {
+	content: "";
+	position: absolute;
+	top: -6px;
+	right: 13px;
+	width: 10px;
+	height: 10px;
+	background: #fff;
+	border-top: 1px solid #aaa;
+	border-left: 1px solid #aaa;
+	z-index: -10;
+	transform: rotate(45deg);
+	-moz-transform: rotate(45deg);
+	-webkit-transform: rotate(45deg);
+	-ms-transform: rotate(45deg);
+}
+.dropdown-header {
+	padding: 0 5px 5px;
+	color: #888;
+	font-weight: bold;
+	text-align: left;
+}
+.dropdown-menu > .item {
+}
+
+.dropdown-menu > .item > a {
+	padding: 0 22px;
+	line-height: 2.5em;
+	color: #666;
+	font-size: 0.8rem;
+}
+
+.dropdown-menu > .item > span,
+.dropdown-menu > .item > .as-link {
+	padding: 0 22px;
+	line-height: 2em;
+	font-size: 0.8rem;
+}
+
+.dropdown-menu > .item:hover {
+	background: #eee;
+	color: #666;
+}
+.dropdown-menu > .item[aria-checked="true"] > a:before {
+	font-weight: bold;
+	margin: 0 0 0 -14px;
+}
+.dropdown-menu > .item:hover > a {
+	color: #666;
+	text-decoration: none;
+}
+.dropdown-menu .input select,
+.dropdown-menu .input input {
+	margin: 0 auto 5px;
+	padding: 2px 5px;
+	border-radius: 3px;
+}
+
+.separator {
+	margin: 5px 0;
+	border-bottom: 1px solid #ddd;
+}
+
+/*=== Alerts */
+.alert {
+	margin: 15px auto;
+	padding: 10px 15px;
+	background: #f4f4f4;
+	border: 1px solid #ccc;
+	border-right: 1px solid #aaa;
+	border-bottom: 1px solid #aaa;
+	border-radius: 5px;
+	color: #aaa;
+	font-size: 0.9em;
+}
+.alert-head {
+	font-size: 1.15em;
+}
+.alert > a {
+	color: inherit;
+	text-decoration: underline;
+}
+.alert-warn {
+	background: #ffe;
+	border: 1px solid #eeb;
+	color: #c95;
+}
+.alert-success {
+	background: #dfd;
+	border: 1px solid #cec;
+	color: #484;
+}
+.alert-error {
+	background: #fdd;
+	border: 1px solid #ecc;
+	color: #844;
+}
+
+/*=== Pagination */
+.pagination {
+	background: #fff;
+	text-align: center;
+	color: #41444f;
+	font-size: 0.8em;
+}
+.content .pagination {
+	margin: 0;
+	padding: 0;
+}
+.pagination .item.pager-current {
+	font-weight: bold;
+	font-size: 1.5em;
+}
+.pagination .item a {
+	display: block;
+	color: #41444f;
+	font-style: italic;
+	line-height: 3em;
+	text-decoration: none;
+}
+.pagination .item a:hover {
+	background: #ddd;
+}
+.pagination:first-child .item {
+	border-bottom: 1px solid #aaa;
+}
+.pagination:last-child .item {
+	border-top: 1px solid #aaa;
+}
+
+.pagination .loading,
+.pagination a:hover.loading {
+	background: url("loader.gif") center center no-repeat #fff;
+	font-size: 0;
+}
+
+/*=== STRUCTURE */
+/*===============*/
+/*=== Header */
+.header {
+	height: 85px;
+	background: #41444f;
+}
+.header > .item {
+	padding: 10px;
+	border-bottom: 1px solid #aaa;
+	vertical-align: middle;
+	text-align: center;
+}
+.header > .item.title{
+	width: 230px;
+}
+.header > .item.title h1 {
+	margin: 0.5em 0;
+}
+
+.header > .item.title h1 a, a.signin {
+	text-decoration: none;
+	color : #C5C6CA;
+}
+ 
+.header > .item.search input {
+	width: 230px;
+	height : 29px;
+	-moz-box-sizing: border-box;
+	-webkit-box-sizing: border-box;
+	box-sizing: border-box;
+}
+
+.header > .item.search button {
+	-moz-box-sizing: border-box;
+	-webkit-box-sizing: border-box;
+	box-sizing: border-box;
+	height : 29px;
+}
+
+.header .item.search input:focus {
+	width: 350px;
+}
+
+/*=== Body */
+#global {
+	height: calc(100% - 85px);
+}
+.aside {
+	border-right: 1px solid #aaa;
+	background: #fff;
+}
+.aside.aside_flux {
+	padding: 10px 0 50px;
+}
+
+/*=== Aside main page (categories) */
+.categories {
+	text-align: center;
+}
+.category {
+	width: 215px;
+	margin: 10px auto;
+	text-align: left;
+}
+.category .btn:first-child {
+	position: relative;
+	width: 203px;
+}
+.category.stick .btn:first-child {
+	width: 176px;
+}
+.category .btn:first-child:not([data-unread="0"]):after {
+	position: absolute;
+	top: 2px; right: 3px;
+	padding: 0px 3px;
+	border: 1px solid ;
+	border-radius: 3px;
+	font-size:10pt;
+	line-height : 20px;
+}
+
+/*=== Aside main page (feeds) */
+.categories .feeds .item.active {
+	background: #5cb85c;
+}
+.categories .feeds .item.active .feed {
+	color: #fff;
+}
+.categories .feeds .item.empty .feed {
+	color: #e67e22;
+}
+.categories .feeds .item.empty.active {
+	background: #e67e22;
+}
+.categories .feeds .item.empty.active .feed {
+	color: #fff;
+}
+.categories .feeds .item.error .feed {
+	color: #BD362F;
+}
+.categories .feeds .item .feed {
+	margin: 0;
+	width: 165px;
+	line-height: 3em;
+	font-size: 0.8em;
+	text-align: left;
+	text-decoration: none;
+}
+.categories .feeds .feed:not([data-unread="0"]) {
+	font-weight: bold;
+}
+.categories .feeds .dropdown-menu:after {
+	left: 2px;
+}
+.categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon,
+.categories .feeds .item:hover .dropdown-toggle > .icon,
+.categories .feeds .item.active .dropdown-toggle > .icon {
+	background-color: #fff;
+	border-radius: 3px;
+	vertical-align: middle;
+}
+
+/*=== Configuration pages */
+.post {
+	padding: 10px 50px;
+	font-size: 0.9em;
+}
+.post form {
+	margin: 10px 0;
+}
+.post.content {
+	max-width: 550px;
+}
+
+/*=== Prompt (centered) */
+.prompt {
+	text-align: center;
+}
+.prompt label {
+	text-align: left;
+}
+.prompt form {
+	margin: 10px auto 20px auto;
+	width: 200px;
+}
+.prompt input {
+	margin: 5px auto;
+	width: 100%;
+}
+.prompt p {
+	margin: 20px 0;
+}
+
+/*=== New article notification */
+#new-article {
+	background: #428bca;
+	text-align: center;
+	font-size: 0.9em;
+}
+#new-article:hover {
+	background: #3276b1;
+}
+#new-article > a {
+	line-height: 3em;
+	color: #fff;
+	font-weight: bold;
+}
+#new-article > a:hover {
+	text-decoration: none;
+}
+
+/*=== Day indication */
+.day {
+	padding: 0 10px;
+	font-weight: bold;
+	line-height: 3em;
+	background: #fff;
+	border-top: 1px solid #aaa;
+	border-bottom: 1px solid #aaa;
+	color : #666;
+}
+#new-article + .day {
+	border-top: none;
+}
+.day .name {
+	padding: 0 10px 0 0;
+	color : #666;
+	font-size: 1.8em;
+	opacity: 0.3;
+	font-style: italic;
+	text-align: right;
+}
+
+/*=== Index menu */
+.nav_menu {
+	background: #fafafa;
+	border-bottom: 1px solid #aaa;
+	text-align: center;
+	padding: 5px 0;
+}
+
+/*=== Feed articles */
+.flux {
+	border-left: 3px solid #5cb85c;
+	background: #fafafa;
+}
+.flux:hover {
+	background: #fff;
+}
+.flux.current {
+	border-left: 3px solid #39b3d7;
+}
+.flux.not_read {
+	border-left: 3px solid #d9534f;
+}
+.flux .item.title a, .flux.not_read:not(.current):hover .item.title {
+	color : #333;
+}
+.flux.favorite {
+	border-left: 2px solid #428bca;
+	background: #FFF6DA;
+}
+.flux.favorite:not(.current):hover .item.title {
+	background: #FFF6DA;
+}
+.flux.current {
+	background: #fff;
+}
+
+
+.flux_header {
+	border-top: 1px solid #ddd;
+	font-size: 0.8rem;
+	cursor: pointer;
+}
+.flux_header .title {
+	font-size: 0.9rem;
+}
+.flux .website .favicon {
+	padding: 5px;
+}
+.flux .date {
+	color: #666;
+	font-size: 0.7rem;
+}
+
+.flux .bottom {
+	font-size: 0.8rem;
+	text-align: center;
+}
+
+/*=== Content of feed articles */
+.content {
+	padding: 20px 10px;
+}
+.content > h1.title > a {
+	color: #333;
+}
+
+.content hr {
+	margin: 30px 10px;
+	height: 1px;
+	background: #ddd;
+	border: 0;
+	box-shadow: 0 2px 5px #ccc;
+}
+
+.content pre {
+	margin: 10px auto;
+	padding: 10px 20px;
+	overflow: auto;
+	background: #222;
+	color: #fff;
+	font-size: 0.9rem;
+	border-radius: 3px;
+}
+.content code {
+	padding: 2px 5px;
+	color: #dd1144;
+	background: #fafafa;
+	border: 1px solid #eee;
+	border-radius: 3px;
+}
+.content pre code {
+	background: transparent;
+	color: #fff;
+	border: none;
+}
+
+.content blockquote {
+	display: block;
+	margin: 0;
+	padding: 5px 20px;
+	border-top: 1px solid #ddd;
+	border-bottom: 1px solid #ddd;
+	background: #fafafa;
+	color: #41444f;
+}
+.content blockquote p {
+	margin: 0;
+}
+
+/*=== Notification and actualize notification */
+.notification {
+	padding: 0 0 0 5px;
+	text-align: center;
+	border: 1px solid #eeb;
+	border-radius: 3px;
+	box-shadow: 0 0 5px #ddd;
+	font-weight: bold;
+	font-size: 0.9em;
+	line-height: 3em;
+	z-index: 10;
+	vertical-align: middle;
+}
+.notification.good {
+	background: #ffe;
+	border: 1px solid #eeb;
+	color: #c95;
+}
+.notification.bad {
+	background: #fdd;
+	border: 1px solid #ecc;
+	color: #844;
+}
+.notification a.close {
+	padding: 0 15px;
+	line-height: 3em;
+}
+.notification.good a.close:hover {
+	background: #eeb;
+}
+.notification.bad a.close:hover {
+	background: #ecc;
+}
+
+.notification#actualizeProgress {
+	line-height: 2em;
+}
+
+/*=== "Load more" part */
+#bigMarkAsRead {
+	text-align: center;
+	text-decoration: none;	
+	color: #666;
+	background: #fafafa;
+}
+#bigMarkAsRead:hover {
+	color: #000;
+	background: #f0f0f0;
+}
+
+#bigMarkAsRead:hover .bigTick {
+/*	text-shadow: 0 0 10px #666;*/
+}
+
+/*=== Navigation menu (for articles) */
+#nav_entries {
+	margin: 0;
+	background: #fff;
+	border-top: 1px solid #ddd;
+	text-align: center;
+	line-height: 3em;
+	table-layout: fixed;
+}
+
+#nav_entries .item:hover {
+	background:#eee	;
+}
+/*=== READER VIEW */
+/*================*/
+#stream.reader .flux {
+	padding: 0 0 50px;
+	border: none;
+	background: #f0f0f0;
+	color: #41444f;
+}
+#stream.reader .flux .author {
+	margin: 0 0 10px;
+	font-size: 90%;
+	color: #666;
+}
+
+/*=== GLOBAL VIEW */
+/*================*/
+#stream.global .box-category {
+	background: #fff;
+	border:none;
+	text-align: left;
+}
+
+#stream.global .category {
+	margin: 0;
+}
+
+#stream.global .category:first-child {
+	margin: 0;
+}
+
+
+#stream.global .btn {
+	width: auto;
+	height: 2em;
+	margin: 0;
+	padding: 0 10px;
+	background: #f6f6f6;
+	border-bottom: 1px solid #aaa;
+	border-radius: 5px 5px 0 0;
+	line-height: 2em;
+	font-size: 1.2rem;
+}
+
+#stream.global .btn:not([data-unread="0"]) {
+	background: #5bc0de;
+	border-color : #5bc0de;
+	color: #fff;
+	font-weight: bold;
+	text-shadow: none;
+
+}
+
+
+#stream.global .btn:first-child:not([data-unread="0"]):after {
+	top: 0; right: 5px;
+	border: 0;
+	background: none;
+	color: #fff;
+	font-weight: bold;
+	box-shadow: none;
+	text-shadow: none;
+}
+
+#stream.global .box-category .feeds {
+	max-height: 250px;
+	width: 302px;
+	border : solid #aaa 1px;
+	border-top : none;
+}
+
+#stream.global .box-category .feeds .item {
+	padding: 2px 10px;
+	font-size: 0.9rem;
+}
+
+/*=== DIVERS */
+/*===========*/
+.aside.aside_feed .nav-form input,
+.aside.aside_feed .nav-form select {
+	width: 140px;
+}
+.aside.aside_feed .nav-form .dropdown .dropdown-menu {
+	right: -20px;
+}
+.aside.aside_feed .nav-form .dropdown .dropdown-menu:after {
+	right: 33px;
+}
+
+/*=== STATISTICS */
+/*===============*/
+.stat {
+	margin: 10px 0 20px;
+}
+
+.stat th,
+.stat td,
+.stat tr {
+	border: none;
+}
+.stat > table td,
+.stat > table th {
+	border-bottom: 1px solid #ddd;
+	text-align: center;
+}
+.stat > .horizontal-list {
+	margin: 0 0 5px;
+}
+.stat > .horizontal-list .item {
+	overflow: hidden;
+	white-space: nowrap;
+	text-overflow: ellipsis;
+}
+.stat > .horizontal-list .item:first-child {
+	width: 250px;
+}
+
+/*=== LOGS */
+/*=========*/
+.logs {
+	border: 1px solid #aaa;
+	border-radius: 5px;
+	overflow: hidden;
+}
+.log {
+	padding: 5px 10px;
+	background: #fafafa;
+	color: #41444f;
+	font-size: 0.8rem;
+}
+.log+.log {
+	border-top: 1px solid #aaa;
+}
+.log .date {
+	display: block;
+	font-weight: bold;
+}
+.log.error {
+	background: #fdd;
+	color: #844;
+}
+.log.warning {
+	background: #ffe;
+	color: #c95;
+}
+.log.notice {
+	background: #f4f4f4;
+	color: #aaa;
+}
+.log.debug {
+	background: #41444f;
+	color: #eee;
+}
+
+/*=== MOBILE */
+/*===========*/
+@media(max-width: 840px) {
+	.aside {
+		box-shadow: 3px 0 3px #aaa;
+		transition: width 200ms linear;
+		-moz-transition: width 200ms linear;
+		-webkit-transition: width 200ms linear;
+		-o-transition: width 200ms linear;
+		-ms-transition: width 200ms linear;
+	}
+	.aside .toggle_aside,
+	#panel .close {
+		position: absolute;
+		display: block;
+		top: 0; right: 0;
+		width: 30px;
+		height: 30px;
+		line-height: 30px;
+		text-align: center;
+		background: #f6f6f6;
+		border-left: 1px solid #ddd;
+		border-bottom: 1px solid #ddd;
+		border-radius: 0 0 0 5px;
+	}
+
+	.nav_menu .btn {
+		margin: 5px 10px;
+	}
+	.nav_menu .stick {
+		margin: 0 10px;
+	}
+	.nav_menu .stick .btn {
+		margin: 5px 0;
+	}
+	.nav_menu .search {
+		display: inline-block;
+		max-width: 97%;
+	}
+	.nav_menu .search input {
+		max-width: 97%;
+		width: 90px;
+	}
+	.nav_menu .search input:focus {
+		width: 400px;
+	}
+
+	.day .name {
+		font-size: 1.1rem;
+		text-shadow: none;
+	}
+
+	.pagination {
+		margin: 0 0 3.5em;
+	}
+
+	.notification a.close {
+		display: block;
+		left: 0;
+		background: transparent;
+	}
+	.notification a.close:hover {
+		opacity: 0.5;
+	}
+	.notification a.close .icon {
+		display: none;
+	}
+}

+ 0 - 5
p/themes/Screwdriver/icons/add.svg

@@ -1,5 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g transform="translate(-60.0002,-726)">
-<path style="color:#666666;" fill="#666" d="m67,729,0,4-4,0,0,2,4,0,0,4,2,0,0-4,4,0,0-2-4,0,0-4-2,0z"/>
-</g>
-</svg>

+ 0 - 7
p/themes/Screwdriver/icons/all.svg

@@ -1,7 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g transform="translate(-40.0002,-746)" fill="#bebebe">
-<rect style="color:#bebebe;" height="2.0002" width="9.9996" y="749" x="43"/>
-<rect style="color:#bebebe;" height="2.0002" width="9.9996" y="753" x="43"/>
-<rect style="color:#bebebe;" height="2.0002" width="9.9996" y="757" x="43"/>
-</g>
-</svg>

BIN
p/themes/Screwdriver/icons/apple-touch-icon.png


+ 0 - 6
p/themes/Screwdriver/icons/bookmark-add.svg

@@ -1,6 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g transform="translate(-141.0002,-807)" fill="#bebebe">
-<path d="m143,807,0,13,4-4,4,4,0-4,0-1-2,0,0-4,2,0,0-4z"/>
-<path d="m152,810,0,2-2,0,0,2,2,0,0,2,2,0,0-2,2,0,0-2-2,0,0-2-2,0z"/>
-</g>
-</svg>

+ 5 - 60
p/themes/Screwdriver/icons/bookmark.svg

@@ -1,60 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   height="16"
-   width="16"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="bookmark.svg">
-  <metadata
-     id="metadata12">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <defs
-     id="defs10" />
-  <sodipodi:namedview
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1"
-     objecttolerance="10"
-     gridtolerance="10"
-     guidetolerance="10"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:window-width="1366"
-     inkscape:window-height="745"
-     id="namedview8"
-     showgrid="false"
-     inkscape:zoom="14.75"
-     inkscape:cx="-2.2033898"
-     inkscape:cy="8"
-     inkscape:window-x="0"
-     inkscape:window-y="0"
-     inkscape:window-maximized="1"
-     inkscape:current-layer="svg2" />
-  <g
-     transform="translate(-41.000202,-397)"
-     id="g4">
-    <path
-       style="enable-background:accumulate;color:#000000;fill:#d18104;fill-opacity:1"
-       d="m530.95,186.71c-0.77941,0.55189-3.1576-1.906-4.1125-1.9179-0.95532-0.0119-3.3949,2.3858-4.161,1.8149-0.76573-0.57072,0.83698-3.592,0.55319-4.5039-0.2839-0.91223-3.3182-2.4915-3.0119-3.3965,0.30617-0.90461,3.6749-0.31399,4.4544-0.86567,0.77986-0.5519,1.3442-3.9257,2.2995-3.914,0.95494,0.0116,1.4342,3.398,2.1998,3.9689,0.76588,0.57114,4.1489,0.0653,4.4331,0.97746,0.28402,0.9118-2.7885,2.414-3.0949,3.3186-0.30652,0.90489,1.22,3.966,0.44027,4.5182z"
-       fill-rule="nonzero"
-       transform="matrix(1.0472113,-0.00871584,0.00871584,1.0472113,-504.35434,220.15425)"
-       fill="#f1c40f"
-       id="path6" />
-  </g>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
+<g transform="translate(-41.000202,-397)">
+<path style="color:#000000;enable-background:accumulate;" d="m530.95,186.71c-0.77941,0.55189-3.1576-1.906-4.1125-1.9179-0.95532-0.0119-3.3949,2.3858-4.161,1.8149-0.76573-0.57072,0.83698-3.592,0.55319-4.5039-0.2839-0.91223-3.3182-2.4915-3.0119-3.3965,0.30617-0.90461,3.6749-0.31399,4.4544-0.86567,0.77986-0.5519,1.3442-3.9257,2.2995-3.914,0.95494,0.0116,1.4342,3.398,2.1998,3.9689,0.76588,0.57114,4.1489,0.0653,4.4331,0.97746,0.28402,0.9118-2.7885,2.414-3.0949,3.3186-0.30652,0.90489,1.22,3.966,0.44027,4.5182z" fill-rule="nonzero" transform="matrix(1.0472113,-0.00871584,0.00871584,1.0472113,-504.35434,220.15425)" fill="#d18104"/>
+</g>
+</svg>

+ 0 - 7
p/themes/Screwdriver/icons/category-white.svg

@@ -1,7 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g transform="translate(-442,-176)">
-<g transform="translate(234.0002,-820)">
-<path d="m208.53,997c-0.28913,0-0.53125,0.24212-0.53125,0.53125v13.938c0,0.2985,0.23264,0.5312,0.53125,0.5312h14.938c0.2986,0,0.53125-0.2326,0.53125-0.5312v-8.9376c0-0.2891-0.24212-0.5312-0.53125-0.5312h-12.469v7.5c0,0.277-0.223,0.5-0.5,0.5s-0.5-0.223-0.5-0.5v-8c0-0.277,0.223-0.5,0.5-0.5h2.9688,8.5312v-1.4062c0-0.3272-0.26666-0.5938-0.59375-0.5938h-7.4062v-1.4688c0-0.39-0.24-0.63-0.53-0.63z" fill="#FFF"/>
-</g>
-</g>
-</svg>

+ 0 - 7
p/themes/Screwdriver/icons/category.svg

@@ -1,7 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g transform="translate(-442,-176)">
-<g transform="translate(234.0002,-820)">
-<path d="m208.53,997c-0.28913,0-0.53125,0.24212-0.53125,0.53125v13.938c0,0.2985,0.23264,0.5312,0.53125,0.5312h14.938c0.2986,0,0.53125-0.2326,0.53125-0.5312v-8.9376c0-0.2891-0.24212-0.5312-0.53125-0.5312h-12.469v7.5c0,0.277-0.223,0.5-0.5,0.5s-0.5-0.223-0.5-0.5v-8c0-0.277,0.223-0.5,0.5-0.5h2.9688,8.5312v-1.4062c0-0.3272-0.26666-0.5938-0.59375-0.5938h-7.4062v-1.4688c0-0.39-0.24-0.63-0.53-0.63z" fill="#666"/>
-</g>
-</g>
-</svg>

+ 0 - 7
p/themes/Screwdriver/icons/close.svg

@@ -1,7 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g transform="translate(-60,-518)">
-<g transform="translate(19,-242)">
-<path style="block-progression:tb;color:#bebebe;direction:ltr;text-indent:0;text-align:start;enable-background:new;text-transform:none;" d="m45,764,1,0c0.01037-0.00012,0.02079-0.00046,0.03125,0,0.25495,0.0112,0.50987,0.12858,0.6875,0.3125l2.282,2.28,2.312-2.28c0.266-0.23,0.447-0.3,0.688-0.31h1v1c0,0.28647-0.03434,0.55065-0.25,0.75l-2.2812,2.2812,2.25,2.25c0.188,0.19,0.281,0.45,0.281,0.72v1h-1c-0.2653-0.00001-0.53059-0.0931-0.71875-0.28125l-2.281-2.28-2.281,2.28c-0.188,0.19-0.454,0.28-0.719,0.28h-1v-1c-0.000003-0.26529,0.09306-0.53058,0.28125-0.71875l2.2812-2.25-2.281-2.28c-0.21-0.19-0.303-0.47-0.281-0.75v-1z" fill-rule="nonzero" fill="#bebebe"/>
-</g>
-</g>
-</svg>

+ 0 - 5
p/themes/Screwdriver/icons/configure.svg

@@ -1,5 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g transform="translate(-441.0002,-400.99999)">
-<path style="color:#666666;enable-background:accumulate;" d="m449,402c-0.22065,0-0.44081,0.0113-0.65625,0.0312l-0.40625,2.0938c-0.33446,0.0733-0.66305,0.17589-0.96875,0.3125l-1.5312-1.4688c-0.38863,0.23011-0.72695,0.51408-1.0625,0.8125l0.90625,1.9062c-0.22242,0.24899-0.42425,0.5225-0.59375,0.8125l-2.0938-0.28125c-0.17772,0.40877-0.30872,0.83637-0.40625,1.2812l1.8438,1c-0.0171,0.16809-0.0312,0.3274-0.0312,0.5s0.0142,0.33191,0.0312,0.5l-1.8438,1c0.0975,0.44488,0.22853,0.87248,0.40625,1.2812l2.0938-0.28125c0.1695,0.29,0.37133,0.56351,0.59375,0.8125l-0.90625,1.9062c0.33555,0.29842,0.67387,0.58239,1.0625,0.8125l1.5312-1.4688c0.3057,0.13661,0.63429,0.23916,0.96875,0.3125l0.40625,2.0938c0.21544,0.02,0.4356,0.0312,0.65625,0.0312s0.44081-0.0113,0.65625-0.0312l0.40625-2.0938c0.33446-0.0733,0.66305-0.17589,0.96875-0.3125l1.5312,1.4688c0.38863-0.23011,0.72695-0.51408,1.0625-0.8125l-0.90625-1.9062c0.22242-0.24899,0.42425-0.5225,0.59375-0.8125l2.0938,0.28125c0.17772-0.40877,0.30872-0.83637,0.40625-1.2812l-1.8438-1c0.0171-0.16809,0.0312-0.3274,0.0312-0.5s-0.0142-0.33191-0.0312-0.5l1.8438-1c-0.0975-0.44488-0.22853-0.87248-0.40625-1.2812l-2.0938,0.28125c-0.1695-0.29-0.37133-0.56351-0.59375-0.8125l0.90625-1.9062c-0.33555-0.29842-0.67387-0.58239-1.0625-0.8125l-1.5312,1.4688c-0.3057-0.13661-0.63429-0.23916-0.96875-0.3125l-0.40625-2.0938c-0.21544-0.02-0.4356-0.0312-0.65625-0.0312zm0,4c1.6568,0,3,1.3432,3,3s-1.3432,3-3,3-3-1.3432-3-3,1.3432-3,3-3z" fill-rule="nonzero" fill="#666"/>
-</g>
-</svg>

BIN
p/themes/Screwdriver/icons/favicon-16-32-48-64.ico


BIN
p/themes/Screwdriver/icons/favicon-256.png


+ 0 - 13
p/themes/Screwdriver/icons/favicon.svg

@@ -1,13 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
-	<title>Logo FreshRSS</title>
-	<circle fill="#FFF" cx="128" cy="128" r="128"/>
-	<circle fill="#0062BE" cx="128" cy="128" r="33"/>
-	<g fill="none" stroke="#0062BE" stroke-width="24">
-		<g stroke-opacity="0.3">
-			<path d="M12,128 A116,116 0 1,1 128,244"/>
-			<path d="M54,128 A74,74 0 1,1 128,202"/>
-		</g>
-		<path d="M128,12 A116,116 0 0,1 244,128"/>
-		<path d="M128,54 A74,74 0 0,1 202,128"/>
-	</g>
-</svg>

BIN
p/themes/Screwdriver/icons/grey.gif


+ 0 - 7
p/themes/Screwdriver/icons/help.svg

@@ -1,7 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g transform="translate(-182,-490)" fill="#bebebe">
-<path style="block-progression:tb;color:#bebebe;direction:ltr;text-indent:0;text-align:start;enable-background:accumulate;text-transform:none;" d="m190,490c-4.4147,0-8,3.5853-8,8s3.5853,8,8,8,8-3.5853,8-8-3.5853-8-8-8zm0,2c3.3413,0,6,2.6587,6,6s-2.6587,6-6,6-6-2.6587-6-6,2.6587-6,6-6z"/>
-<path style="block-progression:tb;color:#bebebe;direction:ltr;text-indent:0;text-align:start;enable-background:accumulate;text-transform:none;" d="M189.34,495c-1.28,0-2.34,1.06-2.34,2.34v1.3125c0,1.2861,1.0576,2.3438,2.3438,2.3438h1.3125c1.29,0.01,2.35-1.05,2.35-2.33v-1.3125c0-1.29-1.06-2.35-2.34-2.35h-1.3125zm0,1,1.3125,0c0.74942,0,1.3438,0.59433,1.3438,1.3438v1.3125c0.01,0.76-0.58,1.35-1.33,1.35h-1.3125c-0.76,0-1.35-0.59-1.35-1.34v-1.3125c0-0.76,0.59-1.35,1.34-1.35z"/>
-<path d="m186.72,491.44c-1.5103,0.6073-2.6811,1.7985-3.2812,3.3125l3.75,1.875c0.25196-0.64029,0.74249-1.1706,1.375-1.4375l-1.8438-3.75zm6.5625,0-1.8438,3.75c0.63251,0.26694,1.123,0.79721,1.375,1.4375l3.75-1.875c-0.60015-1.514-1.7709-2.7052-3.2812-3.3125zm-6.0938,8-3.75,1.875c0.60709,1.4886,1.789,2.65,3.2812,3.25l1.875-3.75c-0.62682-0.25556-1.1433-0.75203-1.4062-1.375zm5.625,0c-0.26291,0.62297-0.77943,1.1194-1.4062,1.375l1.875,3.75c1.4923-0.60005,2.6742-1.7614,3.2812-3.25l-3.75-1.875z"/>
-</g>
-</svg>

+ 0 - 7
p/themes/Screwdriver/icons/key.svg

@@ -1,7 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(-340.99994,-257)" fill="#666666">
-<path style="block-progression:tb;color:#000000;direction:ltr;text-indent:0;text-align:start;enable-background:accumulate;text-transform:none;" d="m346,260c-2.7496,0-5,2.2504-5,5s2.2504,5,5,5c1.5862,0,2.9034-0.84459,3.8125-2h4.8438,0.75l0.21875-0.75,1.0312-4,0.3125-1.25h-1.2812-5.875c-0.90914-1.1554-2.2263-2-3.8125-2zm0,2c1.1158,0,2.0379,0.59507,2.5625,1.5l0.3125,0.5h0.5625,4.9688l-0.53125,2h-4.4375-0.5625l-0.3125,0.5c-0.52462,0.90493-1.4466,1.5-2.5625,1.5-1.6687,0-3-1.3313-3-3s1.3313-3,3-3z"/>
-<path opacity="0.35" style="enable-background:accumulate;color:#000000;" d="M355.5,265,350,265,349.44,267,355,267z" fill-rule="nonzero"/>
-<path style="enable-background:accumulate;color:#000000;" d="m346,265c0,0.55228-0.44772,1-1,1s-1-0.44772-1-1,0.44772-1,1-1,1,0.44772,1,1z" fill-rule="nonzero"/>
-</g>
-</svg>

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 4
p/themes/Screwdriver/icons/link.svg


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 2
p/themes/Screwdriver/icons/non-starred.svg


+ 0 - 5
p/themes/Screwdriver/icons/prev.svg

@@ -1,5 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
-<g transform="translate(-301.0002,-747)">
-<path style="block-progression:tb;color:#bebebe;direction:ltr;text-indent:0;text-align:start;enable-background:new;text-transform:none;" d="m313.01,749-1,0c-0.0104-0.00012-0.0208-0.00046-0.0313,0-0.25495,0.0112-0.50987,0.12858-0.6875,0.3125l-6.2977,5.7188,6.2977,5.7188c0.18816,0.18819,0.45346,0.28125,0.71875,0.28125h1v-1c0-0.26529-0.0931-0.53058-0.28125-0.71875l-4.829-4.2812,4.829-4.2812c0.21074-0.19463,0.30316-0.46925,0.28125-0.75z" fill-rule="nonzero" fill="#bebebe"/>
-</g>
-</svg>

+ 3 - 57
p/themes/Screwdriver/icons/read.svg

@@ -1,57 +1,3 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   height="16.001"
-   width="16"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="read.svg">
-  <metadata
-     id="metadata12">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <defs
-     id="defs10" />
-  <sodipodi:namedview
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1"
-     objecttolerance="10"
-     gridtolerance="10"
-     guidetolerance="10"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:window-width="1366"
-     inkscape:window-height="745"
-     id="namedview8"
-     showgrid="false"
-     inkscape:zoom="14.749079"
-     inkscape:cx="-2.2040272"
-     inkscape:cy="8.0004997"
-     inkscape:window-x="0"
-     inkscape:window-y="0"
-     inkscape:window-maximized="1"
-     inkscape:current-layer="svg2" />
-  <g
-     transform="translate(-60.99995,-296.9989)"
-     id="g4" />
-  <path
-     style="fill:#cccccc;fill-opacity:1"
-     inkscape:connector-curvature="0"
-     d="m 8.0004996,3.2833392 c -3.433907,0 -6.410294,1.9996259 -7.87290101,4.9205634 1.46260701,2.9209364 4.43899401,4.9205624 7.87290101,4.9205624 3.4338474,0 6.4102344,-1.999626 7.8729014,-4.9205624 C 14.410824,5.2829651 11.434347,3.2833392 8.0004996,3.2833392 z m 3.8818634,2.6094965 c 0.925096,0.590068 1.709004,1.3804357 2.29781,2.3110669 -0.588806,0.9306312 -1.372744,1.7209988 -2.29784,2.3110964 -1.162392,0.741404 -2.5047194,1.133295 -3.8818334,1.133295 -1.377143,0 -2.719472,-0.391891 -3.881863,-1.133326 -0.925066,-0.5900366 -1.708974,-1.3804016 -2.29781,-2.3110654 0.588806,-0.9306638 1.372744,-1.7210288 2.29781,-2.3110669 0.06025,-0.038442 0.121108,-0.075682 0.182338,-0.1122479 -0.153123,0.4202145 -0.236925,0.873738 -0.236925,1.3469419 0,2.1740274 1.762423,3.9364493 3.93645,3.9364493 2.1740274,0 3.9364514,-1.7624219 3.9364514,-3.9364493 0,-0.4732039 -0.0838,-0.9267274 -0.236925,-1.3469745 0.0612,0.036566 0.122061,0.073839 0.182337,0.1122805 z M 8.0004996,6.6354719 c 0,0.8152761 -0.660894,1.4761705 -1.476168,1.4761705 -0.815275,0 -1.476169,-0.6608944 -1.476169,-1.4761705 0,-0.8152759 0.660894,-1.4761676 1.476169,-1.4761676 0.815274,0 1.476168,0.6608917 1.476168,1.4761676 z"
-     id="path3167" />
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" height="16.001" width="16">
+<path fill="#CCC" d="m8.0005,3.2833c-3.4339,0-6.4103,1.9996-7.8729,4.9206,1.4626,2.9209,4.439,4.9206,7.8729,4.9206,3.4338,0,6.4102-1.9996,7.8729-4.9206-1.462-2.9204-4.439-4.9201-7.8725-4.9201zm3.8819,2.6095c0.9251,0.59007,1.709,1.3804,2.2978,2.3111-0.58881,0.93063-1.3727,1.721-2.2978,2.3111-1.1624,0.7414-2.5047,1.1333-3.8818,1.1333s-2.7195-0.39189-3.8819-1.1333c-0.92507-0.59004-1.709-1.3804-2.2978-2.3111,0.58881-0.93066,1.3727-1.721,2.2978-2.3111,0.06025-0.038442,0.12111-0.075682,0.18234-0.11225-0.15312,0.42021-0.23692,0.87374-0.23692,1.3469,0,2.174,1.7624,3.9364,3.9364,3.9364s3.9365-1.7624,3.9365-3.9364c0-0.4732-0.0838-0.92673-0.23692-1.347,0.0612,0.036566,0.12206,0.073839,0.18234,0.11228zm-3.8815,0.7427c0,0.81528-0.66089,1.4762-1.4762,1.4762-0.81528,0-1.4762-0.66089-1.4762-1.4762,0-0.81528,0.66089-1.4762,1.4762-1.4762,0.81527,0,1.4762,0.66089,1.4762,1.4762z"/>
+</svg>

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác