Browse Source

Merge branch 'dev' into beta

Marien Fressinaud 12 years ago
parent
commit
7f51bf0d02
55 changed files with 1073 additions and 479 deletions
  1. 4 4
      CHANGELOG
  2. 4 4
      README.md
  3. 1 1
      app/Controllers/configureController.php
  4. 23 17
      app/Controllers/indexController.php
  5. 1 1
      app/Controllers/javascriptController.php
  6. 1 0
      app/Controllers/usersController.php
  7. 2 2
      app/FreshRSS.php
  8. 2 2
      app/Models/Configuration.php
  9. 1 43
      app/Models/Feed.php
  10. 205 0
      app/Models/StatsDAO.php
  11. 24 7
      app/i18n/en.php
  12. 23 6
      app/i18n/fr.php
  13. 2 0
      app/i18n/install.en.php
  14. 1 0
      app/i18n/install.fr.php
  15. 4 3
      app/layout/aside_configure.phtml
  16. 2 2
      app/layout/aside_feed.phtml
  17. 1 1
      app/layout/aside_flux.phtml
  18. 4 2
      app/layout/header.phtml
  19. 1 1
      app/layout/layout.phtml
  20. 7 4
      app/layout/nav_menu.phtml
  21. 1 1
      app/views/configure/archiving.phtml
  22. 11 8
      app/views/configure/display.phtml
  23. 9 9
      app/views/configure/feed.phtml
  24. 6 6
      app/views/configure/sharing.phtml
  25. 5 3
      app/views/configure/users.phtml
  26. 9 7
      app/views/helpers/view/normal_view.phtml
  27. 11 20
      app/views/index/formLogin.phtml
  28. 2 0
      app/views/index/index.phtml
  29. 125 0
      app/views/index/stats.phtml
  30. 29 10
      lib/Minz/Configuration.php
  31. 1 1
      lib/Minz/Error.php
  32. 56 5
      lib/lib_rss.php
  33. BIN
      p/favicon.ico
  34. 70 30
      p/i/install.php
  35. 9 0
      p/scripts/flotr2.min.js
  36. 0 0
      p/scripts/jquery-2.0.3.min.map
  37. 0 3
      p/scripts/jquery.min.js
  38. 22 10
      p/scripts/main.js
  39. 78 23
      p/themes/Dark/freshrss.css
  40. 36 3
      p/themes/Dark/global.css
  41. 80 12
      p/themes/Flat/freshrss.css
  42. 28 0
      p/themes/Flat/global.css
  43. 31 0
      p/themes/Flat/icons/category.svg
  44. 77 16
      p/themes/Origine/freshrss.css
  45. 34 1
      p/themes/Origine/global.css
  46. BIN
      p/themes/icons/favicon-128.png
  47. BIN
      p/themes/icons/favicon-16-32-48-64.ico
  48. BIN
      p/themes/icons/favicon-16.png
  49. BIN
      p/themes/icons/favicon-256.png
  50. BIN
      p/themes/icons/favicon-32.png
  51. BIN
      p/themes/icons/favicon-48.png
  52. BIN
      p/themes/icons/favicon-512.png
  53. BIN
      p/themes/icons/favicon-64.png
  54. 17 0
      p/themes/icons/favicon.svg
  55. 13 211
      p/themes/icons/icon.svg

+ 4 - 4
CHANGELOG

@@ -1,6 +1,6 @@
 # Journal des modifications
 # Journal des modifications
 
 
-## 2014-01-xx FreshRSS 0.7
+## 2014-01-29 FreshRSS 0.7
 
 
 * Nouveau mode multi-utilisateur
 * Nouveau mode multi-utilisateur
 	* L’utilisateur par défaut (administrateur) peut créer et supprimer d’autres utilisateurs
 	* L’utilisateur par défaut (administrateur) peut créer et supprimer d’autres utilisateurs
@@ -14,10 +14,10 @@
 * Installateur supportant les mises à jour :
 * Installateur supportant les mises à jour :
 	* Depuis une v0.6, placer application.ini et Configuration.array.php dans le nouveau répertoire “./data/”
 	* Depuis une v0.6, placer application.ini et Configuration.array.php dans le nouveau répertoire “./data/”
 		(voir réorganisation ci-dessous)
 		(voir réorganisation ci-dessous)
-	* Pour les versions suivantes, juste garder “./data/config.php” et “./data/*_user.php”,
-		éventuellement “./data/persona/*”
-* Rafraîchissement automatique du nombre d’articles non lus toutes les minutes (utilise le cache HTTP à bon escient)
+	* Pour les versions suivantes, juste garder le répertoire “./data/”
+* Rafraîchissement automatique du nombre d’articles non lus toutes les deux minutes (utilise le cache HTTP à bon escient)
 	* Permet aussi de conserver la session valide, surtout dans le cas de Persona
 	* Permet aussi de conserver la session valide, surtout dans le cas de Persona
+* Nouvelle page de statistiques (nombres d’articles par jour / catégorie)
 * Importation OPML instantanée et plus tolérante
 * Importation OPML instantanée et plus tolérante
 * Nouvelle gestion des favicons avec téléchargement en parallèle
 * Nouvelle gestion des favicons avec téléchargement en parallèle
 * Nouvelles options
 * Nouvelles options

+ 4 - 4
README.md

@@ -6,10 +6,10 @@ Il se veut léger et facile à prendre en main tout en étant un outil puissant
 Il permet de gérer plusieurs utilisateurs, et dispose d’un mode de lecture anonyme.
 Il permet de gérer plusieurs utilisateurs, et dispose d’un mode de lecture anonyme.
 
 
 * Site officiel : http://freshrss.org
 * Site officiel : http://freshrss.org
-* Démo : http://marienfressinaud.fr/projets/freshrss/
+* Démo : http://demo.freshrss.org/
 * Développeur : Marien Fressinaud <dev@marienfressinaud.fr>
 * Développeur : Marien Fressinaud <dev@marienfressinaud.fr>
-* Version actuelle : 0.7-RC1
-* Date de publication 2014-01-xx
+* Version actuelle : 0.7
+* Date de publication 2014-01-29
 * License [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html)
 * License [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html)
 
 
 ![Logo de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_title.png)
 ![Logo de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_title.png)
@@ -29,7 +29,7 @@ Privilégiez pour cela des demandes sur GitHub
 	* Requis : [PDO_MySQL](http://php.net/pdo-mysql), [cURL](http://php.net/curl), [LibXML](http://php.net/xml), [PCRE](http://php.net/pcre), [ctype](http://php.net/ctype)
 	* Requis : [PDO_MySQL](http://php.net/pdo-mysql), [cURL](http://php.net/curl), [LibXML](http://php.net/xml), [PCRE](http://php.net/pcre), [ctype](http://php.net/ctype)
 	* Recommandés : [JSON](http://php.net/json), [zlib](http://php.net/zlib), [mbstring](http://php.net/mbstring), [iconv](http://php.net/iconv)
 	* Recommandés : [JSON](http://php.net/json), [zlib](http://php.net/zlib), [mbstring](http://php.net/mbstring), [iconv](http://php.net/iconv)
 * MySQL 5.0.3+ (ou SQLite 3.7.4+ à venir)
 * MySQL 5.0.3+ (ou SQLite 3.7.4+ à venir)
-* Un navigateur Web récent tel Firefox, Chrome, Opera, Safari, Internet Explorer 9+
+* Un navigateur Web récent tel Firefox 4+, Chrome, Opera, Safari, Internet Explorer 9+
 	* Fonctionne aussi sur mobile
 	* Fonctionne aussi sur mobile
 
 
 ![Capture d’écran de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_default-design.png)
 ![Capture d’écran de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_default-design.png)

+ 1 - 1
app/Controllers/configureController.php

@@ -196,7 +196,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 		if (Minz_Request::isPost ()) {
 		if (Minz_Request::isPost ()) {
 			$this->view->conf->_sharing (array(
 			$this->view->conf->_sharing (array(
 				'shaarli' => Minz_Request::param ('shaarli', false),
 				'shaarli' => Minz_Request::param ('shaarli', false),
-				'poche' => Minz_Request::param ('poche', false),
+				'wallabag' => Minz_Request::param ('wallabag', false),
 				'diaspora' => Minz_Request::param ('diaspora', false),
 				'diaspora' => Minz_Request::param ('diaspora', false),
 				'twitter' => Minz_Request::param ('twitter', false),
 				'twitter' => Minz_Request::param ('twitter', false),
 				'g+' => Minz_Request::param ('g+', false),
 				'g+' => Minz_Request::param ('g+', false),

+ 23 - 17
app/Controllers/indexController.php

@@ -1,18 +1,7 @@
 <?php
 <?php
 
 
 class FreshRSS_index_Controller extends Minz_ActionController {
 class FreshRSS_index_Controller extends Minz_ActionController {
-	private $get = false;
 	private $nb_not_read_cat = 0;
 	private $nb_not_read_cat = 0;
-	private $entryDAO;
-	private $feedDAO;
-	private $catDAO;
-
-	function __construct($router) {
-		parent::__construct($router);
-		$this->entryDAO = new FreshRSS_EntryDAO ();
-		$this->feedDAO = new FreshRSS_FeedDAO ();
-		$this->catDAO = new FreshRSS_CategoryDAO ();
-	}
 
 
 	public function indexAction () {
 	public function indexAction () {
 		$output = Minz_Request::param ('output');
 		$output = Minz_Request::param ('output');
@@ -50,8 +39,11 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 			Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js')));
 			Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js')));
 		}
 		}
 
 
-		$this->view->cat_aside = $this->catDAO->listCategories ();
-		$this->view->nb_favorites = $this->entryDAO->countUnreadReadFavorites ();
+		$catDAO = new FreshRSS_CategoryDAO();
+		$entryDAO = new FreshRSS_EntryDAO();
+
+		$this->view->cat_aside = $catDAO->listCategories ();
+		$this->view->nb_favorites = $entryDAO->countUnreadReadFavorites ();
 		$this->view->currentName = '';
 		$this->view->currentName = '';
 
 
 		$this->view->get_c = '';
 		$this->view->get_c = '';
@@ -125,14 +117,14 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 		$keepHistoryDefault = $this->view->conf->keep_history_default;
 		$keepHistoryDefault = $this->view->conf->keep_history_default;
 
 
 		try {
 		try {
-			$entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, $keepHistoryDefault);
+			$entries = $entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, $keepHistoryDefault);
 
 
 			// Si on a récupéré aucun article "non lus"
 			// Si on a récupéré aucun article "non lus"
 			// on essaye de récupérer tous les articles
 			// on essaye de récupérer tous les articles
 			if ($state === 'not_read' && empty($entries)) {
 			if ($state === 'not_read' && empty($entries)) {
 				Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG);
 				Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG);
 				$this->view->state = 'all';
 				$this->view->state = 'all';
-				$entries = $this->entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, $keepHistoryDefault);
+				$entries = $entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, $keepHistoryDefault);
 			}
 			}
 
 
 			if (count($entries) <= $nb) {
 			if (count($entries) <= $nb) {
@@ -170,7 +162,8 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 			case 'c':
 			case 'c':
 				$cat = isset($this->view->cat_aside[$getId]) ? $this->view->cat_aside[$getId] : null;
 				$cat = isset($this->view->cat_aside[$getId]) ? $this->view->cat_aside[$getId] : null;
 				if ($cat === null) {
 				if ($cat === null) {
-					$cat = $this->catDAO->searchById ($getId);
+					$catDAO = new FreshRSS_CategoryDAO();
+					$cat = $catDAO->searchById($getId);
 				}
 				}
 				if ($cat) {
 				if ($cat) {
 					$this->view->currentName = $cat->name ();
 					$this->view->currentName = $cat->name ();
@@ -183,7 +176,8 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 			case 'f':
 			case 'f':
 				$feed = FreshRSS_CategoryDAO::findFeed($this->view->cat_aside, $getId);
 				$feed = FreshRSS_CategoryDAO::findFeed($this->view->cat_aside, $getId);
 				if (empty($feed)) {
 				if (empty($feed)) {
-					$feed = $this->feedDAO->searchById ($getId);
+					$feedDAO = new FreshRSS_FeedDAO();
+					$feed = $feedDAO->searchById($getId);
 				}
 				}
 				if ($feed) {
 				if ($feed) {
 					$this->view->currentName = $feed->name ();
 					$this->view->currentName = $feed->name ();
@@ -198,6 +192,16 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 				return false;
 				return false;
 		}
 		}
 	}
 	}
+	
+	public function statsAction () {
+		$statsDAO = new FreshRSS_StatsDAO ();
+		Minz_View::appendScript (Minz_Url::display ('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js')));
+		$this->view->repartition = $statsDAO->calculateEntryRepartition();
+		$this->view->count = ($statsDAO->calculateEntryCount());
+		$this->view->feedByCategory = $statsDAO->calculateFeedByCategory();
+		$this->view->entryByCategory = $statsDAO->calculateEntryByCategory();
+		$this->view->topFeed = $statsDAO->calculateTopFeed();
+	}
 
 
 	public function aboutAction () {
 	public function aboutAction () {
 		Minz_View::prependTitle (Minz_Translate::t ('about') . ' · ');
 		Minz_View::prependTitle (Minz_Translate::t ('about') . ' · ');
@@ -316,6 +320,8 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 				} catch (Minz_Exception $me) {
 				} catch (Minz_Exception $me) {
 					Minz_Log::record('Login failure: ' . $me->getMessage(), Minz_Log::WARNING);
 					Minz_Log::record('Login failure: ' . $me->getMessage(), Minz_Log::WARNING);
 				}
 				}
+			} else {
+				Minz_Log::record('Invalid credential parameters: user=' . $username . ' challenge=' . $c . ' nonce=' . $nonce, Minz_Log::DEBUG);
 			}
 			}
 			if (!$ok) {
 			if (!$ok) {
 				$notif = array(
 				$notif = array(

+ 1 - 1
app/Controllers/javascriptController.php

@@ -37,7 +37,7 @@ class FreshRSS_javascript_Controller extends Minz_ActionController {
 					return;	//Success
 					return;	//Success
 				}
 				}
 			} catch (Minz_Exception $me) {
 			} catch (Minz_Exception $me) {
-				Minz_Log::record('Login failure: ' . $me->getMessage(), Minz_Log::WARNING);
+				Minz_Log::record('Nonce failure: ' . $me->getMessage(), Minz_Log::WARNING);
 			}
 			}
 		}
 		}
 		$this->view->nonce = '';	//Failure
 		$this->view->nonce = '';	//Failure

+ 1 - 0
app/Controllers/usersController.php

@@ -106,6 +106,7 @@ class FreshRSS_users_Controller extends Minz_ActionController {
 					}
 					}
 					$passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST));
 					$passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST));
 					$passwordPlain = '';
 					$passwordPlain = '';
+					$passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash);	//Compatibility with bcrypt.js
 					$ok &= ($passwordHash != '');
 					$ok &= ($passwordHash != '');
 				}
 				}
 				if (empty($passwordHash)) {
 				if (empty($passwordHash)) {

+ 2 - 2
app/FreshRSS.php

@@ -106,8 +106,8 @@ class FreshRSS extends Minz_FrontController {
 	private function loadParamsView () {
 	private function loadParamsView () {
 		Minz_Session::_param ('language', $this->conf->language);
 		Minz_Session::_param ('language', $this->conf->language);
 		Minz_Translate::init();
 		Minz_Translate::init();
-		$output = Minz_Request::param ('output');
-		if (!$output) {
+		$output = Minz_Request::param ('output', '');
+		if (($output === '') || ($output !== 'normal' && $output !== 'rss' && $output !== 'reader' && $output !== 'global')) {
 			$output = $this->conf->view_mode;
 			$output = $this->conf->view_mode;
 			Minz_Request::_param ('output', $output);
 			Minz_Request::_param ('output', $output);
 		}
 		}

+ 2 - 2
app/Models/Configuration.php

@@ -48,7 +48,7 @@ class FreshRSS_Configuration {
 		'bottomline_link' => true,
 		'bottomline_link' => true,
 		'sharing' => array(
 		'sharing' => array(
 			'shaarli' => '',
 			'shaarli' => '',
-			'poche' => '',
+			'wallabag' => '',
 			'diaspora' => '',
 			'diaspora' => '',
 			'twitter' => true,
 			'twitter' => true,
 			'g+' => true,
 			'g+' => true,
@@ -185,7 +185,7 @@ class FreshRSS_Configuration {
 		}
 		}
 	}
 	}
 	public function _sharing ($values) {
 	public function _sharing ($values) {
-		$are_url = array ('shaarli', 'poche', 'diaspora');
+		$are_url = array ('shaarli', 'wallabag', 'diaspora');
 		foreach ($values as $key => $value) {
 		foreach ($values as $key => $value) {
 			if (in_array($key, $are_url)) {
 			if (in_array($key, $are_url)) {
 				$is_url = (
 				$is_url = (

+ 1 - 43
app/Models/Feed.php

@@ -187,54 +187,12 @@ class FreshRSS_Feed extends Minz_Model {
 					Minz_Exception::ERROR
 					Minz_Exception::ERROR
 				);
 				);
 			} else {
 			} else {
-				$feed = new SimplePie ();
-				$feed->set_useragent(Minz_Translate::t ('freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION);
 				$url = htmlspecialchars_decode ($this->url, ENT_QUOTES);
 				$url = htmlspecialchars_decode ($this->url, ENT_QUOTES);
 				if ($this->httpAuth != '') {
 				if ($this->httpAuth != '') {
 					$url = preg_replace ('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url);
 					$url = preg_replace ('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url);
 				}
 				}
-
+				$feed = customSimplePie();
 				$feed->set_feed_url ($url);
 				$feed->set_feed_url ($url);
-				$feed->set_cache_location (CACHE_PATH);
-				$feed->set_cache_duration(1500);
-				$feed->strip_htmltags (array (
-					'base', 'blink', 'body', 'doctype', 'embed',
-					'font', 'form', 'frame', 'frameset', 'html',
-					'input', 'marquee', 'meta', 'noscript',
-					'object', 'param', 'plaintext', 'script', 'style',
-				));
-				$feed->strip_attributes(array_merge($feed->strip_attributes, array(
-					'autoplay', 'onload', 'onunload', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup',
-					'onmouseover', 'onmousemove', 'onmouseout', 'onfocus', 'onblur',
-					'onkeypress', 'onkeydown', 'onkeyup', 'onselect', 'onchange', 'seamless')));
-				$feed->add_attributes(array(
-					'img' => array('lazyload' => ''),	//http://www.w3.org/TR/resource-priorities/
-					'audio' => array('preload' => 'none'),
-					'iframe' => array('postpone' => '', 'sandbox' => 'allow-scripts allow-same-origin'),
-					'video' => array('postpone' => '', 'preload' => 'none'),
-				));
-				$feed->set_url_replacements(array(
-					'a' => 'href',
-					'area' => 'href',
-					'audio' => 'src',
-					'blockquote' => 'cite',
-					'del' => 'cite',
-					'form' => 'action',
-					'iframe' => 'src',
-					'img' => array(
-						'longdesc',
-						'src'
-					),
-					'input' => 'src',
-					'ins' => 'cite',
-					'q' => 'cite',
-					'source' => 'src',
-					'track' => 'src',
-					'video' => array(
-						'poster',
-						'src',
-					),
-				));
 				$feed->init ();
 				$feed->init ();
 
 
 				if ($feed->error ()) {
 				if ($feed->error ()) {

+ 205 - 0
app/Models/StatsDAO.php

@@ -0,0 +1,205 @@
+<?php
+
+class FreshRSS_StatsDAO extends Minz_ModelPdo {
+
+	/**
+	 * Calculates entry repartition for all feeds and for main stream.
+	 * The repartition includes:
+	 *   - total entries
+	 *   - read entries
+	 *   - unread entries
+	 *   - favorite entries
+	 * 
+	 * @return type
+	 */
+	public function calculateEntryRepartition() {
+		$repartition = array();
+
+		// Generates the repartition for the main stream of entry
+		$sql = <<<SQL
+SELECT COUNT(1) AS `total`,
+COUNT(1) - SUM(e.is_read) AS `unread`,
+SUM(e.is_read) AS `read`,
+SUM(e.is_favorite) AS `favorite`
+FROM {$this->prefix}entry AS e
+, {$this->prefix}feed AS f
+WHERE e.id_feed = f.id
+AND f.priority = 10
+SQL;
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+		$repartition['main_stream'] = $res[0];
+
+		// Generates the repartition for all entries
+		$sql = <<<SQL
+SELECT COUNT(1) AS `total`,
+COUNT(1) - SUM(e.is_read) AS `unread`,
+SUM(e.is_read) AS `read`,
+SUM(e.is_favorite) AS `favorite`
+FROM {$this->prefix}entry AS e
+SQL;
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+		$repartition['all_feeds'] = $res[0];
+
+		return $repartition;
+	}
+
+	/**
+	 * Calculates entry count per day on a 30 days period.
+	 * Returns the result as a JSON string.
+	 * 
+	 * @return string
+	 */
+	public function calculateEntryCount() {
+		$count = array();
+
+		// Generates a list of 30 last day to be sure we always have 30 days.
+		// If we do not do that kind of thing, we'll end up with holes in the
+		// days if the user do not have a lot of feeds.
+		$sql = <<<SQL
+SELECT - (tens.val + units.val + 1) AS day
+FROM (
+    SELECT 0 AS val
+    UNION ALL SELECT 1
+    UNION ALL SELECT 2
+    UNION ALL SELECT 3
+    UNION ALL SELECT 4
+    UNION ALL SELECT 5
+    UNION ALL SELECT 6
+    UNION ALL SELECT 7
+    UNION ALL SELECT 8
+    UNION ALL SELECT 9
+) AS units
+CROSS JOIN (
+    SELECT 0 AS val
+    UNION ALL SELECT 10
+    UNION ALL SELECT 20
+) AS tens
+ORDER BY day ASC
+SQL;
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+		foreach ($res as $value) {
+			$count[$value['day']] = 0;
+		}
+
+		// Get stats per day for the last 30 days and applies the result on 
+		// the array created with the last query.
+		$sql = <<<SQL
+SELECT DATEDIFF(FROM_UNIXTIME(e.date), NOW()) AS day,
+COUNT(1) AS count
+FROM {$this->prefix}entry AS e
+WHERE FROM_UNIXTIME(e.date, '%Y%m%d') BETWEEN DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -30 DAY), '%Y%m%d') AND DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -1 DAY), '%Y%m%d')
+GROUP BY day
+ORDER BY day ASC
+SQL;
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+
+		foreach ($res as $value) {
+			$count[$value['day']] = (int) $value['count'];
+		}
+
+		return $this->convertToSerie($count);
+	}
+
+	/**
+	 * Calculates feed count per category.
+	 * Returns the result as a JSON string.
+	 * 
+	 * @return string
+	 */
+	public function calculateFeedByCategory() {
+		$sql = <<<SQL
+SELECT c.name AS label
+, COUNT(f.id) AS data
+FROM {$this->prefix}category AS c,
+{$this->prefix}feed AS f
+WHERE c.id = f.category
+GROUP BY label
+ORDER BY data DESC
+SQL;
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+
+		return $this->convertToPieSerie($res);
+	}
+
+	/**
+	 * Calculates entry count per category.
+	 * Returns the result as a JSON string.
+	 * 
+	 * @return string
+	 */
+	public function calculateEntryByCategory() {
+		$sql = <<<SQL
+SELECT c.name AS label
+, COUNT(e.id) AS data
+FROM {$this->prefix}category AS c,
+{$this->prefix}feed AS f,
+{$this->prefix}entry AS e
+WHERE c.id = f.category
+AND f.id = e.id_feed
+GROUP BY label
+ORDER BY data DESC
+SQL;
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+
+		return $this->convertToPieSerie($res);
+	}
+
+	/**
+	 * Calculates the 10 top feeds based on their number of entries
+	 * 
+	 * @return array
+	 */
+	public function calculateTopFeed() {
+		$sql = <<<SQL
+SELECT f.id AS id
+, MAX(f.name) AS name
+, MAX(c.name) AS category
+, COUNT(e.id) AS count
+FROM {$this->prefix}category AS c,
+{$this->prefix}feed AS f,
+{$this->prefix}entry AS e
+WHERE c.id = f.category
+AND f.id = e.id_feed
+GROUP BY id
+ORDER BY count DESC
+LIMIT 10
+SQL;
+		$stm = $this->bd->prepare($sql);
+		$stm->execute();
+		return $stm->fetchAll(PDO::FETCH_ASSOC);
+	}
+
+	private function convertToSerie($data) {
+		$serie = array();
+
+		foreach ($data as $key => $value) {
+			$serie[] = array($key, $value);
+		}
+
+		return json_encode($serie);
+	}
+
+	private function convertToPieSerie($data) {
+		$serie = array();
+
+		foreach ($data as $value) {
+			$value['data'] = array(array(0, (int) $value['data']));
+			$serie[] = $value;
+		}
+
+		return json_encode($serie);
+	}
+
+}

+ 24 - 7
app/i18n/en.php

@@ -11,8 +11,11 @@ return array (
 	'users'				=> 'Users',
 	'users'				=> 'Users',
 	'categories'			=> 'Categories',
 	'categories'			=> 'Categories',
 	'category'			=> 'Category',
 	'category'			=> 'Category',
+	'feed'				=> 'Feed',
+	'feeds'				=> 'Feeds',
 	'shortcuts'			=> 'Shortcuts',
 	'shortcuts'			=> 'Shortcuts',
 	'about'				=> 'About',
 	'about'				=> 'About',
+	'stats'				=> 'Statistics',
 
 
 	'your_rss_feeds'		=> 'Your RSS feeds',
 	'your_rss_feeds'		=> 'Your RSS feeds',
 	'add_rss_feed'			=> 'Add a RSS feed',
 	'add_rss_feed'			=> 'Add a RSS feed',
@@ -20,7 +23,8 @@ return array (
 	'import_export_opml'		=> 'Import / export (OPML)',
 	'import_export_opml'		=> 'Import / export (OPML)',
 
 
 	'subscription_management'	=> 'Subscriptions management',
 	'subscription_management'	=> 'Subscriptions management',
-	'all_feeds'			=> 'Main stream',
+	'main_stream'			=> 'Main stream',
+	'all_feeds'			=> 'All feeds',
 	'favorite_feeds'		=> 'Favourites (%d)',
 	'favorite_feeds'		=> 'Favourites (%d)',
 	'not_read'			=> '%d unread',
 	'not_read'			=> '%d unread',
 	'not_reads'			=> '%d unread',
 	'not_reads'			=> '%d unread',
@@ -157,7 +161,7 @@ return array (
 	'not_yet_implemented'		=> 'Not yet implemented',
 	'not_yet_implemented'		=> 'Not yet implemented',
 	'access_protected_feeds'	=> 'Connection allows to access HTTP protected RSS feeds',
 	'access_protected_feeds'	=> 'Connection allows to access HTTP protected RSS feeds',
 	'no_selected_feed'		=> 'No feed selected.',
 	'no_selected_feed'		=> 'No feed selected.',
-	'think_to_add'			=> '<a href="./?c=configure&amp;a=feed">Remember to add some RSS feeds!</a>',
+	'think_to_add'			=> '<a href="./?c=configure&amp;a=feed">You may add some feeds</a>.',
 
 
 	'current_user'			=> 'Current user',
 	'current_user'			=> 'Current user',
 	'default_user'			=> 'Username of the default user <small>(maximum 16 alphanumeric characters)</small>',
 	'default_user'			=> 'Username of the default user <small>(maximum 16 alphanumeric characters)</small>',
@@ -206,7 +210,7 @@ return array (
 	'scroll'			=> 'during page scrolls',
 	'scroll'			=> 'during page scrolls',
 	'upon_reception'		=> 'upon reception of the article',
 	'upon_reception'		=> 'upon reception of the article',
 	'your_shaarli'			=> 'Your Shaarli',
 	'your_shaarli'			=> 'Your Shaarli',
-	'your_poche'			=> 'Your Poche',
+	'your_wallabag'			=> 'Your wallabag',
 	'your_diaspora_pod'		=> 'Your Diaspora* pod',
 	'your_diaspora_pod'		=> 'Your Diaspora* pod',
 	'sharing'			=> 'Sharing',
 	'sharing'			=> 'Sharing',
 	'share'				=> 'Share',
 	'share'				=> 'Share',
@@ -217,7 +221,7 @@ return array (
 	'more_information'		=> 'More information',
 	'more_information'		=> 'More information',
 	'activate_sharing'		=> 'Activate sharing',
 	'activate_sharing'		=> 'Activate sharing',
 	'shaarli'			=> 'Shaarli',
 	'shaarli'			=> 'Shaarli',
-	'poche'				=> 'Poche',
+	'wallabag'			=> 'wallabag',
 	'diaspora'			=> 'Diaspora*',
 	'diaspora'			=> 'Diaspora*',
 	'twitter'			=> 'Twitter',
 	'twitter'			=> 'Twitter',
 	'g+'				=> 'Google+',
 	'g+'				=> 'Google+',
@@ -232,7 +236,7 @@ return array (
 	'by'				=> 'by',
 	'by'				=> 'by',
 
 
 	'load_more'			=> 'Load more articles',
 	'load_more'			=> 'Load more articles',
-	'nothing_to_load'		=> 'There is no more articles',
+	'nothing_to_load'		=> 'There are no more articles',
 
 
 	'rss_feeds_of'			=> 'RSS feed of %s',
 	'rss_feeds_of'			=> 'RSS feed of %s',
 
 
@@ -241,9 +245,10 @@ return array (
 	'today'				=> 'Today',
 	'today'				=> 'Today',
 	'yesterday'			=> 'Yesterday',
 	'yesterday'			=> 'Yesterday',
 	'before_yesterday'		=> 'Before yesterday',
 	'before_yesterday'		=> 'Before yesterday',
+	'new_article'			=> 'There are new available articles, click to refresh the page.',
 	'by_author'			=> 'By <em>%s</em>',
 	'by_author'			=> 'By <em>%s</em>',
 	'related_tags'			=> 'Related tags',
 	'related_tags'			=> 'Related tags',
-	'no_feed_to_display'		=> 'There is no feed to show yet.',
+	'no_feed_to_display'		=> 'There is no article to show.',
 
 
 	'about_freshrss'		=> 'About FreshRSS',
 	'about_freshrss'		=> 'About FreshRSS',
 	'project_website'		=> 'Project website',
 	'project_website'		=> 'Project website',
@@ -295,5 +300,17 @@ return array (
 	'Dec'				=> '\D\e\c\e\m\b\e\r',
 	'Dec'				=> '\D\e\c\e\m\b\e\r',
 	// format for date() function, %s allows to indicate month in letter
 	// format for date() function, %s allows to indicate month in letter
 	'format_date'			=> '%s j\<\s\u\p\>S\<\/\s\u\p\> Y',
 	'format_date'			=> '%s j\<\s\u\p\>S\<\/\s\u\p\> Y',
-	'format_date_hour'		=> '%s j\<\s\u\p\>S\<\/\s\u\p\> Y \a\t H\.i',
+	'format_date_hour'		=> '%s j\<\s\u\p\>S\<\/\s\u\p\> Y \a\t H\:i',
+	
+	'status_favorites'		=> 'Favourites',
+	'status_read'			=> 'Read',
+	'status_unread'			=> 'Unread',
+	'status_total'			=> 'Total',
+	
+	'stats_entry_repartition'	=> 'Entries repartition',
+	'stats_entry_per_day'		=> 'Entries per day (last 30 days)',
+	'stats_feed_per_category'	=> 'Feeds per category',
+	'stats_entry_per_category'	=> 'Entries per category',
+	'stats_top_feed'		=> 'Top ten feeds',
+	'stats_entry_count'		=> 'Entry count',
 );
 );

+ 23 - 6
app/i18n/fr.php

@@ -11,8 +11,11 @@ return array (
 	'users'				=> 'Utilisateurs',
 	'users'				=> 'Utilisateurs',
 	'categories'			=> 'Catégories',
 	'categories'			=> 'Catégories',
 	'category'			=> 'Catégorie',
 	'category'			=> 'Catégorie',
+	'feed'				=> 'Flux',
+	'feeds'				=> 'Flux',
 	'shortcuts'			=> 'Raccourcis',
 	'shortcuts'			=> 'Raccourcis',
 	'about'				=> 'À propos',
 	'about'				=> 'À propos',
+	'stats'				=> 'Statistiques',
 
 
 	'your_rss_feeds'		=> 'Vos flux RSS',
 	'your_rss_feeds'		=> 'Vos flux RSS',
 	'add_rss_feed'			=> 'Ajouter un flux RSS',
 	'add_rss_feed'			=> 'Ajouter un flux RSS',
@@ -20,7 +23,8 @@ return array (
 	'import_export_opml'		=> 'Importer / exporter (OPML)',
 	'import_export_opml'		=> 'Importer / exporter (OPML)',
 
 
 	'subscription_management'	=> 'Gestion des abonnements',
 	'subscription_management'	=> 'Gestion des abonnements',
-	'all_feeds'			=> 'Flux principal',
+	'main_stream'			=> 'Flux principal',
+	'all_feeds'			=> 'Tous les flux',
 	'favorite_feeds'		=> 'Favoris (%d)',
 	'favorite_feeds'		=> 'Favoris (%d)',
 	'not_read'			=> '%d non lu',
 	'not_read'			=> '%d non lu',
 	'not_reads'			=> '%d non lus',
 	'not_reads'			=> '%d non lus',
@@ -157,7 +161,7 @@ return array (
 	'not_yet_implemented'		=> 'Pas encore implémenté',
 	'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',
 	'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é.',
 	'no_selected_feed'		=> 'Aucun flux sélectionné.',
-	'think_to_add'			=> '<a href="./?c=configure&amp;a=feed">Pensez à en ajouter !</a>',
+	'think_to_add'			=> '<a href="./?c=configure&amp;a=feed">Vous pouvez ajouter des flux</a>.',
 
 
 	'current_user'			=> 'Utilisateur actuel',
 	'current_user'			=> 'Utilisateur actuel',
 	'password_form'			=> 'Mot de passe<br /><small>(pour connexion par formulaire)</small>',
 	'password_form'			=> 'Mot de passe<br /><small>(pour connexion par formulaire)</small>',
@@ -206,7 +210,7 @@ return array (
 	'scroll'			=> 'au défilement de la page',
 	'scroll'			=> 'au défilement de la page',
 	'upon_reception'		=> 'dès la réception du nouvel article',
 	'upon_reception'		=> 'dès la réception du nouvel article',
 	'your_shaarli'			=> 'Votre Shaarli',
 	'your_shaarli'			=> 'Votre Shaarli',
-	'your_poche'			=> 'Votre Poche',
+	'your_wallabag'			=> 'Votre wallabag',
 	'your_diaspora_pod'		=> 'Votre pod Diaspora*',
 	'your_diaspora_pod'		=> 'Votre pod Diaspora*',
 	'sharing'			=> 'Partage',
 	'sharing'			=> 'Partage',
 	'share'				=> 'Partager',
 	'share'				=> 'Partager',
@@ -217,7 +221,7 @@ return array (
 	'more_information'		=> 'Plus d’informations',
 	'more_information'		=> 'Plus d’informations',
 	'activate_sharing'		=> 'Activer le partage',
 	'activate_sharing'		=> 'Activer le partage',
 	'shaarli'			=> 'Shaarli',
 	'shaarli'			=> 'Shaarli',
-	'poche'				=> 'Poche',
+	'wallabag'			=> 'wallabag',
 	'diaspora'			=> 'Diaspora*',
 	'diaspora'			=> 'Diaspora*',
 	'twitter'			=> 'Twitter',
 	'twitter'			=> 'Twitter',
 	'g+'				=> 'Google+',
 	'g+'				=> 'Google+',
@@ -241,9 +245,10 @@ return array (
 	'today'				=> 'Aujourd’hui',
 	'today'				=> 'Aujourd’hui',
 	'yesterday'			=> 'Hier',
 	'yesterday'			=> 'Hier',
 	'before_yesterday'		=> 'À partir d’avant-hier',
 	'before_yesterday'		=> 'À partir d’avant-hier',
+	'new_article'			=> 'Il y a de nouveaux articles disponibles, cliquez pour rafraîchir la page.',
 	'by_author'			=> 'Par <em>%s</em>',
 	'by_author'			=> 'Par <em>%s</em>',
 	'related_tags'			=> 'Tags associés',
 	'related_tags'			=> 'Tags associés',
-	'no_feed_to_display'		=> 'Il n’y a aucun flux à afficher pour l’instant.',
+	'no_feed_to_display'		=> 'Il n’y a aucun article à afficher.',
 
 
 	'about_freshrss'		=> 'À propos de FreshRSS',
 	'about_freshrss'		=> 'À propos de FreshRSS',
 	'project_website'		=> 'Site du projet',
 	'project_website'		=> 'Site du projet',
@@ -295,5 +300,17 @@ return array (
 	'Dec'				=> '\d\é\c\e\m\b\r\e',
 	'Dec'				=> '\d\é\c\e\m\b\r\e',
 	// format pour la fonction date(), %s permet d'indiquer le mois en toutes lettres
 	// format pour la fonction date(), %s permet d'indiquer le mois en toutes lettres
 	'format_date'			=> 'j %s Y',
 	'format_date'			=> 'j %s Y',
-	'format_date_hour'		=> '\l\e j %s Y \à H\:i',
+	'format_date_hour'		=> 'j %s Y \à H\:i',
+	
+	'status_favorites'		=> 'favoris',
+	'status_read'			=> 'lus',
+	'status_unread'			=> 'non lus',
+	'status_total'			=> 'total',
+	
+	'stats_entry_repartition'	=> 'Répartition des articles',
+	'stats_entry_per_day'		=> 'Nombre d’articles par jour (30 derniers jours)',
+	'stats_feed_per_category'	=> 'Flux par catégorie',
+	'stats_entry_per_category'	=> 'Articles par catégorie',
+	'stats_top_feed'		=> 'Les dix plus gros flux',
+	'stats_entry_count'		=> 'Nombre d’articles',
 );
 );

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

@@ -58,6 +58,8 @@ return array (
 
 
 	'update_start'			=> 'Start update process',
 	'update_start'			=> 'Start update process',
 	'update_long'			=> 'This can take a long time, depending on the size of your database. You may have to wait for this page to time out (~5 minutes) and then refresh this page.',
 	'update_long'			=> 'This can take a long time, depending on the size of your database. You may have to wait for this page to time out (~5 minutes) and then refresh this page.',
+	'update_end'			=> 'Update process is completed, now you can go to the final step.',
+
 
 
 	'installation_is_ok'		=> 'The installation process was successful.<br />The final step will now attempt to delete the <kbd>./p/i/install.php</kbd> file and any database backup created during the update process.<br />You may choose to skip this step and delete <kbd>./p/i/install.php</kbd> manually.',
 	'installation_is_ok'		=> 'The installation process was successful.<br />The final step will now attempt to delete the <kbd>./p/i/install.php</kbd> file and any database backup created during the update process.<br />You may choose to skip this step and delete <kbd>./p/i/install.php</kbd> manually.',
 	'finish_installation'		=> 'Complete installation',
 	'finish_installation'		=> 'Complete installation',

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

@@ -58,6 +58,7 @@ return array (
 
 
 	'update_start'			=> 'Lancer la mise à jour',
 	'update_start'			=> 'Lancer la mise à jour',
 	'update_long'			=> 'Ce processus peut prendre longtemps, selon la taille de votre base de données. Vous aurez peut-être à attendre que cette page dépasse son temps maximum d’exécution (~5 minutes) puis à la recharger.',
 	'update_long'			=> 'Ce processus peut prendre longtemps, selon la taille de votre base de données. Vous aurez peut-être à attendre que cette page dépasse son temps maximum d’exécution (~5 minutes) puis à la recharger.',
+	'update_end'			=> 'La mise à jour est terminée, vous pouvez maintenant passer à l’étape finale.',
 
 
 	'installation_is_ok'		=> 'L’installation s’est bien passée.<br />La dernière étape va maintenant tenter de supprimer le fichier <kbd>./p/i/install.php</kbd>, ainsi que d’éventuelles copies de base de données créées durant le processus de mise à jour.<br />Vous pouvez choisir de sauter cette étape et de supprimer <kbd>./p/i/install.php</kbd> manuellement.',
 	'installation_is_ok'		=> 'L’installation s’est bien passée.<br />La dernière étape va maintenant tenter de supprimer le fichier <kbd>./p/i/install.php</kbd>, ainsi que d’éventuelles copies de base de données créées durant le processus de mise à jour.<br />Vous pouvez choisir de sauter cette étape et de supprimer <kbd>./p/i/install.php</kbd> manuellement.',
 	'finish_installation'		=> 'Terminer l’installation',
 	'finish_installation'		=> 'Terminer l’installation',

+ 4 - 3
app/layout/aside_configure.phtml

@@ -1,8 +1,5 @@
 <ul class="nav nav-list aside">
 <ul class="nav nav-list aside">
 	<li class="nav-header"><?php echo Minz_Translate::t ('configuration'); ?></li>
 	<li class="nav-header"><?php echo Minz_Translate::t ('configuration'); ?></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>
 	<li class="item<?php echo Minz_Request::actionName () == 'display' ? ' active' : ''; ?>">
 	<li class="item<?php echo Minz_Request::actionName () == 'display' ? ' active' : ''; ?>">
 		<a href="<?php echo _url ('configure', 'display'); ?>"><?php echo Minz_Translate::t ('reading_configuration'); ?></a>
 		<a href="<?php echo _url ('configure', 'display'); ?>"><?php echo Minz_Translate::t ('reading_configuration'); ?></a>
 	</li>
 	</li>
@@ -15,4 +12,8 @@
 	<li class="item<?php echo Minz_Request::actionName () == 'shortcut' ? ' active' : ''; ?>">
 	<li class="item<?php echo Minz_Request::actionName () == 'shortcut' ? ' active' : ''; ?>">
 		<a href="<?php echo _url ('configure', 'shortcut'); ?>"><?php echo Minz_Translate::t ('shortcuts'); ?></a>
 		<a href="<?php echo _url ('configure', 'shortcut'); ?>"><?php echo Minz_Translate::t ('shortcuts'); ?></a>
 	</li>
 	</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>
 </ul>
 </ul>

+ 2 - 2
app/layout/aside_feed.phtml

@@ -27,10 +27,10 @@
 
 
 					<li class="dropdown-header"><?php echo Minz_Translate::t ('http_authentication'); ?></li>
 					<li class="dropdown-header"><?php echo Minz_Translate::t ('http_authentication'); ?></li>
 					<li class="input">
 					<li class="input">
-						<input type="text" name="http_user" id="http_user" autocomplete="off" placeholder="<?php echo Minz_Translate::t ('username'); ?>" />
+						<input type="text" name="http_user" id="http_user_add" autocomplete="off" placeholder="<?php echo Minz_Translate::t ('username'); ?>" />
 					</li>
 					</li>
 					<li class="input">
 					<li class="input">
-						<input type="password" name="http_pass" id="http_pass" autocomplete="off" placeholder="<?php echo Minz_Translate::t ('password'); ?>" />
+						<input type="password" name="http_pass" id="http_pass_add" autocomplete="off" placeholder="<?php echo Minz_Translate::t ('password'); ?>" />
 					</li>
 					</li>
 				</ul>
 				</ul>
 			</div>
 			</div>

+ 1 - 1
app/layout/aside_flux.phtml

@@ -23,7 +23,7 @@
 			<div class="category all">
 			<div class="category all">
 				<a data-unread="<?php echo $this->nb_not_read; ?>" class="btn<?php echo $this->get_c == 'a' ? ' active' : ''; ?>" href="<?php echo Minz_Url::display($arUrl); ?>">
 				<a data-unread="<?php echo $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 FreshRSS_Themes::icon('all'); ?>
-					<?php echo Minz_Translate::t ('all_feeds'); ?>
+					<?php echo Minz_Translate::t ('main_stream'); ?>
 				</a>
 				</a>
 			</div>
 			</div>
 		</li>
 		</li>

+ 4 - 2
app/layout/header.phtml

@@ -36,7 +36,7 @@ if (Minz_Configuration::canLogIn()) {
 		<form action="<?php echo _url ('index', 'index'); ?>" method="get">
 		<form action="<?php echo _url ('index', 'index'); ?>" method="get">
 			<div class="stick">
 			<div class="stick">
 				<?php $search = Minz_Request::param ('search', ''); ?>
 				<?php $search = Minz_Request::param ('search', ''); ?>
-				<input type="search" name="search" id="search" value="<?php echo $search; ?>" placeholder="<?php echo Minz_Translate::t ('search'); ?>" />
+				<input type="search" name="search" id="search" class="extend" value="<?php echo $search; ?>" placeholder="<?php echo Minz_Translate::t ('search'); ?>" />
 
 
 				<?php $get = Minz_Request::param ('get', ''); ?>
 				<?php $get = Minz_Request::param ('get', ''); ?>
 				<?php if($get != '') { ?>
 				<?php if($get != '') { ?>
@@ -67,12 +67,14 @@ if (Minz_Configuration::canLogIn()) {
 			<ul class="dropdown-menu">
 			<ul class="dropdown-menu">
 				<li class="dropdown-close"><a href="#close">❌</a></li>
 				<li class="dropdown-close"><a href="#close">❌</a></li>
 				<li class="dropdown-header"><?php echo Minz_Translate::t ('configuration'); ?></li>
 				<li class="dropdown-header"><?php echo Minz_Translate::t ('configuration'); ?></li>
-				<li class="item"><a href="<?php echo _url ('configure', 'users'); ?>"><?php echo Minz_Translate::t ('users'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('configure', 'display'); ?>"><?php echo Minz_Translate::t ('reading_configuration'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('configure', 'display'); ?>"><?php echo Minz_Translate::t ('reading_configuration'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('configure', 'archiving'); ?>"><?php echo Minz_Translate::t ('archiving_configuration'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('configure', 'archiving'); ?>"><?php echo Minz_Translate::t ('archiving_configuration'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('configure', 'sharing'); ?>"><?php echo Minz_Translate::t ('sharing'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('configure', 'sharing'); ?>"><?php echo Minz_Translate::t ('sharing'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('configure', 'shortcut'); ?>"><?php echo Minz_Translate::t ('shortcuts'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('configure', 'shortcut'); ?>"><?php echo Minz_Translate::t ('shortcuts'); ?></a></li>
 				<li class="separator"></li>
 				<li class="separator"></li>
+				<li class="item"><a href="<?php echo _url ('configure', 'users'); ?>"><?php echo Minz_Translate::t ('users'); ?></a></li>
+				<li class="separator"></li>
+				<li class="item"><a href="<?php echo _url ('index', 'stats'); ?>"><?php echo Minz_Translate::t ('stats'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('index', 'about'); ?>"><?php echo Minz_Translate::t ('about'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('index', 'about'); ?>"><?php echo Minz_Translate::t ('about'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('index', 'logs'); ?>"><?php echo Minz_Translate::t ('logs'); ?></a></li>
 				<li class="item"><a href="<?php echo _url ('index', 'logs'); ?>"><?php echo Minz_Translate::t ('logs'); ?></a></li>
 				<?php
 				<?php

+ 1 - 1
app/layout/layout.phtml

@@ -28,7 +28,7 @@
 		<meta name="msapplication-TileColor" content="#FFF" />
 		<meta name="msapplication-TileColor" content="#FFF" />
 		<meta name="robots" content="noindex,nofollow" />
 		<meta name="robots" content="noindex,nofollow" />
 	</head>
 	</head>
-	<body>
+	<body class="<?php echo Minz_Request::param('output', 'normal'); ?>">
 <?php $this->partial ('header'); ?>
 <?php $this->partial ('header'); ?>
 
 
 <div id="global">
 <div id="global">

+ 7 - 4
app/layout/nav_menu.phtml

@@ -1,5 +1,10 @@
+<?php
+	$actual_view = Minz_Request::param('output', 'normal');
+?>
 <div class="nav_menu">
 <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 FreshRSS_Themes::icon('category'); ?></a>
+	<?php } ?>
 
 
 	<?php if ($this->loginOk) { ?>
 	<?php if ($this->loginOk) { ?>
 	<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 FreshRSS_Themes::icon('refresh'); ?></a>
@@ -107,9 +112,7 @@
 
 
 			<?php
 			<?php
 				$url_output = $url;
 				$url_output = $url;
-				$actual_view = Minz_Request::param('output', 'normal');
-			?>
-			<?php if($actual_view !== 'normal') { ?>
+				if ($actual_view !== 'normal') { ?>
 			<li class="item">
 			<li class="item">
 				<?php $url_output['params']['output'] = 'normal'; ?>
 				<?php $url_output['params']['output'] = 'normal'; ?>
 				<a class="view_normal" href="<?php echo Minz_Url::display ($url_output); ?>">
 				<a class="view_normal" href="<?php echo Minz_Url::display ($url_output); ?>">
@@ -202,7 +205,7 @@
 	<div class="item search">
 	<div class="item search">
 		<form action="<?php echo _url ('index', 'index'); ?>" method="get">
 		<form action="<?php echo _url ('index', 'index'); ?>" method="get">
 			<?php $search = Minz_Request::param ('search', ''); ?>
 			<?php $search = Minz_Request::param ('search', ''); ?>
-			<input type="search" name="search" value="<?php echo $search; ?>" placeholder="<?php echo Minz_Translate::t ('search_short'); ?>" />
+			<input type="search" name="search" class="extend" value="<?php echo $search; ?>" placeholder="<?php echo Minz_Translate::t ('search_short'); ?>" />
 
 
 			<?php $get = Minz_Request::param ('get', ''); ?>
 			<?php $get = Minz_Request::param ('get', ''); ?>
 			<?php if($get != '') { ?>
 			<?php if($get != '') { ?>

+ 1 - 1
app/views/configure/archiving.phtml

@@ -39,7 +39,7 @@
 		<div class="form-group">
 		<div class="form-group">
 		<p class="group-name"><?php echo Minz_Translate::t('current_user'); ?></p>
 		<p class="group-name"><?php echo Minz_Translate::t('current_user'); ?></p>
 			<div class="group-controls">
 			<div class="group-controls">
-				<p><?php echo $this->nb_total, ' ', Minz_Translate::t('articles'), ', ', formatBytes($this->size_user); ?></p>
+				<p><?php echo formatNumber($this->nb_total), ' ', Minz_Translate::t('articles'), ', ', formatBytes($this->size_user); ?></p>
 				<input type="hidden" name="optimiseDatabase" value="1" />
 				<input type="hidden" name="optimiseDatabase" value="1" />
 				<button type="submit" class="btn btn-important"><?php echo Minz_Translate::t('optimize_bdd'); ?></button>
 				<button type="submit" class="btn btn-important"><?php echo Minz_Translate::t('optimize_bdd'); ?></button>
 				<?php echo FreshRSS_Themes::icon('help'); ?> <?php echo Minz_Translate::t('optimize_todo_sometimes'); ?>
 				<?php echo FreshRSS_Themes::icon('help'); ?> <?php echo Minz_Translate::t('optimize_todo_sometimes'); ?>

+ 11 - 8
app/views/configure/display.phtml

@@ -21,14 +21,17 @@
 		<div class="form-group">
 		<div class="form-group">
 			<label class="group-name" for="theme"><?php echo Minz_Translate::t ('theme'); ?></label>
 			<label class="group-name" for="theme"><?php echo Minz_Translate::t ('theme'); ?></label>
 			<div class="group-controls">
 			<div class="group-controls">
-				<select name="theme" id="theme" required="">
-				<option></option>
-				<?php foreach ($this->themes as $theme) { ?>
-				<option value="<?php echo $theme['id']; ?>"<?php echo $this->conf->theme === $theme['id'] ? ' selected="selected"' : ''; ?>>
-					<?php echo $theme['name'] . ' — ' . Minz_Translate::t ('by') . ' ' . $theme['author']; ?> 
-				</option>
-				<?php } ?>
-				</select>
+				<select name="theme" id="theme" required=""><?php
+					$found = false;
+					foreach ($this->themes as $theme) {
+						?><option value="<?php echo $theme['id']; ?>"<?php if ($this->conf->theme === $theme['id']) { echo ' selected="selected"'; $found = true; } ?>><?php
+							echo $theme['name'] . ' — ' . Minz_Translate::t ('by') . ' ' . $theme['author'];
+						?></option><?php
+					}
+					if (!$found) {
+						?><option selected="selected"></option><?php
+					}
+				?></select>
 			</div>
 			</div>
 		</div>
 		</div>
 
 

+ 9 - 9
app/views/configure/feed.phtml

@@ -16,26 +16,26 @@
 		<div class="form-group">
 		<div class="form-group">
 			<label class="group-name" for="name"><?php echo Minz_Translate::t ('title'); ?></label>
 			<label class="group-name" for="name"><?php echo Minz_Translate::t ('title'); ?></label>
 			<div class="group-controls">
 			<div class="group-controls">
-				<input type="text" name="name" id="name" value="<?php echo $this->flux->name () ; ?>" />
+				<input type="text" name="name" id="name" class="extend" value="<?php echo $this->flux->name () ; ?>" />
 			</div>
 			</div>
 		</div>
 		</div>
 		<div class="form-group">
 		<div class="form-group">
-			<label class="group-name"><?php echo Minz_Translate::t ('feed_description'); ?></label>
+			<label class="group-name" for="description"><?php echo Minz_Translate::t ('feed_description'); ?></label>
 			<div class="group-controls">
 			<div class="group-controls">
 				<textarea name="description" id="description"><?php echo htmlspecialchars($this->flux->description(), ENT_NOQUOTES, 'UTF-8'); ?></textarea>
 				<textarea name="description" id="description"><?php echo htmlspecialchars($this->flux->description(), ENT_NOQUOTES, 'UTF-8'); ?></textarea>
 			</div>
 			</div>
 		</div>
 		</div>
 		<div class="form-group">
 		<div class="form-group">
-			<label class="group-name"><?php echo Minz_Translate::t ('website_url'); ?></label>
+			<label class="group-name" for="website"><?php echo Minz_Translate::t ('website_url'); ?></label>
 			<div class="group-controls">
 			<div class="group-controls">
-				<input type="text" name="website" id="website" value="<?php echo $this->flux->website (); ?>" />
+				<input type="text" name="website" id="website" class="extend" value="<?php echo $this->flux->website (); ?>" />
 				<a target="_blank" href="<?php echo $this->flux->website (); ?>"><?php echo FreshRSS_Themes::icon('link'); ?></a>
 				<a target="_blank" href="<?php echo $this->flux->website (); ?>"><?php echo FreshRSS_Themes::icon('link'); ?></a>
 			</div>
 			</div>
 		</div>
 		</div>
 		<div class="form-group">
 		<div class="form-group">
-			<label class="group-name"><?php echo Minz_Translate::t ('feed_url'); ?></label>
+			<label class="group-name" for="url"><?php echo Minz_Translate::t ('feed_url'); ?></label>
 			<div class="group-controls">
 			<div class="group-controls">
-				<input type="text" name="url" id="url" value="<?php echo $this->flux->url (); ?>" />
+				<input type="text" name="url" id="url" class="extend" value="<?php echo $this->flux->url (); ?>" />
 				<a target="_blank" href="<?php echo $this->flux->url (); ?>"><?php echo FreshRSS_Themes::icon('link'); ?></a>
 				<a target="_blank" href="<?php echo $this->flux->url (); ?>"><?php echo FreshRSS_Themes::icon('link'); ?></a>
 				  <a class="btn" target="_blank" href="http://validator.w3.org/feed/check.cgi?url=<?php echo $this->flux->url (); ?>"><?php echo Minz_Translate::t ('feed_validator'); ?></a>
 				  <a class="btn" target="_blank" href="http://validator.w3.org/feed/check.cgi?url=<?php echo $this->flux->url (); ?>"><?php echo Minz_Translate::t ('feed_validator'); ?></a>
 			</div>
 			</div>
@@ -106,13 +106,13 @@
 		<div class="form-group">
 		<div class="form-group">
 			<label class="group-name" for="http_user"><?php echo Minz_Translate::t ('http_username'); ?></label>
 			<label class="group-name" for="http_user"><?php echo Minz_Translate::t ('http_username'); ?></label>
 			<div class="group-controls">
 			<div class="group-controls">
-				<input type="text" name="http_user" id="http_user" value="<?php echo $auth['username']; ?>" autocomplete="off" />
+				<input type="text" name="http_user" id="http_user" class="extend" value="<?php echo $auth['username']; ?>" autocomplete="off" />
 				<?php echo FreshRSS_Themes::icon('help'); ?> <?php echo Minz_Translate::t ('access_protected_feeds'); ?>
 				<?php echo FreshRSS_Themes::icon('help'); ?> <?php echo Minz_Translate::t ('access_protected_feeds'); ?>
 			</div>
 			</div>
 
 
 			<label class="group-name" for="http_pass"><?php echo Minz_Translate::t ('http_password'); ?></label>
 			<label class="group-name" for="http_pass"><?php echo Minz_Translate::t ('http_password'); ?></label>
 			<div class="group-controls">
 			<div class="group-controls">
-				<input type="password" name="http_pass" id="http_pass" value="<?php echo $auth['password']; ?>" autocomplete="off" />
+				<input type="password" name="http_pass" id="http_pass" class="extend" value="<?php echo $auth['password']; ?>" autocomplete="off" />
 			</div>
 			</div>
 		</div>
 		</div>
 
 
@@ -127,7 +127,7 @@
 		<div class="form-group">
 		<div class="form-group">
 			<label class="group-name" for="path_entries"><?php echo Minz_Translate::t ('css_path_on_website'); ?></label>
 			<label class="group-name" for="path_entries"><?php echo Minz_Translate::t ('css_path_on_website'); ?></label>
 			<div class="group-controls">
 			<div class="group-controls">
-				<input type="text" name="path_entries" id="path_entries" value="<?php echo $this->flux->pathEntries (); ?>" placeholder="<?php echo Minz_Translate::t ('blank_to_disable'); ?>" />
+				<input type="text" name="path_entries" id="path_entries" class="extend" value="<?php echo $this->flux->pathEntries (); ?>" placeholder="<?php echo Minz_Translate::t ('blank_to_disable'); ?>" />
 				<?php echo FreshRSS_Themes::icon('help'); ?> <?php echo Minz_Translate::t ('retrieve_truncated_feeds'); ?>
 				<?php echo FreshRSS_Themes::icon('help'); ?> <?php echo Minz_Translate::t ('retrieve_truncated_feeds'); ?>
 			</div>
 			</div>
 		</div>
 		</div>

+ 6 - 6
app/views/configure/sharing.phtml

@@ -10,20 +10,20 @@
 				<?php echo Minz_Translate::t ('your_shaarli'); ?>
 				<?php echo Minz_Translate::t ('your_shaarli'); ?>
 			</label>
 			</label>
 			<div class="group-controls">
 			<div class="group-controls">
-				<input type="url" id="shaarli" name="shaarli" value="<?php echo $this->conf->sharing ('shaarli'); ?>" placeholder="<?php echo Minz_Translate::t ('blank_to_disable'); ?>" size="64" />
+				<input type="url" id="shaarli" name="shaarli" class="extend" value="<?php echo $this->conf->sharing ('shaarli'); ?>" placeholder="<?php echo Minz_Translate::t ('blank_to_disable'); ?>" size="64" />
 
 
 				<?php echo FreshRSS_Themes::icon('help'); ?> <a target="_blank" href="http://sebsauvage.net/wiki/doku.php?id=php:shaarli"><?php echo Minz_Translate::t ('more_information'); ?></a>
 				<?php echo FreshRSS_Themes::icon('help'); ?> <a target="_blank" href="http://sebsauvage.net/wiki/doku.php?id=php:shaarli"><?php echo Minz_Translate::t ('more_information'); ?></a>
 			</div>
 			</div>
 		</div>
 		</div>
 
 
 		<div class="form-group">
 		<div class="form-group">
-			<label class="group-name" for="poche">
-				<?php echo Minz_Translate::t ('your_poche'); ?>
+			<label class="group-name" for="wallabag">
+				<?php echo Minz_Translate::t ('your_wallabag'); ?>
 			</label>
 			</label>
 			<div class="group-controls">
 			<div class="group-controls">
-				<input type="url" id="poche" name="poche" value="<?php echo $this->conf->sharing ('poche'); ?>" placeholder="<?php echo Minz_Translate::t ('blank_to_disable'); ?>" size="64" />
+				<input type="url" id="wallabag" name="wallabag" class="extend" value="<?php echo $this->conf->sharing ('wallabag'); ?>" placeholder="<?php echo Minz_Translate::t ('blank_to_disable'); ?>" size="64" />
 
 
-				<?php echo FreshRSS_Themes::icon('help'); ?> <a target="_blank" href="http://www.inthepoche.com/"><?php echo Minz_Translate::t ('more_information'); ?></a>
+				<?php echo FreshRSS_Themes::icon('help'); ?> <a target="_blank" href="http://www.wallabag.org"><?php echo Minz_Translate::t ('more_information'); ?></a>
 			</div>
 			</div>
 		</div>
 		</div>
 
 
@@ -32,7 +32,7 @@
 				<?php echo Minz_Translate::t ('your_diaspora_pod'); ?>
 				<?php echo Minz_Translate::t ('your_diaspora_pod'); ?>
 			</label>
 			</label>
 			<div class="group-controls">
 			<div class="group-controls">
-				<input type="url" id="diaspora" name="diaspora" value="<?php echo $this->conf->sharing ('diaspora'); ?>" placeholder="<?php echo Minz_Translate::t ('blank_to_disable'); ?>" size="64" />
+				<input type="url" id="diaspora" name="diaspora" class="extend" value="<?php echo $this->conf->sharing ('diaspora'); ?>" placeholder="<?php echo Minz_Translate::t ('blank_to_disable'); ?>" size="64" />
 
 
 				<?php echo FreshRSS_Themes::icon('help'); ?> <a target="_blank" href="https://diasporafoundation.org/"><?php echo Minz_Translate::t ('more_information'); ?></a>
 				<?php echo FreshRSS_Themes::icon('help'); ?> <a target="_blank" href="https://diasporafoundation.org/"><?php echo Minz_Translate::t ('more_information'); ?></a>
 			</div>
 			</div>

+ 5 - 3
app/views/configure/users.phtml

@@ -29,7 +29,7 @@
 			<label class="group-name" for="mail_login"><?php echo Minz_Translate::t('persona_connection_email'); ?></label>
 			<label class="group-name" for="mail_login"><?php echo Minz_Translate::t('persona_connection_email'); ?></label>
 			<?php $mail = $this->conf->mail_login; ?>
 			<?php $mail = $this->conf->mail_login; ?>
 			<div class="group-controls">
 			<div class="group-controls">
-				<input type="email" id="mail_login" name="mail_login" value="<?php echo $mail; ?>" <?php echo Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_')) ? '' : 'disabled="disabled"'; ?> placeholder="alice@example.net" />
+				<input type="email" id="mail_login" name="mail_login" class="extend" value="<?php echo $mail; ?>" <?php echo Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_')) ? '' : 'disabled="disabled"'; ?> placeholder="alice@example.net" />
 				<noscript><b><?php echo Minz_Translate::t('javascript_should_be_activated'); ?></b></noscript>
 				<noscript><b><?php echo Minz_Translate::t('javascript_should_be_activated'); ?></b></noscript>
 			</div>
 			</div>
 		</div>
 		</div>
@@ -49,7 +49,9 @@
 			<label class="group-name" for="auth_type"><?php echo Minz_Translate::t('auth_type'); ?></label>
 			<label class="group-name" for="auth_type"><?php echo Minz_Translate::t('auth_type'); ?></label>
 			<div class="group-controls">
 			<div class="group-controls">
 				<select id="auth_type" name="auth_type" required="required">
 				<select id="auth_type" name="auth_type" required="required">
-					<option value=""></option>
+					<?php if (!in_array(Minz_Configuration::authType(), array('form', 'persona', 'http_auth', 'none'))) { ?>
+						<option selected="selected"></option>
+					<?php } ?>
 					<option value="form"<?php echo Minz_Configuration::authType() === 'form' ? ' selected="selected"' : '', version_compare(PHP_VERSION, '5.3', '<') ? ' disabled="disabled"' : ''; ?>><?php echo Minz_Translate::t('auth_form'); ?></option>
 					<option value="form"<?php echo Minz_Configuration::authType() === 'form' ? ' selected="selected"' : '', version_compare(PHP_VERSION, '5.3', '<') ? ' disabled="disabled"' : ''; ?>><?php echo Minz_Translate::t('auth_form'); ?></option>
 					<option value="persona"<?php echo Minz_Configuration::authType() === 'persona' ? ' selected="selected"' : '', $this->conf->mail_login == '' ? ' disabled="disabled"' : ''; ?>><?php echo Minz_Translate::t('auth_persona'); ?></option>
 					<option value="persona"<?php echo Minz_Configuration::authType() === 'persona' ? ' selected="selected"' : '', $this->conf->mail_login == '' ? ' disabled="disabled"' : ''; ?>><?php echo Minz_Translate::t('auth_persona'); ?></option>
 					<option value="http_auth"<?php echo Minz_Configuration::authType() === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : ''; ?>><?php echo Minz_Translate::t('http_auth'); ?> (REMOTE_USER = '<?php echo httpAuthUser(); ?>')</option>
 					<option value="http_auth"<?php echo Minz_Configuration::authType() === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : ''; ?>><?php echo Minz_Translate::t('http_auth'); ?> (REMOTE_USER = '<?php echo httpAuthUser(); ?>')</option>
@@ -143,7 +145,7 @@
 			<label class="group-name" for="new_user_email"><?php echo Minz_Translate::t('persona_connection_email'); ?></label>
 			<label class="group-name" for="new_user_email"><?php echo Minz_Translate::t('persona_connection_email'); ?></label>
 			<?php $mail = $this->conf->mail_login; ?>
 			<?php $mail = $this->conf->mail_login; ?>
 			<div class="group-controls">
 			<div class="group-controls">
-				<input type="email" id="new_user_email" name="new_user_email" placeholder="alice@example.net" />
+				<input type="email" id="new_user_email" name="new_user_email" class="extend" placeholder="alice@example.net" />
 			</div>
 			</div>
 		</div>
 		</div>
 
 

+ 9 - 7
app/views/helpers/view/normal_view.phtml

@@ -9,11 +9,11 @@ if (!empty($this->entries)) {
 	$display_others = true;
 	$display_others = true;
 	if ($this->loginOk) {
 	if ($this->loginOk) {
 		$shaarli = $this->conf->sharing ('shaarli');
 		$shaarli = $this->conf->sharing ('shaarli');
-		$poche = $this->conf->sharing ('poche');
+		$wallabag = $this->conf->sharing ('wallabag');
 		$diaspora = $this->conf->sharing ('diaspora');
 		$diaspora = $this->conf->sharing ('diaspora');
 	} else {
 	} else {
 		$shaarli = '';
 		$shaarli = '';
-		$poche = '';
+		$wallabag = '';
 		$diaspora = '';
 		$diaspora = '';
 	}
 	}
 	$twitter = $this->conf->sharing ('twitter');
 	$twitter = $this->conf->sharing ('twitter');
@@ -30,7 +30,7 @@ if (!empty($this->entries)) {
 	$bottomline_read = $this->conf->bottomline_read;
 	$bottomline_read = $this->conf->bottomline_read;
 	$bottomline_favorite = $this->conf->bottomline_favorite;
 	$bottomline_favorite = $this->conf->bottomline_favorite;
 	$bottomline_sharing = $this->conf->bottomline_sharing && (
 	$bottomline_sharing = $this->conf->bottomline_sharing && (
-		$shaarli || $poche || $diaspora || $twitter ||
+		$shaarli || $wallabag || $diaspora || $twitter ||
 		$google_plus || $facebook || $email || $print);
 		$google_plus || $facebook || $email || $print);
 	$bottomline_tags = $this->conf->bottomline_tags;
 	$bottomline_tags = $this->conf->bottomline_tags;
 	$bottomline_date = $this->conf->bottomline_date;
 	$bottomline_date = $this->conf->bottomline_date;
@@ -38,6 +38,9 @@ if (!empty($this->entries)) {
 ?>
 ?>
 
 
 <div id="stream" class="normal<?php echo $hidePosts ? ' hide_posts' : ''; ?>"><?php
 <div id="stream" class="normal<?php echo $hidePosts ? ' hide_posts' : ''; ?>"><?php
+	?><div id="new-article">
+		<a href="<?php echo _url('index', 'index'); ?>"><?php echo Minz_Translate::t ('new_article'); ?></a>
+	</div><?php
 	foreach ($this->entries as $item) {
 	foreach ($this->entries as $item) {
 		if ($display_today && $item->isDay (FreshRSS_Days::TODAY, $this->today)) {
 		if ($display_today && $item->isDay (FreshRSS_Days::TODAY, $this->today)) {
 			?><div class="day" id="day_today"><?php
 			?><div class="day" id="day_today"><?php
@@ -62,7 +65,6 @@ if (!empty($this->entries)) {
 			?></div><?php
 			?></div><?php
 			$display_others = false;
 			$display_others = false;
 		}
 		}
-
 	?><div class="flux<?php echo !$item->isRead () ? ' not_read' : ''; ?><?php echo $item->isFavorite () ? ' favorite' : ''; ?>" id="flux_<?php echo $item->id (); ?>">
 	?><div class="flux<?php echo !$item->isRead () ? ' not_read' : ''; ?><?php echo $item->isFavorite () ? ' favorite' : ''; ?>" id="flux_<?php echo $item->id (); ?>">
 		<ul class="horizontal-list flux_header"><?php
 		<ul class="horizontal-list flux_header"><?php
 			if ($this->loginOk) {
 			if ($this->loginOk) {
@@ -150,10 +152,10 @@ if (!empty($this->entries)) {
 									<?php echo Minz_Translate::t ('shaarli'); ?>
 									<?php echo Minz_Translate::t ('shaarli'); ?>
 								</a>
 								</a>
 							</li>
 							</li>
-							<?php } if ($poche) { ?>
+							<?php } if ($wallabag) { ?>
 							<li class="item">
 							<li class="item">
-								<a target="_blank" href="<?php echo $poche . '?action=add&amp;url=' . base64_encode (urldecode($link)); ?>">
-									<?php echo Minz_Translate::t ('poche'); ?>
+								<a target="_blank" href="<?php echo $wallabag . '?action=add&amp;url=' . base64_encode (urldecode($link)); ?>">
+									<?php echo Minz_Translate::t ('wallabag'); ?>
 								</a>
 								</a>
 							</li>
 							</li>
 							<?php } if ($diaspora) { ?>
 							<?php } if ($diaspora) { ?>

+ 11 - 20
app/views/index/formLogin.phtml

@@ -1,5 +1,4 @@
-<div class="post content">
-
+<div class="prompt">
 <?php
 <?php
 if (Minz_Configuration::canLogIn()) {
 if (Minz_Configuration::canLogIn()) {
 	?><h1><?php echo Minz_Translate::t('login'); ?></h1><?php
 	?><h1><?php echo Minz_Translate::t('login'); ?></h1><?php
@@ -7,25 +6,17 @@ if (Minz_Configuration::canLogIn()) {
 
 
 		case 'form':
 		case 'form':
 		?><form id="loginForm" method="post" action="<?php echo _url('index', 'formLogin'); ?>">
 		?><form id="loginForm" method="post" action="<?php echo _url('index', 'formLogin'); ?>">
-			<div class="form-group">
-				<label class="group-name" for="username"><?php echo Minz_Translate::t('username'); ?></label>
-				<div class="group-controls">
-					<input type="text" id="username" name="username" size="16" required="required" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" autofocus="autofocus" />
-				</div>
-			</div>
-			<div class="form-group">
-				<label class="group-name" for="passwordPlain"><?php echo Minz_Translate::t('password'); ?></label>
-				<div class="group-controls">
+			<p>
+				<label for="username"><?php echo Minz_Translate::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" />
+			</p><p>
+				<label for="passwordPlain"><?php echo Minz_Translate::t('password'); ?></label>
 					<input type="password" id="passwordPlain" required="required" />
 					<input type="password" id="passwordPlain" required="required" />
-					<input type="hidden" id="challenge" name="challenge" />
+					<input type="hidden" id="challenge" name="challenge" /><br />
 					<noscript><strong><?php echo Minz_Translate::t('javascript_should_be_activated'); ?></strong></noscript>
 					<noscript><strong><?php echo Minz_Translate::t('javascript_should_be_activated'); ?></strong></noscript>
-				</div>
-			</div>
-			<div class="form-group form-actions">
-				<div class="group-controls">
-					<button id="loginButton" type="submit" class="btn btn-important"><?php echo Minz_Translate::t('login'); ?></button>
-				</div>
-			</div>
+			</p><p>
+				<button id="loginButton" type="submit" class="btn btn-important"><?php echo Minz_Translate::t('login'); ?></button>
+			</p>
 		</form><?php
 		</form><?php
 			break;
 			break;
 
 
@@ -39,5 +30,5 @@ if (Minz_Configuration::canLogIn()) {
 }
 }
 ?>
 ?>
 
 
-	<p><a href="<?php echo _url('index', 'about'); ?>"><?php echo Minz_Translate::t('about_freshrss'); ?></a></p>
+<p><a href="<?php echo _url('index', 'about'); ?>"><?php echo Minz_Translate::t('about_freshrss'); ?></a></p>
 </div>
 </div>

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

@@ -12,6 +12,8 @@ if ($this->loginOk || Minz_Configuration::allowAnonymous()) {
 	} elseif ($output === 'global') {
 	} elseif ($output === 'global') {
 		$this->renderHelper ('view/global_view');
 		$this->renderHelper ('view/global_view');
 	} else {
 	} else {
+		Minz_Request::_param ('output', 'normal');
+		$output = 'normal';
 		$this->renderHelper ('view/normal_view');
 		$this->renderHelper ('view/normal_view');
 	}
 	}
 } elseif ($output === 'rss') {
 } elseif ($output === 'rss') {

+ 125 - 0
app/views/index/stats.phtml

@@ -0,0 +1,125 @@
+<div class="post content">
+	<a href="<?php echo _url ('index', 'index'); ?>"><?php echo Minz_Translate::t ('back_to_rss_feeds'); ?></a>
+	
+	<h1><?php echo Minz_Translate::t ('stats'); ?></h1>
+	
+	<div class="stat">
+		<h2><?php echo Minz_Translate::t ('stats_entry_repartition')?></h2>
+		<table>
+			<thead>
+				<tr>
+					<th> </th>
+					<th><?php echo Minz_Translate::t ('main_stream')?></th>
+					<th><?php echo Minz_Translate::t ('all_feeds')?></th>
+				</tr>
+			</thead>
+			<tbody>
+				<tr>
+					<th><?php echo Minz_Translate::t ('status_total')?></th>
+					<td class="numeric"><?php echo $this->repartition['main_stream']['total']?></td>
+					<td class="numeric"><?php echo $this->repartition['all_feeds']['total']?></td>
+				</tr>
+				<tr>
+					<th><?php echo Minz_Translate::t ('status_read')?></th>
+					<td class="numeric"><?php echo $this->repartition['main_stream']['read']?></td>
+					<td class="numeric"><?php echo $this->repartition['all_feeds']['read']?></td>
+				</tr>
+				<tr>
+					<th><?php echo Minz_Translate::t ('status_unread')?></th>
+					<td class="numeric"><?php echo $this->repartition['main_stream']['unread']?></td>
+					<td class="numeric"><?php echo $this->repartition['all_feeds']['unread']?></td>
+				</tr>
+				<tr>
+					<th><?php echo Minz_Translate::t ('status_favorites')?></th>
+					<td class="numeric"><?php echo $this->repartition['main_stream']['favorite']?></td>
+					<td class="numeric"><?php echo $this->repartition['all_feeds']['favorite']?></td>
+				</tr>
+			</tbody>
+		</table>
+	</div>
+	
+	<div class="stat">
+		<h2><?php echo Minz_Translate::t ('stats_entry_per_day')?></h2>
+		<div id="statsEntryPerDay" style="height: 300px"></div>
+	</div>
+	
+	<div class="stat">
+		<h2><?php echo Minz_Translate::t ('stats_feed_per_category')?></h2>
+		<div id="statsFeedPerCategory" style="height: 300px"></div>
+		<div id="statsFeedPerCategoryLegend"></div>
+	</div>
+	
+	<div class="stat">
+		<h2><?php echo Minz_Translate::t ('stats_entry_per_category')?></h2>
+		<div id="statsEntryPerCategory" style="height: 300px"></div>
+		<div id="statsEntryPerCategoryLegend"></div>
+	</div>
+	
+	<div class="stat">
+		<h2><?php echo Minz_Translate::t ('stats_top_feed')?></h2>
+		<table>
+			<thead>
+				<tr>
+					<th><?php echo Minz_Translate::t ('feed')?></th>
+					<th><?php echo Minz_Translate::t ('category')?></th>
+					<th><?php echo Minz_Translate::t ('stats_entry_count')?></th>
+				</tr>
+			</thead>
+			<tbody>
+				<?php foreach ($this->topFeed as $feed):?>
+					<tr>
+						<td><?php echo $feed['name']?></td>
+						<td><?php echo $feed['category']?></td>
+						<td class="numeric"><?php echo $feed['count']?></td>
+					</tr>
+				<?php endforeach;?>
+			</tbody>
+		</table>
+	</div>
+</div>
+
+<script>
+"use strict";
+function initStats() {
+	if (!window.Flotr) {
+		if (window.console) {
+			console.log('FreshRSS waiting for Flotr…');
+		}
+		window.setTimeout(initStats, 50);
+		return;
+	}
+	// Entry per day
+	Flotr.draw(document.getElementById('statsEntryPerDay'),
+		[<?php echo $this->count ?>],
+		{
+			grid: {verticalLines: false},
+			bars: {horizontal: false, show: true},
+			xaxis: {noTicks: 6, showLabels: false, tickDecimals: 0},
+			yaxis: {min: 0},
+			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.y;}}
+		});
+	// Feed per category
+	Flotr.draw(document.getElementById('statsFeedPerCategory'),
+		<?php echo $this->feedByCategory ?>,
+		{
+			grid: {verticalLines: false, horizontalLines: false},
+			pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
+			xaxis: {showLabels: false},
+			yaxis: {showLabels: false},
+			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ obj.y + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
+			legend: {container: document.getElementById('statsFeedPerCategoryLegend'), noColumns: 3}
+		});
+	// Entry per category
+	Flotr.draw(document.getElementById('statsEntryPerCategory'),
+		<?php echo $this->entryByCategory ?>,
+		{
+			grid: {verticalLines: false, horizontalLines: false},
+			pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
+			xaxis: {showLabels: false},
+			yaxis: {showLabels: false},
+			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ obj.y + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
+			legend: {container: document.getElementById('statsEntryPerCategoryLegend'), noColumns: 3}
+		});
+}
+initStats();
+</script>

+ 29 - 10
lib/Minz/Configuration.php

@@ -69,8 +69,24 @@ class Minz_Configuration {
 	public static function salt () {
 	public static function salt () {
 		return self::$salt;
 		return self::$salt;
 	}
 	}
-	public static function environment () {
-		return self::$environment;
+	public static function environment ($str = false) {
+		$env = self::$environment;
+
+		if ($str) {
+			switch (self::$environment) {
+			case self::SILENT:
+				$env = 'silent';
+				break;
+			case self::DEVELOPMENT:
+				$env = 'development';
+				break;
+			case self::PRODUCTION:
+			default:
+				$env = 'production';
+			}
+		}
+
+		return $env;
 	}
 	}
 	public static function baseUrl () {
 	public static function baseUrl () {
 		return self::$base_url;
 		return self::$base_url;
@@ -147,7 +163,7 @@ class Minz_Configuration {
 	public static function writeFile() {
 	public static function writeFile() {
 		$ini_array = array(
 		$ini_array = array(
 			'general' => array(
 			'general' => array(
-				'environment' => self::$environment,
+				'environment' => self::environment(true),
 				'use_url_rewriting' => self::$use_url_rewriting,
 				'use_url_rewriting' => self::$use_url_rewriting,
 				'salt' => self::$salt,
 				'salt' => self::$salt,
 				'base_url' => self::$base_url,
 				'base_url' => self::$base_url,
@@ -205,23 +221,26 @@ class Minz_Configuration {
 
 
 		if (isset ($general['environment'])) {
 		if (isset ($general['environment'])) {
 			switch ($general['environment']) {
 			switch ($general['environment']) {
-			case Minz_Configuration::SILENT:
 			case 'silent':
 			case 'silent':
 				self::$environment = Minz_Configuration::SILENT;
 				self::$environment = Minz_Configuration::SILENT;
 				break;
 				break;
-			case Minz_Configuration::DEVELOPMENT:
 			case 'development':
 			case 'development':
 				self::$environment = Minz_Configuration::DEVELOPMENT;
 				self::$environment = Minz_Configuration::DEVELOPMENT;
 				break;
 				break;
-			case Minz_Configuration::PRODUCTION:
 			case 'production':
 			case 'production':
 				self::$environment = Minz_Configuration::PRODUCTION;
 				self::$environment = Minz_Configuration::PRODUCTION;
 				break;
 				break;
 			default:
 			default:
-				throw new Minz_BadConfigurationException (
-					'environment',
-					Minz_Exception::ERROR
-				);
+				if ($general['environment'] >= 0 &&
+					$general['environment'] <= 2) {
+					// fallback 0.7-beta
+					self::$environment = $general['environment'];
+				} else {
+					throw new Minz_BadConfigurationException (
+						'environment',
+						Minz_Exception::ERROR
+					);
+				}
 			}
 			}
 
 
 		}
 		}

+ 1 - 1
lib/Minz/Error.php

@@ -21,7 +21,7 @@ class Minz_Error {
 	*/
 	*/
 	public static function error ($code = 404, $logs = array (), $redirect = false) {
 	public static function error ($code = 404, $logs = array (), $redirect = false) {
 		$logs = self::processLogs ($logs);
 		$logs = self::processLogs ($logs);
-		$error_filename = APP_PATH . '/Controllers/ErrorController.php';
+		$error_filename = APP_PATH . '/Controllers/errorController.php';
 
 
 		if (file_exists ($error_filename)) {
 		if (file_exists ($error_filename)) {
 			$params = array (
 			$params = array (

+ 56 - 5
lib/lib_rss.php

@@ -62,6 +62,11 @@ function small_hash ($txt) {
 	return strtr ($t, '+/', '-_');
 	return strtr ($t, '+/', '-_');
 }
 }
 
 
+function formatNumber($n, $precision = 0) {
+	return str_replace(' ', '&#8239;',	//Espace fine insécable
+		number_format($n, $precision, '.', ' '));	//number_format does not seem to be Unicode-compatible
+}
+
 function formatBytes($bytes, $precision = 2, $system = 'IEC') {
 function formatBytes($bytes, $precision = 2, $system = 'IEC') {
 	if ($system === 'IEC') {
 	if ($system === 'IEC') {
 		$base = 1024;
 		$base = 1024;
@@ -74,7 +79,7 @@ function formatBytes($bytes, $precision = 2, $system = 'IEC') {
 	$pow = $bytes === 0 ? 0 : floor(log($bytes) / log($base));
 	$pow = $bytes === 0 ? 0 : floor(log($bytes) / log($base));
 	$pow = min($pow, count($units) - 1);
 	$pow = min($pow, count($units) - 1);
 	$bytes /= pow($base, $pow);
 	$bytes /= pow($base, $pow);
-	return round($bytes, $precision) . ' ' . $units[$pow];
+	return formatNumber($bytes, $precision) . ' ' . $units[$pow];
 }
 }
 
 
 function timestamptodate ($t, $hour = true) {
 function timestamptodate ($t, $hour = true) {
@@ -106,13 +111,59 @@ function html_only_entity_decode($text) {
 	return strtr($text, $htmlEntitiesOnly);
 	return strtr($text, $htmlEntitiesOnly);
 }
 }
 
 
-function sanitizeHTML($data) {
+function customSimplePie() {
+	$simplePie = new SimplePie();
+	$simplePie->set_useragent(Minz_Translate::t('freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION);
+	$simplePie->set_cache_location(CACHE_PATH);
+	$simplePie->set_cache_duration(1500);
+	$simplePie->strip_htmltags(array(
+		'base', 'blink', 'body', 'doctype', 'embed',
+		'font', 'form', 'frame', 'frameset', 'html',
+		'link', 'input', 'marquee', 'meta', 'noscript',
+		'object', 'param', 'plaintext', 'script', 'style',
+	));
+	$simplePie->strip_attributes(array_merge($simplePie->strip_attributes, array(
+		'autoplay', 'onload', 'onunload', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup',
+		'onmouseover', 'onmousemove', 'onmouseout', 'onfocus', 'onblur',
+		'onkeypress', 'onkeydown', 'onkeyup', 'onselect', 'onchange', 'seamless')));
+	$simplePie->add_attributes(array(
+		'img' => array('lazyload' => ''),	//http://www.w3.org/TR/resource-priorities/
+		'audio' => array('preload' => 'none'),
+		'iframe' => array('postpone' => '', 'sandbox' => 'allow-scripts allow-same-origin'),
+		'video' => array('postpone' => '', 'preload' => 'none'),
+	));
+	$simplePie->set_url_replacements(array(
+		'a' => 'href',
+		'area' => 'href',
+		'audio' => 'src',
+		'blockquote' => 'cite',
+		'del' => 'cite',
+		'form' => 'action',
+		'iframe' => 'src',
+		'img' => array(
+			'longdesc',
+			'src'
+		),
+		'input' => 'src',
+		'ins' => 'cite',
+		'q' => 'cite',
+		'source' => 'src',
+		'track' => 'src',
+		'video' => array(
+			'poster',
+			'src',
+		),
+	));
+	return $simplePie;
+}
+
+function sanitizeHTML($data, $base = '') {
 	static $simplePie = null;
 	static $simplePie = null;
 	if ($simplePie == null) {
 	if ($simplePie == null) {
-		$simplePie = new SimplePie();
+		$simplePie = customSimplePie();
 		$simplePie->init();
 		$simplePie->init();
 	}
 	}
-	return html_only_entity_decode($simplePie->sanitize->sanitize($data, SIMPLEPIE_CONSTRUCT_MAYBE_HTML));
+	return html_only_entity_decode($simplePie->sanitize->sanitize($data, SIMPLEPIE_CONSTRUCT_HTML, $base));
 }
 }
 
 
 /* permet de récupérer le contenu d'un article pour un flux qui n'est pas complet */
 /* permet de récupérer le contenu d'un article pour un flux qui n'est pas complet */
@@ -125,7 +176,7 @@ function get_content_by_parsing ($url, $path) {
 	if ($html) {
 	if ($html) {
 		$doc = phpQuery::newDocument ($html);
 		$doc = phpQuery::newDocument ($html);
 		$content = $doc->find ($path);
 		$content = $doc->find ($path);
-		return sanitizeHTML($content->__toString());
+		return sanitizeHTML($content->__toString(), $url);
 	} else {
 	} else {
 		throw new Exception ();
 		throw new Exception ();
 	}
 	}

BIN
p/favicon.ico


+ 70 - 30
p/i/install.php

@@ -4,7 +4,7 @@ if (function_exists('opcache_reset')) {
 }
 }
 
 
 require('../../constants.php');
 require('../../constants.php');
-const BCRYPT_COST = 9;
+define('BCRYPT_COST', 9);
 
 
 include(LIB_PATH . '/lib_rss.php');
 include(LIB_PATH . '/lib_rss.php');
 
 
@@ -87,6 +87,8 @@ SET f.cache_nbEntries=x.nbEntries, f.cache_nbUnreads=x.nbUnreads
 ');
 ');
 
 
 define('SQL_UPDATE_HISTORYv007b', 'UPDATE `%1$sfeed` SET keep_history = CASE WHEN keep_history = 0 THEN -2 WHEN keep_history = 1 THEN -1 ELSE keep_history END;');
 define('SQL_UPDATE_HISTORYv007b', 'UPDATE `%1$sfeed` SET keep_history = CASE WHEN keep_history = 0 THEN -2 WHEN keep_history = 1 THEN -1 ELSE keep_history END;');
+
+define('SQL_GET_FEEDS', 'SELECT id, url, website FROM `%1$sfeed`;');
 //</updates>
 //</updates>
 
 
 // gestion internationalisation
 // gestion internationalisation
@@ -170,6 +172,9 @@ function saveStep2 () {
 		$_SESSION['default_user'] = substr(preg_replace('/[^a-zA-Z0-9]/', '', $_POST['default_user']), 0, 16);
 		$_SESSION['default_user'] = substr(preg_replace('/[^a-zA-Z0-9]/', '', $_POST['default_user']), 0, 16);
 		$_SESSION['auth_type'] = $_POST['auth_type'];
 		$_SESSION['auth_type'] = $_POST['auth_type'];
 		if (!empty($_POST['passwordPlain'])) {
 		if (!empty($_POST['passwordPlain'])) {
+			if (!function_exists('password_hash')) {
+				include_once(LIB_PATH . '/password_compat.php');
+			}
 			$passwordHash = password_hash($_POST['passwordPlain'], PASSWORD_BCRYPT, array('cost' => BCRYPT_COST));
 			$passwordHash = password_hash($_POST['passwordPlain'], PASSWORD_BCRYPT, array('cost' => BCRYPT_COST));
 			$passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash);	//Compatibility with bcrypt.js
 			$passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash);	//Compatibility with bcrypt.js
 			$_SESSION['passwordHash'] = $passwordHash;
 			$_SESSION['passwordHash'] = $passwordHash;
@@ -307,14 +312,6 @@ function updateDatabase($perform = false) {
 			$stm->execute();
 			$stm->execute();
 		}
 		}
 
 
-		$sql = sprintf(SQL_UPDATE_HISTORYv007b, $_SESSION['bd_prefix_user']);
-		$stm = $c->prepare($sql);
-		$stm->execute();
-
-		$sql = sprintf(SQL_UPDATE_CACHED_VALUES, $_SESSION['bd_prefix_user']);
-		$stm = $c->prepare($sql);
-		$stm->execute();
-
 		$sql = sprintf(SQL_CONVERT_SELECTv006, $_SESSION['bd_prefix'], $_SESSION['bd_prefix_user']);
 		$sql = sprintf(SQL_CONVERT_SELECTv006, $_SESSION['bd_prefix'], $_SESSION['bd_prefix_user']);
 		if (!$perform) {
 		if (!$perform) {
 			$sql .= ' LIMIT 1';
 			$sql .= ' LIMIT 1';
@@ -336,6 +333,7 @@ function updateDatabase($perform = false) {
 			$content = unserialize(gzinflate(base64_decode($row['content'])));
 			$content = unserialize(gzinflate(base64_decode($row['content'])));
 			$stm2->execute(array($content, $id));
 			$stm2->execute(array($content, $id));
 		}
 		}
+
 		return true;
 		return true;
 	} catch (PDOException $e) {
 	} catch (PDOException $e) {
 		return false;
 		return false;
@@ -343,6 +341,51 @@ function updateDatabase($perform = false) {
 	return false;
 	return false;
 }
 }
 
 
+function newPdo() {
+	switch ($_SESSION['bd_type']) {
+		case 'mysql':
+			$str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base'];
+			$driver_options = array(
+				PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
+			);
+			break;
+		case 'sqlite':
+			$str = 'sqlite:' . DATA_PATH . $_SESSION['bd_base'] . '.sqlite';
+			$driver_options = null;
+			break;
+		default:
+			return false;
+	}
+	return new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options);
+}
+
+function postUpdate() {
+	$c = newPdo();
+
+	$sql = sprintf(SQL_UPDATE_HISTORYv007b, $_SESSION['bd_prefix_user']);
+	$stm = $c->prepare($sql);
+	$stm->execute();
+
+	$sql = sprintf(SQL_UPDATE_CACHED_VALUES, $_SESSION['bd_prefix_user']);
+	$stm = $c->prepare($sql);
+	$stm->execute();
+
+	//<favicons>
+	$sql = sprintf(SQL_GET_FEEDS, $_SESSION['bd_prefix_user']);
+	$stm = $c->prepare($sql);
+	$stm->execute();
+	$res = $stm->fetchAll(PDO::FETCH_ASSOC);
+	foreach ($res as $feed) {
+		if (empty($feed['url'])) {
+			continue;
+		}
+		$hash = hash('crc32b', $_SESSION['salt'] . $feed['url']);
+		@file_put_contents(DATA_PATH . '/favicons/' . $hash . '.txt',
+			empty($feed['website']) ? $feed['url'] : $feed['website']);
+	}
+	//</favicons>
+}
+
 function deleteInstall () {
 function deleteInstall () {
 	$res = unlink (INDEX_PATH . '/install.php');
 	$res = unlink (INDEX_PATH . '/install.php');
 	if ($res) {
 	if ($res) {
@@ -357,22 +400,7 @@ function deleteInstall () {
 	}
 	}
 
 
 	try {
 	try {
-		switch ($_SESSION['bd_type']) {
-			case 'mysql':
-				$str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base'];
-				$driver_options = array(
-					PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
-				);
-				break;
-			case 'sqlite':
-				$str = 'sqlite:' . DATA_PATH . $_SESSION['bd_base'] . '.sqlite';
-				$driver_options = null;
-				break;
-			default:
-				return false;
-		}
-
-		$c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options);
+		$c = newPdo();
 		$sql = sprintf(SQL_DROP_BACKUPv006, $_SESSION['bd_prefix']);
 		$sql = sprintf(SQL_DROP_BACKUPv006, $_SESSION['bd_prefix']);
 		$stm = $c->prepare($sql);
 		$stm = $c->prepare($sql);
 		$stm->execute();
 		$stm->execute();
@@ -805,7 +833,9 @@ function printStep2 () {
 			<label class="group-name" for="auth_type"><?php echo _t('auth_type'); ?></label>
 			<label class="group-name" for="auth_type"><?php echo _t('auth_type'); ?></label>
 			<div class="group-controls">
 			<div class="group-controls">
 				<select id="auth_type" name="auth_type" required="required">
 				<select id="auth_type" name="auth_type" required="required">
-					<option value=""></option>
+					<?php if (!in_array($_SESSION['auth_type'], array('form', 'persona', 'http_auth', 'none'))) { ?>
+						<option selected="selected"></option>
+					<?php } ?>
 					<option value="form"<?php echo $_SESSION['auth_type'] === 'form' ? ' selected="selected"' : '', version_compare(PHP_VERSION, '5.3', '<') ? ' disabled="disabled"' : ''; ?>><?php echo _t('auth_form'); ?></option>
 					<option value="form"<?php echo $_SESSION['auth_type'] === 'form' ? ' selected="selected"' : '', version_compare(PHP_VERSION, '5.3', '<') ? ' disabled="disabled"' : ''; ?>><?php echo _t('auth_form'); ?></option>
 					<option value="persona"<?php echo $_SESSION['auth_type'] === 'persona' ? ' selected="selected"' : ''; ?>><?php echo _t('auth_persona'); ?></option>
 					<option value="persona"<?php echo $_SESSION['auth_type'] === 'persona' ? ' selected="selected"' : ''; ?>><?php echo _t('auth_persona'); ?></option>
 					<option value="http_auth"<?php echo $_SESSION['auth_type'] === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : ''; ?>><?php echo _t('http_auth'); ?> (REMOTE_USER = '<?php echo httpAuthUser(); ?>')</option>
 					<option value="http_auth"<?php echo $_SESSION['auth_type'] === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : ''; ?>><?php echo _t('http_auth'); ?> (REMOTE_USER = '<?php echo httpAuthUser(); ?>')</option>
@@ -922,17 +952,26 @@ function printStep4 () {
 ?>
 ?>
 	<form action="index.php?step=4" method="post">
 	<form action="index.php?step=4" method="post">
 		<legend><?php echo _t ('version_update'); ?></legend>
 		<legend><?php echo _t ('version_update'); ?></legend>
+
+		<?php if (updateDatabase(false)) { ?>
+		<p class="alert"><?php echo _t ('update_long'); ?></p>
+
 		<div class="form-group form-actions">
 		<div class="form-group form-actions">
 			<div class="group-controls">
 			<div class="group-controls">
-				<?php if (updateDatabase(false)) { ?>
 				<input type="hidden" name="updateDatabase" value="1" />
 				<input type="hidden" name="updateDatabase" value="1" />
 				<button type="submit" class="btn btn-important"><?php echo _t ('update_start'); ?></button>
 				<button type="submit" class="btn btn-important"><?php echo _t ('update_start'); ?></button>
-				<p><?php echo _t ('update_long'); ?></p>
-				<?php } else { ?>
+			</div>
+		</div>
+
+		<?php } else { ?>
+		<p class="alert"><?php echo _t ('update_end'); ?></p>
+
+		<div class="form-group form-actions">
+			<div class="group-controls">
 				<a class="btn btn-important next-step" href="?step=5"><?php echo _t ('next_step'); ?></a>
 				<a class="btn btn-important next-step" href="?step=5"><?php echo _t ('next_step'); ?></a>
-				<?php } ?>
 			</div>
 			</div>
 		</div>
 		</div>
+		<?php } ?>
 	</form>
 	</form>
 <?php
 <?php
 }
 }
@@ -973,6 +1012,7 @@ case 4:
 	}
 	}
 	break;
 	break;
 case 5:
 case 5:
+	postUpdate();
 	break;
 	break;
 case 6:
 case 6:
 	deleteInstall ();
 	deleteInstall ();

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


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


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


+ 22 - 10
p/scripts/main.js

@@ -59,14 +59,18 @@ function incUnreadsFeed(article, feed_id, nb) {
 		}
 		}
 	}
 	}
 
 
+	var isCurrentView = false;
 	//Update unread: title
 	//Update unread: title
 	document.title = document.title.replace(/((?: \(\d+\))?)( · .*?)((?: \(\d+\))?)$/, function (m, p1, p2, p3) {
 	document.title = document.title.replace(/((?: \(\d+\))?)( · .*?)((?: \(\d+\))?)$/, function (m, p1, p2, p3) {
-		if (article || ($('#' + feed_id).closest('.active').length > 0)) {
+		var $feed = $('#' + feed_id);
+		if (article || ($feed.closest('.active').length > 0 && $feed.siblings('.active').length === 0)) {
+			isCurrentView = true;
 			return incLabel(p1, nb) + p2 + incLabel(p3, feed_priority > 0 ? nb : 0);
 			return incLabel(p1, nb) + p2 + incLabel(p3, feed_priority > 0 ? nb : 0);
 		} else {
 		} else {
 			return p1 + p2 + incLabel(p3, feed_priority > 0 ? nb : 0);
 			return p1 + p2 + incLabel(p3, feed_priority > 0 ? nb : 0);
 		}
 		}
 	});
 	});
+	return isCurrentView;
 }
 }
 
 
 function mark_read(active, only_not_read) {
 function mark_read(active, only_not_read) {
@@ -519,11 +523,15 @@ function init_notifications() {
 
 
 function refreshUnreads() {
 function refreshUnreads() {
 	$.getJSON('./?c=javascript&a=nbUnreadsPerFeed').done(function (data) {
 	$.getJSON('./?c=javascript&a=nbUnreadsPerFeed').done(function (data) {
+		var isAll = $('.category.all > .active').length > 0;
 		$.each(data, function(feed_id, nbUnreads) {
 		$.each(data, function(feed_id, nbUnreads) {
 			feed_id = 'f_' + feed_id;
 			feed_id = 'f_' + feed_id;
 			var elem = $('#' + feed_id + '>.feed').get(0),
 			var elem = $('#' + feed_id + '>.feed').get(0),
 				feed_unreads = elem ? (parseInt(elem.getAttribute('data-unread'), 10) || 0) : 0;
 				feed_unreads = elem ? (parseInt(elem.getAttribute('data-unread'), 10) || 0) : 0;
-			incUnreadsFeed(null, feed_id, nbUnreads - feed_unreads);
+			if ((incUnreadsFeed(null, feed_id, nbUnreads - feed_unreads) || isAll) &&	//Update of current view?
+				(nbUnreads - feed_unreads > 0)) {
+				$('#new-article').show();
+			};
 		});
 		});
 	});
 	});
 }
 }
@@ -618,14 +626,18 @@ function init_loginForm() {
 			if (data.salt1 == '' || data.nonce == '') {
 			if (data.salt1 == '' || data.nonce == '') {
 				alert('Invalid user!');
 				alert('Invalid user!');
 			} else {
 			} else {
-				var strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'),
-					s = dcodeIO.bcrypt.hashSync($('#passwordPlain').val(), data.salt1),
-					c = dcodeIO.bcrypt.hashSync(data.nonce + s, strong ? 4 : poormanSalt());
-				$('#challenge').val(c);
-				if (s == '' || c == '') {
-					alert('Crypto error!');
-				} else {
-					success = true;
+				try {
+					var strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'),
+						s = dcodeIO.bcrypt.hashSync($('#passwordPlain').val(), data.salt1),
+						c = dcodeIO.bcrypt.hashSync(data.nonce + s, strong ? 4 : poormanSalt());
+					$('#challenge').val(c);
+					if (s == '' || c == '') {
+						alert('Crypto error!');
+					} else {
+						success = true;
+					}
+				} catch (e) {
+					alert('Crypto exception! ' + e);
 				}
 				}
 			}
 			}
 		}).fail(function() {
 		}).fail(function() {

+ 78 - 23
p/themes/Dark/freshrss.css

@@ -31,7 +31,6 @@
 			}
 			}
 		.header > .item.search input {
 		.header > .item.search input {
 			width: 230px;
 			width: 230px;
-			transition: width 200ms linear;
 		}
 		}
 			.header .item.search input:focus {
 			.header .item.search input:focus {
 				width: 330px;
 				width: 330px;
@@ -201,10 +200,9 @@
 	font-weight: bold;
 	font-weight: bold;
 	line-height: 50px;
 	line-height: 50px;
 	background: #1c1c1c;
 	background: #1c1c1c;
-	border-top: 1px solid #888;
-	border-bottom: 1px solid #888;
+	border-top: 1px solid #2f2f2f;
 }
 }
-	.day:first-child {
+	#new-article + .day {
 		border-top: none;
 		border-top: none;
 	}
 	}
 	.day .name {
 	.day .name {
@@ -224,8 +222,27 @@
 		text-align: right;
 		text-align: right;
 	}
 	}
 
 
+#new-article {
+	display: none;
+	min-height: 40px;
+	background: #26303F;
+	text-align: center;
+}
+	#new-article:hover {
+		background: #4A5D7A;
+	}
+	#new-article > a {
+		display: block;
+		line-height: 40px;
+		color: #fff;
+		font-weight: bold;
+	}
+		#new-article > a:hover {
+			text-decoration: none;
+		}
+
 .flux {
 .flux {
-	border-left: 3px solid #aaa;
+	border-left: 3px solid #2f2f2f;
 	background: #1c1c1c;
 	background: #1c1c1c;
 }
 }
 	.flux.not_read {
 	.flux.not_read {
@@ -241,6 +258,10 @@
 		background: #1a1a1a;
 		background: #1a1a1a;
 	}
 	}
 
 
+	.horizontal-list > .item:not(.title):not(.website) > a {
+		display: block;
+	}
+
 	.flux_header {
 	.flux_header {
 		background: inherit;
 		background: inherit;
 		height: 25px;
 		height: 25px;
@@ -268,11 +289,6 @@
 			}
 			}
 		.flux .item.title {
 		.flux .item.title {
 			background: inherit;
 			background: inherit;
-		}
-		.flux:hover .item.title {
-			border-right: 2px solid rgba(127, 127, 127, 0.1);
-			padding-right: 1em;
-			position: absolute;
 		}
 		}
 			.flux .item.title a {
 			.flux .item.title a {
 				color: #888;
 				color: #888;
@@ -283,7 +299,7 @@
 				font-weight: bold;
 				font-weight: bold;
 			}
 			}
 		.flux .item.date {
 		.flux .item.date {
-			width: 200px;
+			width: 145px;
 			padding:0 5px 0 0;
 			padding:0 5px 0 0;
 			text-align: right;
 			text-align: right;
 			font-size: 10px;
 			font-size: 10px;
@@ -652,6 +668,14 @@ select.number option {
 	text-align:right;
 	text-align:right;
 }
 }
 
 
+@media(min-width: 841px) {
+	.flux:not(.current):hover .item.title {
+		max-width: calc(100% - 580px);
+		padding-right: 1.5em;
+		position: absolute;
+	}
+}
+
 @media(max-width: 840px) {
 @media(max-width: 840px) {
 	.header,
 	.header,
 	.aside .btn-important,
 	.aside .btn-important,
@@ -672,11 +696,6 @@ select.number option {
 		display: block;
 		display: block;
 	}
 	}
 
 
-	.content {
-		font-size: 120%;
-		padding: 0;
-	}
-
 	.pagination {
 	.pagination {
 		margin: 0 0 40px;
 		margin: 0 0 40px;
 	}
 	}
@@ -801,16 +820,12 @@ select.number option {
 }
 }
 
 
 .nav-head {
 .nav-head {
-	background: #fff;
-	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%);
+	background: #1c1c1c;
 }
 }
 
 
-.header > .item.search input {
+input.extend {
 	-moz-transition: width 200ms linear;
 	-moz-transition: width 200ms linear;
-	 -webkit-transition: width 200ms linear;
+	-webkit-transition: width 200ms linear;
 	-o-transition: width 200ms linear;
 	-o-transition: width 200ms linear;
 	-ms-transition: width 200ms linear;
 	-ms-transition: width 200ms linear;
 }
 }
@@ -860,3 +875,43 @@ select.number option {
 		text-decoration: underline;
 		text-decoration: underline;
 	}
 	}
 }
 }
+
+.stat {
+	border:1px solid #2f2f2f;
+	border-radius:10px;
+	margin:10px 0;
+	padding:0 5px;
+}
+.stat > h2 {
+	border-bottom:1px solid #2f2f2f;
+	margin:0 -5px;
+	padding-left:5px;
+}
+.stat > div {
+	margin:5px 0;
+}
+.stat > table {
+	border-collapse:collapse;
+	margin:5px 0;
+	width:100%;
+}
+.stat > table > thead > tr {
+	border-bottom:2px solid #2f2f2f;
+}
+.stat > table > tbody > tr {
+	border-bottom:1px solid #2f2f2f;
+}
+.stat > table > tbody > tr:last-child {
+	border-bottom:0;
+}
+.stat > table th, .stat > table td {
+	border-left:2px solid #2f2f2f;
+	padding:5px;
+}
+.stat > table th:first-child, .stat > table td:first-child {
+	border-left:0;
+}
+.stat > table td.numeric{
+	margin:5px 0;
+	text-align:center;
+}

+ 36 - 3
p/themes/Dark/global.css

@@ -87,6 +87,13 @@ label {
 	line-height: 25px;
 	line-height: 25px;
 	cursor: pointer;
 	cursor: pointer;
 }
 }
+input {
+	width: 180px;
+}
+textarea {
+	width: 360px;
+	height: 100px;
+}
 input, select, textarea {
 input, select, textarea {
 	display: inline-block;
 	display: inline-block;
 	max-width: 100%;
 	max-width: 100%;
@@ -116,6 +123,10 @@ input, select, textarea {
 		border-color: red;
 		border-color: red;
 		box-shadow: 0 0 2px 1px red;
 		box-shadow: 0 0 2px 1px red;
 	}
 	}
+	input:focus.extend {
+		width: 300px;
+		transition: width 200ms linear;
+	}
 
 
 .form-group {
 .form-group {
 	margin: 0;
 	margin: 0;
@@ -179,8 +190,13 @@ input, select, textarea {
 	}
 	}
 	.stick .btn + .btn,
 	.stick .btn + .btn,
 	.stick .btn + input,
 	.stick .btn + input,
+	.stick .btn + .dropdown > .btn,
 	.stick input + .btn,
 	.stick input + .btn,
-	.stick input + input {
+	.stick input + input,
+	.stick input + .dropdown > .btn,
+	.stick .dropdown + .btn,
+	.stick .dropdown + input,
+	.stick .dropdown + .dropdown > .btn {
 		border-left: none;
 		border-left: none;
 	}
 	}
 	.stick .btn + .dropdown > .btn {
 	.stick .btn + .dropdown > .btn {
@@ -305,7 +321,7 @@ input, select, textarea {
 		display: block;
 		display: block;
 		height: 0;
 		height: 0;
 		margin: 5px 0;
 		margin: 5px 0;
-		border-bottom: 1px solid #ddd;
+		border-bottom: 1px solid #2f2f2f;
 	}
 	}
 
 
 	.nav-list .nav-form {
 	.nav-list .nav-form {
@@ -317,7 +333,7 @@ input, select, textarea {
 	display: block;
 	display: block;
 	margin: 0;
 	margin: 0;
 	background: linear-gradient(to bottom, #fff, #f0f0f0);
 	background: linear-gradient(to bottom, #fff, #f0f0f0);
-	border-bottom: 1px solid #ddd;
+	border-bottom: 1px solid #2f2f2f;
 	text-align: right;
 	text-align: right;
 }
 }
 	.nav-head .item {
 	.nav-head .item {
@@ -489,3 +505,20 @@ input, select, textarea {
 	vertical-align: middle;
 	vertical-align: middle;
 	line-height: 16px;
 	line-height: 16px;
 }
 }
+
+/* Prompt (centré) */
+.prompt > h1, .prompt > p {
+	text-align:center;
+}
+.prompt > form {
+	margin:1em auto 2.5em auto;
+	width:10em;
+}
+.prompt .btn {
+	display:block;
+	margin:.5em auto;
+}
+.prompt input {
+	margin:.4em auto 1.1em auto;
+	width:99%;
+}

+ 80 - 12
p/themes/Flat/freshrss.css

@@ -34,7 +34,6 @@ body {
 			}
 			}
 		.header > .item.search input {
 		.header > .item.search input {
 			width: 230px;
 			width: 230px;
-			transition: width 200ms linear;
 		}
 		}
 			.header .item.search input:focus {
 			.header .item.search input:focus {
 				width: 330px;
 				width: 330px;
@@ -217,6 +216,25 @@ body {
 		z-index: -10;
 		z-index: -10;
 	}
 	}
 
 
+#new-article {
+	display: none;
+	min-height: 40px;
+	background: #3498db;
+	text-align: center;
+}
+	#new-article:hover {
+		background: #2980b9;
+	}
+	#new-article > a {
+		display: block;
+		line-height: 40px;
+		color: #fff;
+		font-weight: bold;
+	}
+		#new-article > a:hover {
+			text-decoration: none;
+		}
+
 .flux {
 .flux {
 	border-left: 3px solid #ecf0f1;
 	border-left: 3px solid #ecf0f1;
 }
 }
@@ -236,6 +254,10 @@ body {
 		background: #fff;
 		background: #fff;
 	}
 	}
 
 
+	.horizontal-list > .item:not(.title):not(.website) > a {
+		display: block;
+	}
+
 	.flux_header {
 	.flux_header {
 		background: inherit;
 		background: inherit;
 		height: 25px;
 		height: 25px;
@@ -263,11 +285,6 @@ body {
 			}
 			}
 		.flux .item.title {
 		.flux .item.title {
 			background: inherit;
 			background: inherit;
-		}
-		.flux:hover .item.title {
-			border-right: 2px solid rgba(127, 127, 127, 0.1);
-			padding-right: 1em;
-			position: absolute;
 		}
 		}
 			.flux .item.title a {
 			.flux .item.title a {
 				color: #333;
 				color: #333;
@@ -278,7 +295,7 @@ body {
 				font-weight: bold;
 				font-weight: bold;
 			}
 			}
 		.flux .item.date {
 		.flux .item.date {
-			width: 200px;
+			width: 145px;
 			padding:0 5px 0 0;
 			padding:0 5px 0 0;
 			text-align: right;
 			text-align: right;
 			font-size: 10px;
 			font-size: 10px;
@@ -656,6 +673,14 @@ select.number option {
 	text-align:right;
 	text-align:right;
 }
 }
 
 
+@media(min-width: 841px) {
+	.flux:not(.current):hover .item.title {
+		max-width: calc(100% - 580px);
+		padding-right: 1.5em;
+		position: absolute;
+	}
+}
+
 @media(max-width: 840px) {
 @media(max-width: 840px) {
 	.header,
 	.header,
 	.aside .btn-important,
 	.aside .btn-important,
@@ -676,11 +701,6 @@ select.number option {
 		display: block;
 		display: block;
 	}
 	}
 
 
-	.content {
-		font-size: 120%;
-		padding: 0;
-	}
-
 	.pagination {
 	.pagination {
 		margin: 0 0 40px;
 		margin: 0 0 40px;
 	}
 	}
@@ -779,6 +799,13 @@ select.number option {
 	-ms-transform: rotate(45deg);
 	-ms-transform: rotate(45deg);
 }
 }
 
 
+input.extend {
+	-moz-transition: width 200ms linear;
+	-webkit-transition: width 200ms linear;
+	-o-transition: width 200ms linear;
+	-ms-transition: width 200ms linear;
+}
+
 @media print {
 @media print {
 	.header,
 	.header,
 	.aside,
 	.aside,
@@ -815,3 +842,44 @@ select.number option {
 		text-decoration: underline;
 		text-decoration: underline;
 	}
 	}
 }
 }
+
+.stat {
+	border:1px solid #aaa;
+	border-radius:10px;
+	box-shadow:2px 2px 5px #aaa;
+	margin:10px 0;
+	padding:0 5px;
+}
+.stat > h2 {
+	border-bottom:1px solid #aaa;
+	margin:0 -5px;
+	padding-left:5px;
+}
+.stat > div {
+	margin:5px 0;
+}
+.stat > table {
+	border-collapse:collapse;
+	margin:5px 0;
+	width:100%;
+}
+.stat > table > thead > tr {
+	border-bottom:2px solid #aaa;
+}
+.stat > table > tbody > tr {
+	border-bottom:1px solid #aaa;
+}
+.stat > table > tbody > tr:last-child {
+	border-bottom:0;
+}
+.stat > table th, .stat > table td {
+	border-left:2px solid #aaa;
+	padding:5px;
+}
+.stat > table th:first-child, .stat > table td:first-child {
+	border-left:0;
+}
+.stat > table td.numeric{
+	margin:5px 0;
+	text-align:center;
+}

+ 28 - 0
p/themes/Flat/global.css

@@ -88,6 +88,13 @@ label {
 	font-weight: bold;
 	font-weight: bold;
 	color: #444;
 	color: #444;
 }
 }
+input {
+	width: 180px;
+}
+textarea {
+	width: 360px;
+	height: 100px;
+}
 input, select, textarea {
 input, select, textarea {
 	display: inline-block;
 	display: inline-block;
 	max-width: 100%;
 	max-width: 100%;
@@ -117,6 +124,10 @@ input, select, textarea {
 		border-color: red;
 		border-color: red;
 		box-shadow: 0 0 2px 1px red;
 		box-shadow: 0 0 2px 1px red;
 	}
 	}
+	input:focus.extend {
+		width: 300px;
+		transition: width 200ms linear;
+	}
 
 
 .form-group {
 .form-group {
 	margin: 5px 0;
 	margin: 5px 0;
@@ -497,3 +508,20 @@ input, select, textarea {
 	vertical-align: middle;
 	vertical-align: middle;
 	line-height: 16px;
 	line-height: 16px;
 }
 }
+
+/* Prompt (centré) */
+.prompt > h1, .prompt > p {
+	text-align:center;
+}
+.prompt > form {
+	margin:1em auto 2.5em auto;
+	width:10em;
+}
+.prompt .btn {
+	display:block;
+	margin:.5em auto;
+}
+.prompt input {
+	margin:.4em auto 1.1em auto;
+	width:99%;
+}

+ 31 - 0
p/themes/Flat/icons/category.svg

@@ -0,0 +1,31 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg xmlns:cc='http://creativecommons.org/ns#' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' xmlns:svg='http://www.w3.org/2000/svg' id='svg7384' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' sodipodi:docname='folder-symbolic.svg' version='1.1' inkscape:version='0.48.0 r9654' height='16' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns='http://www.w3.org/2000/svg' width='16'>
+  <metadata id='metadata90'>
+    <rdf:RDF>
+      <cc:Work rdf:about=''>
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type rdf:resource='http://purl.org/dc/dcmitype/StillImage'/>
+        <dc:title>Gnome Symbolic Icon Theme</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview inkscape:cy='-173.07332' pagecolor='#555753' borderopacity='1' showborder='false' inkscape:bbox-paths='false' guidetolerance='10' inkscape:window-width='1310' showguides='true' inkscape:object-nodes='true' inkscape:snap-bbox='true' inkscape:pageshadow='2' inkscape:guide-bbox='true' inkscape:snap-nodes='true' bordercolor='#FFFFFF' objecttolerance='10' id='namedview88' showgrid='false' inkscape:window-maximized='0' inkscape:window-x='52' inkscape:snap-global='true' inkscape:window-y='24' gridtolerance='10' inkscape:window-height='690' inkscape:snap-to-guides='true' inkscape:current-layer='layer13' inkscape:zoom='1' inkscape:cx='-157.67647' inkscape:snap-grids='true' inkscape:pageopacity='1'>
+    <inkscape:grid spacingx='1px' spacingy='1px' id='grid4866' empspacing='2' enabled='true' type='xygrid' snapvisiblegridlinesonly='true' visible='true'/>
+  </sodipodi:namedview>
+  <title id='title9167'>Gnome Symbolic Icon Theme</title>
+  <defs id='defs7386'/>
+  <g inkscape:label='status' transform='translate(-442,-176)' inkscape:groupmode='layer' id='layer9' style='display:inline'/>
+  <g inkscape:label='devices' transform='translate(-442,-176)' inkscape:groupmode='layer' id='layer10'/>
+  <g inkscape:label='apps' transform='translate(-442,-176)' inkscape:groupmode='layer' id='layer11'/>
+  <g inkscape:label='actions' transform='translate(-442,-176)' inkscape:groupmode='layer' id='layer12'/>
+  <g inkscape:label='places' transform='translate(-442,-176)' inkscape:groupmode='layer' id='layer13'>
+    <g transform='translate(234.0002,-820)' id='g14154'>
+      <path inkscape:connector-curvature='0' d='m 208.53105,997 c -0.28913,0 -0.53125,0.24212 -0.53125,0.53125 l 0,13.93755 c 0,0.2985 0.23264,0.5312 0.53125,0.5312 l 14.9375,0 c 0.2986,0 0.53125,-0.2326 0.53125,-0.5312 l 0,-8.9376 c 0,-0.2891 -0.24212,-0.5312 -0.53125,-0.5312 l -12.46875,0 0,7.5 c 0,0.277 -0.223,0.5 -0.5,0.5 -0.277,0 -0.5,-0.223 -0.5,-0.5 l 0,-8 c 0,-0.277 0.223,-0.5 0.5,-0.5 l 2.96875,0 8.53125,0 0,-1.4062 c 0,-0.3272 -0.26666,-0.5938 -0.59375,-0.5938 l -7.40625,0 0,-1.46875 C 213.9998,997.2421 213.75768,997 213.46855,997 z' id='rect3845' sodipodi:nodetypes='ccccccccccsccccccccccc' style='fill:#FFFFFF;fill-opacity:1;stroke:none;display:inline'/>
+      
+    </g>
+  </g>
+  <g inkscape:label='mimetypes' transform='translate(-442,-176)' inkscape:groupmode='layer' id='layer14'/>
+  <g inkscape:label='emblems' transform='translate(-442,-176)' inkscape:groupmode='layer' id='layer15' style='display:inline'/>
+</svg>

+ 77 - 16
p/themes/Origine/freshrss.css

@@ -32,7 +32,6 @@
 			}
 			}
 		.header > .item.search input {
 		.header > .item.search input {
 			width: 230px;
 			width: 230px;
-			transition: width 200ms linear;
 		}
 		}
 			.header .item.search input:focus {
 			.header .item.search input:focus {
 				width: 330px;
 				width: 330px;
@@ -210,7 +209,7 @@
 	border-top: 1px solid #aaa;
 	border-top: 1px solid #aaa;
 	border-bottom: 1px solid #aaa;
 	border-bottom: 1px solid #aaa;
 }
 }
-	.day:first-child {
+	#new-article + .day {
 		border-top: none;
 		border-top: none;
 	}
 	}
 	.day .name {
 	.day .name {
@@ -230,6 +229,25 @@
 		text-align: right;
 		text-align: right;
 	}
 	}
 
 
+#new-article {
+	display: none;
+	min-height: 40px;
+	background: #0084CC;
+	text-align: center;
+}
+	#new-article:hover {
+		background: #0066CC;
+	}
+	#new-article > a {
+		display: block;
+		line-height: 40px;
+		color: #fff;
+		font-weight: bold;
+	}
+		#new-article > a:hover {
+			text-decoration: none;
+		}
+
 .flux {
 .flux {
 	border-left: 3px solid #aaa;
 	border-left: 3px solid #aaa;
 	background: #fafafa;
 	background: #fafafa;
@@ -250,6 +268,10 @@
 		background: #fff;
 		background: #fff;
 	}
 	}
 
 
+	.horizontal-list > .item:not(.title):not(.website) > a {
+		display: block;
+	}
+
 	.flux_header {
 	.flux_header {
 		background: inherit;
 		background: inherit;
 		height: 25px;
 		height: 25px;
@@ -277,11 +299,6 @@
 			}
 			}
 		.flux .item.title {
 		.flux .item.title {
 			background: inherit;
 			background: inherit;
-		}
-		.flux:hover .item.title {
-			border-right: 2px solid rgba(127, 127, 127, 0.1);
-			padding-right: 1em;
-			position: absolute;
 		}
 		}
 			.flux .item.title a {
 			.flux .item.title a {
 				color: #000;
 				color: #000;
@@ -292,7 +309,7 @@
 				font-weight: bold;
 				font-weight: bold;
 			}
 			}
 		.flux .item.date {
 		.flux .item.date {
-			width: 200px;
+			width: 145px;
 			padding:0 5px 0 0;
 			padding:0 5px 0 0;
 			text-align: right;
 			text-align: right;
 			font-size: 10px;
 			font-size: 10px;
@@ -657,6 +674,14 @@ select.number option {
 	text-align:right;
 	text-align:right;
 }
 }
 
 
+@media(min-width: 841px) {
+	.flux:not(.current):hover .item.title {
+		max-width: calc(100% - 580px);
+		padding-right: 1.5em;
+		position: absolute;
+	}
+}
+
 @media(max-width: 840px) {
 @media(max-width: 840px) {
 	.header,
 	.header,
 	.aside .btn-important,
 	.aside .btn-important,
@@ -677,11 +702,6 @@ select.number option {
 		display: block;
 		display: block;
 	}
 	}
 
 
-	.content {
-		font-size: 120%;
-		padding: 0;
-	}
-
 	.pagination {
 	.pagination {
 		margin: 0 0 40px;
 		margin: 0 0 40px;
 	}
 	}
@@ -826,9 +846,9 @@ select.number option {
 	background: -ms-linear-gradient(top, #fff 0%, #f0f0f0 100%);
 	background: -ms-linear-gradient(top, #fff 0%, #f0f0f0 100%);
 }
 }
 
 
-.header > .item.search input {
+input.extend {
 	-moz-transition: width 200ms linear;
 	-moz-transition: width 200ms linear;
-	 -webkit-transition: width 200ms linear;
+	-webkit-transition: width 200ms linear;
 	-o-transition: width 200ms linear;
 	-o-transition: width 200ms linear;
 	-ms-transition: width 200ms linear;
 	-ms-transition: width 200ms linear;
 }
 }
@@ -836,7 +856,7 @@ select.number option {
 @media(max-width: 840px) {
 @media(max-width: 840px) {
 	.aside {
 	.aside {
 		-moz-transition: width 200ms linear;
 		-moz-transition: width 200ms linear;
-		 -webkit-transition: width 200ms linear;
+		-webkit-transition: width 200ms linear;
 		-o-transition: width 200ms linear;
 		-o-transition: width 200ms linear;
 		-ms-transition: width 200ms linear;
 		-ms-transition: width 200ms linear;
 	}
 	}
@@ -878,3 +898,44 @@ select.number option {
 		text-decoration: underline;
 		text-decoration: underline;
 	}
 	}
 }
 }
+
+.stat {
+	border:1px solid #aaa;
+	border-radius:10px;
+	box-shadow:2px 2px 5px #aaa;
+	margin:10px 0;
+	padding:0 5px;
+}
+.stat > h2 {
+	border-bottom:1px solid #aaa;
+	margin:0 -5px;
+	padding-left:5px;
+}
+.stat > div {
+	margin:5px 0;
+}
+.stat > table {
+	border-collapse:collapse;
+	margin:5px 0;
+	width:100%;
+}
+.stat > table > thead > tr {
+	border-bottom:2px solid #aaa;
+}
+.stat > table > tbody > tr {
+	border-bottom:1px solid #aaa;
+}
+.stat > table > tbody > tr:last-child {
+	border-bottom:0;
+}
+.stat > table th, .stat > table td {
+	border-left:2px solid #aaa;
+	padding:5px;
+}
+.stat > table th:first-child, .stat > table td:first-child {
+	border-left:0;
+}
+.stat > table td.numeric{
+	margin:5px 0;
+	text-align:center;
+}

+ 34 - 1
p/themes/Origine/global.css

@@ -86,6 +86,13 @@ label {
 	line-height: 25px;
 	line-height: 25px;
 	cursor: pointer;
 	cursor: pointer;
 }
 }
+input {
+	width: 180px;
+}
+textarea {
+	width: 360px;
+	height: 100px;
+}
 input, select, textarea {
 input, select, textarea {
 	display: inline-block;
 	display: inline-block;
 	max-width: 100%;
 	max-width: 100%;
@@ -116,6 +123,10 @@ input, select, textarea {
 		border-color: red;
 		border-color: red;
 		box-shadow: 0 0 2px 1px red;
 		box-shadow: 0 0 2px 1px red;
 	}
 	}
+	input:focus.extend {
+		width: 300px;
+		transition: width 200ms linear;
+	}
 
 
 .form-group {
 .form-group {
 	margin: 0;
 	margin: 0;
@@ -179,8 +190,13 @@ input, select, textarea {
 	}
 	}
 	.stick .btn + .btn,
 	.stick .btn + .btn,
 	.stick .btn + input,
 	.stick .btn + input,
+	.stick .btn + .dropdown > .btn,
 	.stick input + .btn,
 	.stick input + .btn,
-	.stick input + input {
+	.stick input + input,
+	.stick input + .dropdown > .btn,
+	.stick .dropdown + .btn,
+	.stick .dropdown + input,
+	.stick .dropdown + .dropdown > .btn {
 		border-left: none;
 		border-left: none;
 	}
 	}
 	.stick input + .btn {
 	.stick input + .btn {
@@ -505,3 +521,20 @@ input, select, textarea {
 	vertical-align: middle;
 	vertical-align: middle;
 	line-height: 16px;
 	line-height: 16px;
 }
 }
+
+/* Prompt (centré) */
+.prompt > h1, .prompt > p {
+	text-align:center;
+}
+.prompt > form {
+	margin:1em auto 2.5em auto;
+	width:10em;
+}
+.prompt .btn {
+	display:block;
+	margin:.5em auto;
+}
+.prompt input {
+	margin:.4em auto 1.1em auto;
+	width:99%;
+}

BIN
p/themes/icons/favicon-128.png


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


BIN
p/themes/icons/favicon-16.png


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


BIN
p/themes/icons/favicon-32.png


BIN
p/themes/icons/favicon-48.png


BIN
p/themes/icons/favicon-512.png


BIN
p/themes/icons/favicon-64.png


+ 17 - 0
p/themes/icons/favicon.svg

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>

+ 13 - 211
p/themes/icons/icon.svg

@@ -1,213 +1,15 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
+	<title>Logo FreshRSS</title>
 
 
-<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"
-   width="256"
-   height="256"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="icon.svg"
-   inkscape:export-filename="/home/cyp/Bureau/FreshRSS.png"
-   inkscape:export-xdpi="197"
-   inkscape:export-ydpi="197">
-  <title
-     id="title2991">Logo FreshRSS</title>
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1.0000001"
-     inkscape:cx="211.05579"
-     inkscape:cy="58.65406"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     inkscape:window-width="1366"
-     inkscape:window-height="711"
-     inkscape:window-x="0"
-     inkscape:window-y="25"
-     inkscape:window-maximized="1"
-     inkscape:snap-global="true"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0"
-     inkscape:showpageshadow="false" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title>Logo FreshRSS</dc:title>
-        <dc:date>2013-08-20</dc:date>
-        <dc:creator>
-          <cc:Agent>
-            <dc:title>Cyprien POUZENC</dc:title>
-          </cc:Agent>
-        </dc:creator>
-        <dc:rights>
-          <cc:Agent>
-            <dc:title />
-          </cc:Agent>
-        </dc:rights>
-        <cc:license
-           rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
-      </cc:Work>
-      <cc:License
-         rdf:about="http://creativecommons.org/licenses/by-sa/3.0/">
-        <cc:permits
-           rdf:resource="http://creativecommons.org/ns#Reproduction" />
-        <cc:permits
-           rdf:resource="http://creativecommons.org/ns#Distribution" />
-        <cc:requires
-           rdf:resource="http://creativecommons.org/ns#Notice" />
-        <cc:requires
-           rdf:resource="http://creativecommons.org/ns#Attribution" />
-        <cc:permits
-           rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
-        <cc:requires
-           rdf:resource="http://creativecommons.org/ns#ShareAlike" />
-      </cc:License>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Calque 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(27,-823.3622)">
-    <path
-       style="color:#000000;fill:#0062be;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       d="m 101,823.3622 0,23.704 c 57.60125,0 104.2963,46.6944 104.2963,104.296 l 23.7037,0 c 0,-70.6912 -57.30755,-128 -128,-128 z"
-       id="path2990-22"
-       inkscape:connector-curvature="0"
-       inkscape:transform-center-x="-64"
-       inkscape:transform-center-y="-63.999956"
-       inkscape:export-xdpi="197"
-       inkscape:export-ydpi="197" />
-    <path
-       style="color:#000000;fill:#0062be;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       d="m 101,866.0294 0,23.704 c 34.0371,0 61.62963,27.592 61.62963,61.6288 l 23.7037,0 c 0,-47.128 -38.20503,-85.3328 -85.33333,-85.3328 z"
-       id="path2990-3"
-       inkscape:connector-curvature="0"
-       inkscape:transform-center-x="-42.666665"
-       inkscape:transform-center-y="-42.666356"
-       inkscape:export-xdpi="197"
-       inkscape:export-ydpi="197" />
-    <path
-       sodipodi:type="arc"
-       style="color:#000000;fill:#0062be;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       id="path2990"
-       sodipodi:cx="-0.088388346"
-       sodipodi:cy="50"
-       sodipodi:rx="3.9774756"
-       sodipodi:ry="3.9774756"
-       d="M -0.08838827,46.022524 A 3.9774756,3.9774756 0 0 1 3.8890873,50 l -3.97747565,0 z"
-       transform="matrix(8.3432784,0,0,8.3432808,101.73744,534.199)"
-       sodipodi:start="4.712389"
-       sodipodi:end="6.2831853"
-       inkscape:export-xdpi="197"
-       inkscape:export-ydpi="197"
-       inkscape:transform-center-x="-16.592584"
-       inkscape:transform-center-y="-16.591714" />
-    <path
-       style="color:#000000;fill:#0062be;fill-opacity:0.29591836;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       d="m 229,951.3622 -23.7037,0 c 0,57.6016 -46.69488,104.296 -104.2963,104.296 l 0,23.704 c 70.6925,0 128,-57.3072 128,-128 z"
-       id="path2990-22-9"
-       inkscape:connector-curvature="0"
-       inkscape:transform-center-x="-64"
-       inkscape:transform-center-y="64.000044"
-       inkscape:export-xdpi="197"
-       inkscape:export-ydpi="197" />
-    <path
-       style="color:#000000;fill:#0062be;fill-opacity:0.29591836;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       d="m 186.33333,951.3622 -23.7037,0 c 0,34.0368 -27.59253,61.6304 -61.62963,61.6304 l 0,23.704 c 47.12818,0 85.33333,-38.2064 85.33333,-85.3344 z"
-       id="path2990-3-6"
-       inkscape:connector-curvature="0"
-       inkscape:transform-center-x="-42.666665"
-       inkscape:transform-center-y="42.667244"
-       inkscape:export-xdpi="197"
-       inkscape:export-ydpi="197" />
-    <path
-       style="color:#000000;fill:#0062be;fill-opacity:0.29591836;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       d="m -27,951.3622 23.703712,0 c 0,-57.6016 46.694864,-104.296 104.296288,-104.296 l 0,-23.704 c -70.692496,0 -128,57.3088 -128,128 z"
-       id="path2990-22-93"
-       inkscape:connector-curvature="0"
-       inkscape:transform-center-x="64"
-       inkscape:transform-center-y="-63.999956"
-       inkscape:export-xdpi="197"
-       inkscape:export-ydpi="197" />
-    <path
-       style="color:#000000;fill:#0062be;fill-opacity:0.29591836;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       d="m 15.666656,951.3622 23.703712,0 c 0,-34.0368 27.592544,-61.6288 61.629632,-61.6288 l 0,-23.704 c -47.128176,0 -85.333344,38.2048 -85.333344,85.3328 z"
-       id="path2990-3-9"
-       inkscape:connector-curvature="0"
-       inkscape:transform-center-x="42.666672"
-       inkscape:transform-center-y="-42.666356"
-       inkscape:export-xdpi="197"
-       inkscape:export-ydpi="197" />
-    <path
-       sodipodi:type="arc"
-       style="color:#000000;fill:#0062be;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       id="path2990-1"
-       sodipodi:cx="-0.088388346"
-       sodipodi:cy="50"
-       sodipodi:rx="3.9774756"
-       sodipodi:ry="3.9774756"
-       d="M -0.08838827,46.022524 A 3.9774756,3.9774756 0 0 1 3.8890873,50 l -3.97747565,0 z"
-       transform="matrix(0,-8.3432784,8.3432808,0,-315.97884,950.62475)"
-       sodipodi:start="4.712389"
-       sodipodi:end="6.2831853"
-       inkscape:export-xdpi="197"
-       inkscape:export-ydpi="197"
-       inkscape:transform-center-x="16.591713"
-       inkscape:transform-center-y="-16.592581" />
-    <path
-       sodipodi:type="arc"
-       style="color:#000000;fill:#0062be;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       id="path2990-6"
-       sodipodi:cx="-0.088388346"
-       sodipodi:cy="50"
-       sodipodi:rx="3.9774756"
-       sodipodi:ry="3.9774756"
-       d="M -0.08838827,46.022524 A 3.9774756,3.9774756 0 0 1 3.8890873,50 l -3.97747565,0 z"
-       transform="matrix(-8.3432784,0,0,-8.3432808,100.44774,1368.3403)"
-       sodipodi:start="4.712389"
-       sodipodi:end="6.2831853"
-       inkscape:export-xdpi="197"
-       inkscape:export-ydpi="197"
-       inkscape:transform-center-x="16.592584"
-       inkscape:transform-center-y="16.591761" />
-    <path
-       sodipodi:type="arc"
-       style="color:#000000;fill:#0062be;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
-       id="path2990-1-6"
-       sodipodi:cx="-0.088388346"
-       sodipodi:cy="50"
-       sodipodi:rx="3.9774756"
-       sodipodi:ry="3.9774756"
-       d="M -0.08838827,46.022524 A 3.9774756,3.9774756 0 0 1 3.8890873,50 l -3.97747565,0 z"
-       transform="matrix(0,8.3432784,-8.3432808,0,518.16402,951.91446)"
-       sodipodi:start="4.712389"
-       sodipodi:end="6.2831853"
-       inkscape:export-xdpi="197"
-       inkscape:export-ydpi="197"
-       inkscape:transform-center-x="-16.591713"
-       inkscape:transform-center-y="16.592578" />
-  </g>
+	<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>
 </svg>

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