Browse Source

Merge branch 'dev' into beta

Marien Fressinaud 10 năm trước cách đây
mục cha
commit
a1267baa0b
48 tập tin đã thay đổi với 1109 bổ sung107 xóa
  1. 17 0
      CHANGELOG.md
  2. 2 0
      CREDITS.md
  3. 4 6
      app/Controllers/authController.php
  4. 1 0
      app/Controllers/configureController.php
  5. 2 2
      app/Controllers/feedController.php
  6. 7 2
      app/Controllers/javascriptController.php
  7. 3 2
      app/Controllers/updateController.php
  8. 7 0
      app/Models/Category.php
  9. 8 0
      app/Models/ConfigurationSetter.php
  10. 11 2
      app/Models/Context.php
  11. 5 0
      app/Models/LogDAO.php
  12. 1 0
      app/i18n/cz/admin.php
  13. 2 0
      app/i18n/cz/gen.php
  14. 1 0
      app/i18n/de/admin.php
  15. 2 0
      app/i18n/de/gen.php
  16. 1 0
      app/i18n/en/admin.php
  17. 2 0
      app/i18n/en/gen.php
  18. 1 0
      app/i18n/fr/admin.php
  19. 2 0
      app/i18n/fr/gen.php
  20. 183 0
      app/i18n/it/admin.php
  21. 174 0
      app/i18n/it/conf.php
  22. 110 0
      app/i18n/it/feedback.php
  23. 180 0
      app/i18n/it/gen.php
  24. 61 0
      app/i18n/it/index.php
  25. 114 0
      app/i18n/it/install.php
  26. 62 0
      app/i18n/it/sub.php
  27. 11 0
      app/i18n/nl/admin.php
  28. 3 0
      app/i18n/nl/gen.php
  29. 4 12
      app/install.php
  30. 1 1
      app/layout/aside_feed.phtml
  31. 4 0
      app/layout/layout.phtml
  32. 8 1
      app/views/configure/system.phtml
  33. 1 2
      constants.php
  34. 6 0
      data/config.default.php
  35. 6 0
      data/shares.php
  36. 1 1
      index.html
  37. 24 3
      lib/Minz/Request.php
  38. 19 19
      p/api/greader.php
  39. 1 1
      p/index.html
  40. 6 6
      p/themes/BlueLagoon/BlueLagoon.css
  41. 5 5
      p/themes/BlueLagoon/template.css
  42. 6 6
      p/themes/Dark/dark.css
  43. 7 7
      p/themes/Flat/flat.css
  44. 6 6
      p/themes/Origine/origine.css
  45. 6 6
      p/themes/Pafat/pafat.css
  46. 6 6
      p/themes/Screwdriver/screwdriver.css
  47. 6 6
      p/themes/base-theme/base.css
  48. 9 5
      p/themes/base-theme/template.css

+ 17 - 0
CHANGELOG.md

@@ -1,5 +1,22 @@
 # Changelog
 
+## 2015-11-03 FreshRSS 1.2.0 / 1.3.0-beta
+
+* Features
+	* Share with Movim [#992](https://github.com/FreshRSS/FreshRSS/issues/992)
+	* New option to allow robots / search engines [#938](https://github.com/FreshRSS/FreshRSS/issues/938)
+* Security
+	* Invalid logins now return HTTP 403, to be easier to catch (e.g. fail2ban) [#1015](https://github.com/FreshRSS/FreshRSS/issues/1015)
+* UI
+    * Remove "title" field during installation [#858](https://github.com/FreshRSS/FreshRSS/issues/858)
+	* Visual alert on categories containing feeds in error [#984](https://github.com/FreshRSS/FreshRSS/pull/984)
+* I18n
+	* Italian [#1003](https://github.com/FreshRSS/FreshRSS/issues/1003)
+* Misc.
+    * Support reverse proxy [#975](https://github.com/FreshRSS/FreshRSS/issues/975)
+    * Make auto-update server URL alterable [#1019](https://github.com/FreshRSS/FreshRSS/issues/1019)
+
+
 ## 2015-09-12 FreshRSS 1.1.3-beta
 
 * UI

+ 2 - 0
CREDITS.md

@@ -12,6 +12,8 @@ People are sorted by name so please keep this order.
 * [Amaury Carrade](https://github.com/AmauryCarrade): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=AmauryCarrade)
 * [ealdraed](https://github.com/ealdraed): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=ealdraed)
 * [Luc Didry](https://github.com/ldidry): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=ldidry)
+* [Marcus Rohrmoser](https://github.com/mro):
+[contributions](https://github.com/FreshRSS/FreshRSS/commits?author=mro)
 * [Marien Fressinaud](https://github.com/marienfressinaud): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=marienfressinaud), [Web](http://marienfressinaud.fr/)
 * [Melvyn Laïly](https://github.com/yaurthek): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=yaurthek)
 * [Nicolas Elie](https://github.com/nicolaselie): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=nicolaselie)

+ 4 - 6
app/Controllers/authController.php

@@ -123,8 +123,8 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 
 			$conf = get_user_configuration($username);
 			if (is_null($conf)) {
-				Minz_Request::bad(_t('feedback.auth.login.invalid'),
-				                  array('c' => 'auth', 'a' => 'login'));
+				Minz_Error::error(403, array(_t('feedback.auth.login.invalid')), false);
+				return;
 			}
 
 			$ok = FreshRSS_FormAuth::checkCredentials(
@@ -151,8 +151,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 				                  ' user=' . $username .
 				                  ', nonce=' . $nonce .
 				                  ', c=' . $challenge);
-				Minz_Request::bad(_t('feedback.auth.login.invalid'),
-				                  array('c' => 'auth', 'a' => 'login'));
+				Minz_Error::error(403, array(_t('feedback.auth.login.invalid')), false);
 			}
 		} elseif (FreshRSS_Context::$system_conf->unsafe_autologin_enabled) {
 			$username = Minz_Request::param('u', '');
@@ -184,8 +183,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 				                   array('c' => 'index', 'a' => 'index'));
 			} else {
 				Minz_Log::warning('Unsafe password mismatch for user ' . $username);
-				Minz_Request::bad(_t('feedback.auth.login.invalid'),
-				                  array('c' => 'auth', 'a' => 'login'));
+				Minz_Error::error(403, array(_t('feedback.auth.login.invalid')), false);
 			}
 		}
 	}

+ 1 - 0
app/Controllers/configureController.php

@@ -317,6 +317,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
 			$limits['max_categories'] = Minz_Request::param('max-categories', 16384);
 			FreshRSS_Context::$system_conf->limits = $limits;
 			FreshRSS_Context::$system_conf->title = Minz_Request::param('instance-name', 'FreshRSS');
+			FreshRSS_Context::$system_conf->auto_update_url = Minz_Request::param('auto-update-url', false);
 			FreshRSS_Context::$system_conf->save();
 
 			invalidateHttpCache();

+ 2 - 2
app/Controllers/feedController.php

@@ -307,9 +307,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 
 			$pubSubHubbubEnabled = $pubsubhubbubEnabledGeneral && $feed->pubSubHubbubEnabled();
 			if ((!$simplePiePush) && (!$id) && $pubSubHubbubEnabled && ($feed->lastUpdate() > $pshbMinAge)) {
-				$text = 'Skip pull of feed using PubSubHubbub: ' . $url;
+				//$text = 'Skip pull of feed using PubSubHubbub: ' . $url;
 				//Minz_Log::debug($text);
-				file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
+				//file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
 				continue;	//When PubSubHubbub is used, do not pull refresh so often
 			}
 

+ 7 - 2
app/Controllers/javascriptController.php

@@ -43,7 +43,12 @@ class FreshRSS_javascript_Controller extends Minz_ActionController {
 		} else {
 			Minz_Log::notice('Nonce failure due to invalid username!');
 		}
-		$this->view->nonce = '';	//Failure
-		$this->view->salt1 = '';
+		//Failure: Return random data.
+		$this->view->salt1 = sprintf('$2a$%02d$', FreshRSS_user_Controller::BCRYPT_COST);
+		$alphabet = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+		for ($i = 22; $i > 0; $i--) {
+			$this->view->salt1 .= $alphabet[rand(0, 63)];
+		}
+		$this->view->nonce = sha1(rand());
 	}
 }

+ 3 - 2
app/Controllers/updateController.php

@@ -53,7 +53,8 @@ class FreshRSS_update_Controller extends Minz_ActionController {
 			return;
 		}
 
-		$c = curl_init(FRESHRSS_UPDATE_WEBSITE);
+		$auto_update_url = FreshRSS_Context::$system_conf->auto_update_url . '?v=' . FRESHRSS_VERSION;
+		$c = curl_init($auto_update_url);
 		curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
 		curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
 		curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
@@ -70,7 +71,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
 			$this->view->message = array(
 				'status' => 'bad',
 				'title' => _t('gen.short.damn'),
-				'body' => _t('feedback.update.server_not_found', FRESHRSS_UPDATE_WEBSITE)
+				'body' => _t('feedback.update.server_not_found', $auto_update_url)
 			);
 			return;
 		}

+ 7 - 0
app/Models/Category.php

@@ -6,6 +6,7 @@ class FreshRSS_Category extends Minz_Model {
 	private $nbFeed = -1;
 	private $nbNotRead = -1;
 	private $feeds = null;
+	private $hasFeedsWithError = false;
 
 	public function __construct($name = '', $feeds = null) {
 		$this->_name($name);
@@ -16,6 +17,7 @@ class FreshRSS_Category extends Minz_Model {
 			foreach ($feeds as $feed) {
 				$this->nbFeed++;
 				$this->nbNotRead += $feed->nbNotRead();
+				$this->hasFeedsWithError |= $feed->inError();
 			}
 		}
 	}
@@ -51,12 +53,17 @@ class FreshRSS_Category extends Minz_Model {
 			foreach ($this->feeds as $feed) {
 				$this->nbFeed++;
 				$this->nbNotRead += $feed->nbNotRead();
+				$this->hasFeedsWithError |= $feed->inError();
 			}
 		}
 
 		return $this->feeds;
 	}
 
+	public function hasFeedsWithError() {
+		return $this->hasFeedsWithError;
+	}
+
 	public function _id($value) {
 		$this->id = $value;
 	}

+ 8 - 0
app/Models/ConfigurationSetter.php

@@ -378,4 +378,12 @@ class FreshRSS_ConfigurationSetter {
 	private function _unsafe_autologin_enabled(&$data, $value) {
 		$data['unsafe_autologin_enabled'] = $this->handleBool($value);
 	}
+
+	private function _auto_update_url(&$data, $value) {
+		if (!$value) {
+			return;
+		}
+
+		$data['auto_update_url'] = $value;
+	}
 }

+ 11 - 2
app/Models/Context.php

@@ -10,6 +10,7 @@ class FreshRSS_Context {
 	public static $categories = array();
 
 	public static $name = '';
+	public static $description = '';
 
 	public static $total_unread = 0;
 	public static $total_starred = array(
@@ -93,6 +94,13 @@ class FreshRSS_Context {
 		}
 	}
 
+	/**
+	 * Return true if the current request targets a feed (and not a category or all articles), false otherwise.
+	 */
+	public static function isFeed() {
+		return self::$current_get['feed'] != false;
+	}
+
 	/**
 	 * Return true if $get parameter correspond to the $current_get attribute.
 	 */
@@ -146,8 +154,8 @@ class FreshRSS_Context {
 			self::$state = self::$state | FreshRSS_Entry::STATE_FAVORITE;
 			break;
 		case 'f':
-			// We try to find the corresponding feed.
-			$feed = FreshRSS_CategoryDAO::findFeed(self::$categories, $id);
+			// We try to find the corresponding feed. When allowing robots, always retrieve the full feed including description
+			$feed = FreshRSS_Context::$system_conf->allow_robots ? null : FreshRSS_CategoryDAO::findFeed(self::$categories, $id);
 			if ($feed === null) {
 				$feedDAO = FreshRSS_Factory::createFeedDao();
 				$feed = $feedDAO->searchById($id);
@@ -160,6 +168,7 @@ class FreshRSS_Context {
 			self::$current_get['feed'] = $id;
 			self::$current_get['category'] = $feed->category();
 			self::$name = $feed->name();
+			self::$description = $feed->description();
 			self::$get_unread = $feed->nbNotRead();
 			break;
 		case 'c':

+ 5 - 0
app/Models/LogDAO.php

@@ -21,5 +21,10 @@ class FreshRSS_LogDAO {
 
 	public static function truncate() {
 		file_put_contents(join_path(DATA_PATH, 'users', Minz_Session::param('currentUser', '_'), 'log.txt'), '');
+		if (FreshRSS_Auth::hasAccess('admin')) {
+			file_put_contents(join_path(DATA_PATH, 'users', '_', 'log.txt'), '');
+			file_put_contents(join_path(DATA_PATH, 'users', '_', 'log_api.txt'), '');
+			file_put_contents(join_path(DATA_PATH, 'users', '_', 'log_pshb.txt'), '');
+		}
 	}
 }

+ 1 - 0
app/i18n/cz/admin.php

@@ -148,6 +148,7 @@ return array(
 	),
 	'system' => array(
 		'_' => 'System configuration', // @todo translate
+		'auto-update-url' => 'Auto-update server URL', // @todo translate
 		'instance-name' => 'Instance name', // @todo translate
 		'max-categories' => 'Categories per user limit', // @todo translate
 		'max-feeds' => 'Feeds per user limit', // @todo translate

+ 2 - 0
app/i18n/cz/gen.php

@@ -120,6 +120,7 @@ return array(
 		'de' => 'Deutsch',
 		'en' => 'English',
 		'fr' => 'Français',
+		'it' => 'Italiano',
 		'nl' => 'Nederlands',
 	),
 	'menu' => array(
@@ -158,6 +159,7 @@ return array(
 		'email' => 'Email',
 		'facebook' => 'Facebook',
 		'g+' => 'Google+',
+		'movim' => 'Movim',
 		'print' => 'Tisk',
 		'shaarli' => 'Shaarli',
 		'twitter' => 'Twitter',

+ 1 - 0
app/i18n/de/admin.php

@@ -148,6 +148,7 @@ return array(
 	),
 	'system' => array(
 		'_' => 'System configuration', // @todo translate
+		'auto-update-url' => 'Auto-update server URL', // @todo translate
 		'instance-name' => 'Instance name', // @todo translate
 		'max-categories' => 'Categories per user limit', // @todo translate
 		'max-feeds' => 'Feeds per user limit', // @todo translate

+ 2 - 0
app/i18n/de/gen.php

@@ -120,6 +120,7 @@ return array(
 		'de' => 'Deutsch',
 		'en' => 'English',
 		'fr' => 'Français',
+		'it' => 'Italiano',
 		'nl' => 'Nederlands',
 	),
 	'menu' => array(
@@ -158,6 +159,7 @@ return array(
 		'email' => 'E-Mail',
 		'facebook' => 'Facebook',
 		'g+' => 'Google+',
+		'movim' => 'Movim',
 		'print' => 'Drucken',
 		'shaarli' => 'Shaarli',
 		'twitter' => 'Twitter',

+ 1 - 0
app/i18n/en/admin.php

@@ -148,6 +148,7 @@ return array(
 	),
 	'system' => array(
 		'_' => 'System configuration',
+		'auto-update-url' => 'Auto-update server URL',
 		'instance-name' => 'Instance name',
 		'max-categories' => 'Categories per user limit',
 		'max-feeds' => 'Feeds per user limit',

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

@@ -120,6 +120,7 @@ return array(
 		'de' => 'Deutsch',
 		'en' => 'English',
 		'fr' => 'Français',
+		'it' => 'Italiano',
 		'nl' => 'Nederlands',
 	),
 	'menu' => array(
@@ -158,6 +159,7 @@ return array(
 		'email' => 'Email',
 		'facebook' => 'Facebook',
 		'g+' => 'Google+',
+		'movim' => 'Movim',
 		'print' => 'Print',
 		'shaarli' => 'Shaarli',
 		'twitter' => 'Twitter',

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

@@ -148,6 +148,7 @@ return array(
 	),
 	'system' => array(
 		'_' => 'Configuration du système',
+		'auto-update-url' => 'URL du service de mise à jour',
 		'instance-name' => 'Nom de l’instance',
 		'max-categories' => 'Limite de catégories par utilisateur',
 		'max-feeds' => 'Limite de flux par utilisateur',

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

@@ -120,6 +120,7 @@ return array(
 		'de' => 'Deutsch',
 		'en' => 'English',
 		'fr' => 'Français',
+		'it' => 'Italiano',
 		'nl' => 'Nederlands',
 	),
 	'menu' => array(
@@ -158,6 +159,7 @@ return array(
 		'email' => 'Courriel',
 		'facebook' => 'Facebook',
 		'g+' => 'Google+',
+		'movim' => 'Movim',
 		'print' => 'Imprimer',
 		'shaarli' => 'Shaarli',
 		'twitter' => 'Twitter',

+ 183 - 0
app/i18n/it/admin.php

@@ -0,0 +1,183 @@
+<?php
+
+return array(
+	'auth' => array(
+		'allow_anonymous' => 'Consenti la lettura agli utenti anonimi degli articoli dell utente predefinito (%s)',
+		'allow_anonymous_refresh' => 'Consenti agli utenti anonimi di aggiornare gli articoli',
+		'api_enabled' => 'Consenti le <abbr>API</abbr> di accesso <small>(richiesto per le app mobili)</small>',
+		'form' => 'Web form (tradizionale, richiede JavaScript)',
+		'http' => 'HTTP (per gli utenti avanzati con HTTPS)',
+		'none' => 'Nessuno (pericoloso)',
+		'persona' => 'Mozilla Persona (moderno, richiede JavaScript)',
+		'title' => 'Autenticazione',
+		'title_reset' => 'Reset autenticazione',
+		'token' => 'Token di autenticazione',
+		'token_help' => 'Consenti accesso agli RSS dell utente predefinito senza autenticazione:',
+		'type' => 'Metodo di autenticazione',
+		'unsafe_autologin' => 'Consenti accesso automatico non sicuro usando il formato: ',
+	),
+	'check_install' => array(
+		'cache' => array(
+			'nok' => 'Verifica i permessi sulla cartella <em>./data/cache</em>. Il server HTTP deve avere i permessi per scriverci dentro',
+			'ok' => 'I permessi sulla cartella della cache sono corretti.',
+		),
+		'categories' => array(
+			'nok' => 'La tabella delle categorie ha una configurazione errata.',
+			'ok' => 'Tabella delle categorie OK.',
+		),
+		'connection' => array(
+			'nok' => 'La connessione al database non può essere stabilita.',
+			'ok' => 'Connessione al database OK',
+		),
+		'ctype' => array(
+			'nok' => 'Manca una libreria richiesta per il controllo dei caratteri (php-ctype).',
+			'ok' => 'Libreria richiesta per il controllo dei caratteri presente (ctype).',
+		),
+		'curl' => array(
+			'nok' => 'Manca il supporto per cURL (pacchetto php5-curl).',
+			'ok' => 'Estensione cURL presente.',
+		),
+		'data' => array(
+			'nok' => 'Verifica i permessi sulla cartella <em>./data</em>. Il server HTTP deve avere i permessi per scriverci dentro',
+			'ok' => 'I permessi sulla cartella data sono corretti.',
+		),
+		'database' => 'Installazione database',
+		'dom' => array(
+			'nok' => 'Manca una libreria richiesta per leggere DOM (pacchetto php-xml).',
+			'ok' => 'Libreria richiesta per leggere DOM presente.',
+		),
+		'entries' => array(
+			'nok' => 'La tabella Entry ha una configurazione errata.',
+			'ok' => 'Tabella Entry OK.',
+		),
+		'favicons' => array(
+			'nok' => 'Verifica i permessi sulla cartella <em>./data/favicons</em>. Il server HTTP deve avere i permessi per scriverci dentro',
+			'ok' => 'I permessi sulla cartella favicons sono corretti.',
+		),
+		'feeds' => array(
+			'nok' => 'La tabella Feed ha una configurazione errata.',
+			'ok' => 'Tabella Feed OK.',
+		),
+		'files' => 'Installazione files',
+		'json' => array(
+			'nok' => 'Manca il supoorto a JSON (pacchetto php5-json).',
+			'ok' => 'Estensione JSON presente.',
+		),
+		'minz' => array(
+			'nok' => 'Manca il framework Minz.',
+			'ok' => 'Framework Minz presente.',
+		),
+		'pcre' => array(
+			'nok' => 'Manca una libreria richiesta per le regular expressions (php-pcre).',
+			'ok' => 'Libreria richiesta per le regular expressions presente (PCRE).',
+		),
+		'pdo' => array(
+			'nok' => 'Manca PDO o uno degli altri driver supportati (pdo_mysql, pdo_sqlite).',
+			'ok' => 'PDO e altri driver supportati (pdo_mysql, pdo_sqlite).',
+		),
+		'persona' => array(
+			'nok' => 'Verifica i permessi sulla cartella <em>./data/persona</em>. Il server HTTP deve avere i permessi per scriverci dentro',
+			'ok' => 'I permessi sulla cartella Mozilla Persona sono corretti.',
+		),
+		'php' => array(
+			'_' => 'Installazione PHP',
+			'nok' => 'Versione PHP %s FreshRSS richiede almeno la versione %s.',
+			'ok' => 'Versione PHP %s, compatibile con FreshRSS.',
+		),
+		'tables' => array(
+			'nok' => 'Rilevate tabelle mancanti nel database.',
+			'ok' => 'Tutte le tabelle sono presenti nel database.',
+		),
+		'title' => 'Verifica installazione',
+		'tokens' => array(
+			'nok' => 'Verifica i permessi sulla cartella <em>./data/tokens</em>. Il server HTTP deve avere i permessi per scriverci dentro',
+			'ok' => 'I permessi sulla cartella tokens sono corretti.',
+		),
+		'users' => array(
+			'nok' => 'Verifica i permessi sulla cartella <em>./data/users</em>. Il server HTTP deve avere i permessi per scriverci dentro',
+			'ok' => 'I permessi sulla cartella users sono corretti.',
+		),
+		'zip' => array(
+			'nok' => 'Manca estensione ZIP (pacchetto php5-zip).',
+			'ok' => 'Estensione ZIP presente.',
+		),
+	),
+	'extensions' => array(
+		'disabled' => 'Disabilitata',
+		'empty_list' => 'Non ci sono estensioni installate',
+		'enabled' => 'Abilitata',
+		'no_configure_view' => 'Questa estensioni non può essere configurata.',
+		'system' => array(
+			'_' => 'Estensioni di sistema',
+			'no_rights' => 'Estensione di sistema (non hai i permessi su questo tipo)',
+		),
+		'title' => 'Estensioni',
+		'user' => 'Estensioni utente',
+	),
+	'stats' => array(
+		'_' => 'Statistiche',
+		'all_feeds' => 'Tutti i feeds',
+		'category' => 'Categoria',
+		'entry_count' => 'Articoli',
+		'entry_per_category' => 'Articoli per categoria',
+		'entry_per_day' => 'Articoli per giorno (ultimi 30 giorni)',
+		'entry_per_day_of_week' => 'Per giorno della settimana (media: %.2f articoli)',
+		'entry_per_hour' => 'Per ora (media: %.2f articoli)',
+		'entry_per_month' => 'Per mese (media: %.2f articoli)',
+		'entry_repartition' => 'Ripartizione contenuti',
+		'feed' => 'Feed',
+		'feed_per_category' => 'Feeds per categoria',
+		'idle' => 'Feeds non aggiornati',
+		'main' => 'Statistiche principali',
+		'main_stream' => 'Flusso principale',
+		'menu' => array(
+			'idle' => 'Feeds non aggiornati',
+			'main' => 'Statistiche principali',
+			'repartition' => 'Ripartizione articoli',
+		),
+		'no_idle' => 'Non ci sono feed non aggiornati',
+		'number_entries' => '%d articoli',
+		'percent_of_total' => '%% del totale',
+		'repartition' => 'Ripartizione articoli',
+		'status_favorites' => 'Preferiti',
+		'status_read' => 'Letti',
+		'status_total' => 'Totale',
+		'status_unread' => 'Non letti',
+		'title' => 'Statistiche',
+		'top_feed' => 'I migliori 10 feeds',
+	),
+	'system' => array(
+		'_' => 'Configurazione di sistema',
+		'auto-update-url' => 'Auto-update server URL', // @todo translate
+		'instance-name' => 'Nome istanza',
+		'max-categories' => 'Limite categorie per utente',
+		'max-feeds' => 'Limite feeds per utente',
+		'registration' => array(
+			'help' => '0 significa che non esiste limite sui profili',
+			'number' => 'Numero massimo di profili',
+		),
+	),
+	'update' => array(
+		'_' => 'Aggiornamento sistema',
+		'apply' => 'Applica',
+		'check' => 'Controlla la presenza di nuovi aggiornamenti',
+		'current_version' => 'FreshRSS versione %s.',
+		'last' => 'Ultima verifica: %s',
+		'none' => 'Nessun aggiornamento da applicare',
+		'title' => 'Aggiorna sistema',
+	),
+	'user' => array(
+		'articles_and_size' => '%s articoli (%s)',
+		'create' => 'Crea nuovo utente',
+		'email_persona' => 'Indirizzo mail<br /><small>(Login <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
+		'language' => 'Lingua',
+		'number' => ' %d profilo utente creato',
+		'numbers' => 'Sono presenti %d profili utente',
+		'password_form' => 'Password<br /><small>(per il login classico)</small>',
+		'password_format' => 'Almeno 7 caratteri',
+		'title' => 'Gestione utenti',
+		'user_list' => 'Lista utenti',
+		'username' => 'Nome utente',
+		'users' => 'Utenti',
+	),
+);

+ 174 - 0
app/i18n/it/conf.php

@@ -0,0 +1,174 @@
+<?php
+
+return array(
+	'archiving' => array(
+		'_' => 'Archiviazione',
+		'advanced' => 'Avanzate',
+		'delete_after' => 'Rimuovi articoli dopo',
+		'help' => 'Altre opzioni sono disponibili nelle impostazioni dei singoli feed',
+		'keep_history_by_feed' => 'Numero minimo di articoli da mantenere per feed',
+		'optimize' => 'Ottimizza database',
+		'optimize_help' => 'Da fare occasionalmente per ridurre le dimensioni del database',
+		'purge_now' => 'Cancella ora',
+		'title' => 'Archiviazione',
+		'ttl' => 'Non effettuare aggiornamenti per più di',
+	),
+	'display' => array(
+		'_' => 'Visualizzazione',
+		'icon' => array(
+			'bottom_line' => 'Barra in fondo',
+			'entry' => 'Icone degli articoli',
+			'publication_date' => 'Data di pubblicazione',
+			'related_tags' => 'Tags correlati',
+			'sharing' => 'Condivisione',
+			'top_line' => 'Barra in alto',
+		),
+		'language' => 'Lingua',
+		'notif_html5' => array(
+			'seconds' => 'secondi (0 significa nessun timeout)',
+			'timeout' => 'Notifica timeout HTML5',
+		),
+		'theme' => 'Tema',
+		'title' => 'Visualizzazione',
+		'width' => array(
+			'content' => 'Larghezza contenuto',
+			'large' => 'Largo',
+			'medium' => 'Medio',
+			'no_limit' => 'Nessun limite',
+			'thin' => 'Stretto',
+		),
+	),
+	'query' => array(
+		'_' => 'Ricerche personali',
+		'deprecated' => 'Questa query non è più valida. La categoria o il feed di riferimento non stati cancellati.',
+		'filter' => 'Filtro applicato:',
+		'get_all' => 'Mostra tutti gli articoli',
+		'get_category' => 'Mostra la categoria "%s" ',
+		'get_favorite' => 'Mostra articoli preferiti',
+		'get_feed' => 'Mostra feed "%s" ',
+		'no_filter' => 'Nessun filtro',
+		'none' => 'Non hai creato nessuna ricerca personale.',
+		'number' => 'Ricerca n°%d',
+		'order_asc' => 'Mostra prima gli articoli più vecchi',
+		'order_desc' => 'Mostra prima gli articoli più nuovi',
+		'search' => 'Cerca per "%s"',
+		'state_0' => 'Mostra tutti gli articoli',
+		'state_1' => 'Mostra gli articoli letti',
+		'state_2' => 'Mostra gli articoli non letti',
+		'state_3' => 'Mostra tutti gli articoli',
+		'state_4' => 'Mostra gli articoli preferiti',
+		'state_5' => 'Mostra gli articoli preferiti letti',
+		'state_6' => 'Mostra gli articoli preferiti non letti',
+		'state_7' => 'Mostra gli articoli preferiti',
+		'state_8' => 'Non mostrare gli articoli preferiti',
+		'state_9' => 'Mostra gli articoli letti non preferiti',
+		'state_10' => 'Mostra gli articoli non letti e non preferiti',
+		'state_11' => 'Non mostrare gli articoli preferiti',
+		'state_12' => 'Mostra tutti gli articoli',
+		'state_13' => 'Mostra gli articoli letti',
+		'state_14' => 'Mostra gli articoli non letti',
+		'state_15' => 'Mostra tutti gli articoli',
+		'title' => 'Ricerche personali',
+	),
+	'profile' => array(
+		'_' => 'Gestione profili',
+		'delete' => array(
+			'_' => 'Cancellazione account',
+			'warn' => 'Il tuo account e tutti i dati associati saranno cancellati.',
+		),
+		'email_persona' => 'Indirizzo email<br /><small>(Login <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
+		'password_api' => 'Password API<br /><small>(e.g., per applicazioni mobili)</small>',
+		'password_form' => 'Password<br /><small>(per il login classico)</small>',
+		'password_format' => 'Almeno 7 caratteri',
+		'title' => 'Profilo',
+	),
+	'reading' => array(
+		'_' => 'Lettura',
+		'after_onread' => 'Dopo “segna tutto come letto”,',
+		'articles_per_page' => 'Numero di articoli per pagina',
+		'auto_load_more' => 'Carica articoli successivi a fondo pagina',
+		'auto_remove_article' => 'Nascondi articoli dopo la lettura',
+		'mark_updated_article_unread' => 'Segna articoli aggiornati come non letti',
+		'confirm_enabled' => 'Mostra una conferma per “segna tutto come letto”',
+		'display_articles_unfolded' => 'Mostra articoli aperti di predefinito',
+		'display_categories_unfolded' => 'Mostra categorie aperte di predefinito',
+		'hide_read_feeds' => 'Nascondi categorie e feeds con articoli già letti (non funziona se “Mostra tutti gli articoli” è selezionato)',
+		'img_with_lazyload' => 'Usa la modalità "caricamento ritardato" per le immagini',
+		'jump_next' => 'Salta al successivo feed o categoria non letto',
+		'number_divided_when_reader' => 'Diviso 2 nella modalità di lettura.',
+		'read' => array(
+			'article_open_on_website' => 'Quando un articolo è aperto nel suo sito di origine',
+			'article_viewed' => 'Quando un articolo viene letto',
+			'scroll' => 'Scorrendo la pagina',
+			'upon_reception' => 'Alla ricezione del contenuto',
+			'when' => 'Segna articoli come letti…',
+		),
+		'show' => array(
+			'_' => 	'Articoli da visualizzare',
+			'adaptive' => 'Adatta visualizzazione',
+			'all_articles' => 'Mostra tutti gli articoli',
+			'unread' => 'Mostra solo non letti',
+		),
+		'sort' => array(
+			'_' => 'Ordinamento',
+			'newer_first' => 'Prima i più recenti',
+			'older_first' => 'Prima i più vecchi',
+		),
+		'sticky_post' => 'Blocca il contenuto a inizio pagina quando aperto',
+		'title' => 'Lettura',
+		'view' => array(
+			'default' => 'Visualizzazione predefinita',
+			'global' => 'Vista globale per categorie',
+			'normal' => 'Vista elenco',
+			'reader' => 'Modalità di lettura',
+		),
+	),
+	'sharing' => array(
+		'_' => 'Condivisione',
+		'blogotext' => 'Blogotext',
+		'diaspora' => 'Diaspora*',
+		'email' => 'Email',
+		'facebook' => 'Facebook',
+		'g+' => 'Google+',
+		'more_information' => 'Ulteriori informazioni',
+		'print' => 'Stampa',
+		'shaarli' => 'Shaarli',
+		'share_name' => 'Nome condivisione',
+		'share_url' => 'URL condivisione',
+		'title' => 'Condividi',
+		'twitter' => 'Twitter',
+		'wallabag' => 'wallabag',
+	),
+	'shortcut' => array(
+		'_' => 'Comandi tastiera',
+		'article_action' => 'Azioni sugli articoli',
+		'auto_share' => 'Condividi',
+		'auto_share_help' => 'Se è presente un solo servizio di condivisione verrà usato quello, altrimenti usare anche il numero associato.',
+		'close_dropdown' => 'Chiudi menù',
+		'collapse_article' => 'Collassa articoli',
+		'first_article' => 'Salta al primo articolo',
+		'focus_search' => 'Modulo di ricerca',
+		'help' => 'Mostra documentazione',
+		'javascript' => 'JavaScript deve essere abilitato per poter usare i comandi da tastiera',
+		'last_article' => 'Salta all ultimo articolo',
+		'load_more' => 'Carica altri articoli',
+		'mark_read' => 'Segna come letto',
+		'mark_favorite' => 'Segna come preferito',
+		'navigation' => 'Navigazione',
+		'navigation_help' => 'Con il tasto "Shift" i comandi di navigazione verranno applicati ai feeds.<br/>Con il tasto "Alt" i comandi di navigazione verranno applicati alle categorie.',
+		'next_article' => 'Salta al contenuto successivo',
+		'other_action' => 'Altre azioni',
+		'previous_article' => 'Salta al contenuto precedente',
+		'see_on_website' => 'Vai al sito fonte',
+		'shift_for_all_read' => '+ <code>shift</code> per segnare tutti gli articoli come letti',
+		'title' => 'Comandi da tastiera',
+		'user_filter' => 'Accedi alle ricerche personali',
+		'user_filter_help' => 'Se è presente una sola ricerca personale verrà usata quella, altrimenti usare anche il numero associato.',
+	),
+	'user' => array(
+		'articles_and_size' => '%s articoli (%s)',
+		'current' => 'Utente connesso',
+		'is_admin' => 'è amministratore',
+		'users' => 'Utenti',
+	),
+);

+ 110 - 0
app/i18n/it/feedback.php

@@ -0,0 +1,110 @@
+<?php
+
+return array(
+	'admin' => array(
+		'optimization_complete' => 'Ottimizzazione completata',
+	),
+	'access' => array(
+		'denied' => 'Non hai i permessi per accedere a questa pagina',
+		'not_found' => 'Pagina non disponibile',
+	),
+	'auth' => array(
+		'form' => array(
+			'not_set' => 'Si è verificato un problema alla configurazione del sistema di autenticazione. Per favore riprova più tardi.',
+			'set' => 'Sistema di autenticazione tramite Form impostato come predefinito.',
+		),
+		'login' => array(
+			'invalid' => 'Autenticazione non valida',
+			'success' => 'Autenticazione effettuata',
+		),
+		'logout' => array(
+			'success' => 'Disconnessione effettuata',
+		),
+		'no_password_set' => 'Password di amministrazione non impostata. Opzione non disponibile.',
+		'not_persona' => 'Solo il sistema Mozilla Persona può essere resettato.',
+	),
+	'conf' => array(
+		'error' => 'Si è verificato un errore durante il salvataggio della configurazione',
+		'query_created' => 'Ricerca "%s" creata.',
+		'shortcuts_updated' => 'Collegamenti tastiera aggiornati',
+		'updated' => 'Configurazione aggiornata',
+	),
+	'extensions' => array(
+		'already_enabled' => '%s è già abilitata',
+		'disable' => array(
+			'ko' => '%s non può essere disabilitata. <a href="%s">Verifica i logs</a> per dettagli.',
+			'ok' => '%s è disabilitata',
+		),
+		'enable' => array(
+			'ko' => '%s non può essere abilitata. <a href="%s">Verifica i logs</a> per dettagli.',
+			'ok' => '%s è ora abilitata',
+		),
+		'no_access' => 'Accesso negato a %s',
+		'not_enabled' => '%s non abilitato',
+		'not_found' => '%s non disponibile',
+	),
+	'import_export' => array(
+		'export_no_zip_extension' => 'Estensione Zip non presente sul server. Per favore esporta i files singolarmente.',
+		'feeds_imported' => 'I tuoi feed sono stati importati e saranno aggiornati',
+		'feeds_imported_with_errors' => 'I tuoi feeds sono stati importati ma si sono verificati alcuni errori',
+		'file_cannot_be_uploaded' => 'Il file non può essere caricato!',
+		'no_zip_extension' => 'Estensione Zip non presente sul server.',
+		'zip_error' => 'Si è verificato un errore importando il file Zip',
+	),
+	'sub' => array(
+		'actualize' => 'Aggiorna',
+		'category' => array(
+			'created' => 'Categoria %s creata.',
+			'deleted' => 'Categoria cancellata',
+			'emptied' => 'Categoria svuotata',
+			'error' => 'Categoria non aggiornata',
+			'name_exists' => 'Categoria già esistente.',
+			'no_id' => 'Categoria senza ID.',
+			'no_name' => 'Il nome della categoria non può essere lasciato vuoto.',
+			'not_delete_default' => 'Non puoi cancellare la categoria predefinita!',
+			'not_exist' => 'La categoria non esite!',
+			'over_max' => 'Hai raggiunto il numero limite di categorie (%d)',
+			'updated' => 'Categoria aggiornata.',
+		),
+		'feed' => array(
+			'actualized' => '<em>%s</em> aggiornato',
+			'actualizeds' => 'RSS feeds aggiornati',
+			'added' => 'RSS feed <em>%s</em> aggiunti',
+			'already_subscribed' => 'Hai già sottoscritto <em>%s</em>',
+			'deleted' => 'Feed cancellato',
+			'error' => 'Feed non aggiornato',
+			'internal_problem' => 'RSS feed non aggiunto. <a href="%s">Verifica i logs</a> per dettagli.',
+			'invalid_url' => 'URL <em>%s</em> non valido',
+			'marked_read' => 'Feeds segnati come letti',
+			'n_actualized' => '%d feeds aggiornati',
+			'n_entries_deleted' => '%d articoli cancellati',
+			'no_refresh' => 'Nessun aggiornamento disponibile…',
+			'not_added' => '<em>%s</em> non può essere aggiunto',
+			'over_max' => 'Hai raggiunto il numero limite di feed (%d)',
+			'updated' => 'Feed aggiornato',
+		),
+		'purge_completed' => 'Svecchiamento completato (%d articoli cancellati)',
+	),
+	'update' => array(
+		'can_apply' => 'FreshRSS verrà aggiornato alla <strong>versione %s</strong>.',
+		'error' => 'Il processo di aggiornamento ha riscontrato il seguente errore: %s',
+		'file_is_nok' => 'Verifica i permessi della cartella <em>%s</em>. Il server HTTP deve avere i permessi per la scrittura ',
+		'finished' => 'Aggiornamento completato con successo!',
+		'none' => 'Nessun aggiornamento disponibile',
+		'server_not_found' => 'Server per aggiornamento non disponibile. [%s]',
+	),
+	'user' => array(
+		'created' => array(
+			'_' => 'Utente %s creato',
+			'error' => 'Errore nella creazione utente %s ',
+		),
+		'deleted' => array(
+			'_' => 'Utente %s cancellato',
+			'error' => 'Utente %s non cancellato',
+		),
+	),
+	'profile' => array(
+		'error' => 'Il tuo profilo non può essere modificato',
+		'updated' => 'Il tuo profilo è stato modificato',
+	),
+);

+ 180 - 0
app/i18n/it/gen.php

@@ -0,0 +1,180 @@
+<?php
+
+return array(
+	'action' => array(
+		'actualize' => 'Aggiorna',
+		'back_to_rss_feeds' => '← Indietro',
+		'cancel' => 'Annulla',
+		'create' => 'Crea',
+		'disable' => 'Disabilita',
+		'empty' => 'Vuoto',
+		'enable' => 'Abilita',
+		'export' => 'Esporta',
+		'filter' => 'Filtra',
+		'import' => 'Importa',
+		'manage' => 'Gestisci',
+		'mark_read' => 'Segna come letto',
+		'mark_favorite' => 'Segna come preferito',
+		'remove' => 'Rimuovi',
+		'see_website' => 'Vai al sito',
+		'submit' => 'Conferma',
+		'truncate' => 'Cancella tutti gli articoli',
+	),
+	'auth' => array(
+		'email' => 'Indirizzo email',
+		'keep_logged_in' => 'Ricorda i dati <small>(1 mese)</small>',
+		'login' => 'Accedi',
+		'login_persona' => 'Accedi con Mozilla Persona',
+		'login_persona_problem' => 'Problemi di connessione con Mozilla Persona?',
+		'logout' => 'Esci',
+		'password' => array(
+			'_' => 'Password',
+			'format' => '<small>almeno 7 caratteri</small>',
+		),
+		'registration' => array(
+			'_' => 'Nuovo profilo',
+			'ask' => 'Vuoi creare un nuovo profilo?',
+			'title' => 'Creazione profilo',
+		),
+		'reset' => 'Reset autenticazione',
+		'username' => array(
+			'_' => 'Username',
+			'admin' => 'Username amministratore',
+			'format' => '<small>massimo 16 caratteri alfanumerici</small>',
+		),
+		'will_reset' => 'Il sistema di autenticazione verrà resettato: un form verrà usato per Mozilla Persona.',
+	),
+	'date' => array(
+		'Apr' => '\\A\\p\\r\\i\\l\\e',
+		'Aug' => '\\A\\g\\o\\s\\t\\o',
+		'Dec' => '\\D\\i\\c\\e\\m\\b\\r\\e',
+		'Feb' => '\\F\\e\\b\\b\\r\\a\\i\\o',
+		'Jan' => '\\G\\e\\n\\u\\a\\i\\o',
+		'Jul' => '\\L\\u\\g\\l\\i\\o',
+		'Jun' => '\\G\\i\\u\\g\\n\\o',
+		'Mar' => '\\M\\a\\r\\z\\o',
+		'May' => '\\M\\a\\g\\g\\i\\o',
+		'Nov' => '\\N\\o\\v\\e\\m\\b\\r\\e',
+		'Oct' => '\\O\\t\\t\\o\\b\\r\\e',
+		'Sep' => '\\S\\e\\t\\t\\e\\m\\b\\r\\e',
+		'apr' => 'apr',
+		'april' => 'Apr',
+		'aug' => 'aug',
+		'august' => 'Aug',
+		'before_yesterday' => 'Meno recenti',
+		'dec' => 'dec',
+		'december' => 'Dec',
+		'feb' => 'feb',
+		'february' => 'Feb',
+		'format_date' => 'j\\ %s Y',
+		'format_date_hour' => 'j\\ %s Y \\o\\r\\e H\\:i',
+		'fri' => 'Fri',
+		'jan' => 'jan',
+		'january' => 'Jan',
+		'jul' => 'jul',
+		'july' => 'Jul',
+		'jun' => 'jun',
+		'june' => 'Jun',
+		'last_3_month' => 'Ultimi 3 mesi',
+		'last_6_month' => 'Ultimi 6 mesi',
+		'last_month' => 'Ultimo mese',
+		'last_week' => 'Ultima settimana',
+		'last_year' => 'Ultimo anno',
+		'mar' => 'mar',
+		'march' => 'Mar',
+		'may' => 'May',
+		'mon' => 'Mon',
+		'month' => 'mesi',
+		'nov' => 'nov',
+		'november' => 'Nov',
+		'oct' => 'oct',
+		'october' => 'Oct',
+		'sat' => 'Sat',
+		'sep' => 'sep',
+		'september' => 'Sep',
+		'sun' => 'Sun',
+		'thu' => 'Thu',
+		'today' => 'Oggi',
+		'tue' => 'Tue',
+		'wed' => 'Wed',
+		'yesterday' => 'Ieri',
+	),
+	'freshrss' => array(
+		'_' => 'Feed RSS Reader',
+		'about' => 'Informazioni',
+	),
+	'js' => array(
+		'category_empty' => 'Categoria vuota',
+		'confirm_action' => 'Sei sicuro di voler continuare?',
+		'confirm_action_feed_cat' => 'Sei sicuro di voler continuare? Verranno persi i preferiti e le ricerche utente correlate!',
+		'feedback' => array(
+			'body_new_articles' => 'Ci sono \\d nuovi articoli da leggere.',
+			'request_failed' => 'Richiesta fallita, probabilmente a causa di problemi di connessione',
+			'title_new_articles' => 'Feed RSS Reader: nuovi articoli!',
+		),
+		'new_article' => 'Sono disponibili nuovi articoli, clicca qui per caricarli.',
+		'should_be_activated' => 'JavaScript deve essere abilitato',
+	),
+	'lang' => array(
+		'cz' => 'Čeština',
+		'de' => 'Deutsch',
+		'en' => 'English',
+		'fr' => 'Français',
+		'it' => 'Italiano',
+		'nl' => 'Nederlands',
+	),
+	'menu' => array(
+		'about' => 'Informazioni',
+		'admin' => 'Amministrazione',
+		'archiving' => 'Archiviazione',
+		'authentication' => 'Autenticazione',
+		'check_install' => 'Installazione',
+		'configuration' => 'Configurazione',
+		'display' => 'Visualizzazione',
+		'extensions' => 'Estensioni',
+		'logs' => 'Logs',
+		'queries' => 'Ricerche personali',
+		'reading' => 'Lettura',
+		'search' => 'Ricerca parole o #tags',
+		'sharing' => 'Condivisione',
+		'shortcuts' => 'Comandi tastiera',
+		'stats' => 'Statistiche',
+		'system' => 'Configurazione sistema',
+		'update' => 'Aggiornamento',
+		'user_management' => 'Gestione utenti',
+		'user_profile' => 'Profilo',
+	),
+	'pagination' => array(
+		'first' => 'Prima',
+		'last' => 'Ultima',
+		'load_more' => 'Carica altri articoli',
+		'mark_all_read' => 'Segna tutto come letto',
+		'next' => 'Successiva',
+		'nothing_to_load' => 'Non ci sono altri articoli',
+		'previous' => 'Precedente',
+	),
+	'share' => array(
+		'blogotext' => 'Blogotext',
+		'diaspora' => 'Diaspora*',
+		'email' => 'Email',
+		'facebook' => 'Facebook',
+		'g+' => 'Google+',
+		'print' => 'Stampa',
+		'shaarli' => 'Shaarli',
+		'twitter' => 'Twitter',
+		'wallabag' => 'wallabag',
+	),
+	'short' => array(
+		'attention' => 'Attenzione!',
+		'blank_to_disable' => 'Lascia vuoto per disabilitare',
+		'by_author' => 'di <em>%s</em>',
+		'by_default' => 'predefinito',
+		'damn' => 'Ops!',
+		'default_category' => 'Senza categoria',
+		'no' => 'No',
+		'not_applicable' => 'Non disponibile',
+		'ok' => 'OK!',
+		'or' => 'o',
+		'yes' => 'Si',
+	),
+);

+ 61 - 0
app/i18n/it/index.php

@@ -0,0 +1,61 @@
+<?php
+
+return array(
+	'about' => array(
+		'_' => 'Informazioni',
+		'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>',
+		'bugs_reports' => 'Bugs',
+		'credits' => 'Crediti',
+		'credits_content' => 'Alcuni elementi di design provengono da <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> sebbene FreshRSS non usi questo framework. Le <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">icone</a> provengono dal progetto <a href="https://www.gnome.org/">GNOME</a>. Il carattere <em>Open Sans</em> è stato creato da <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Le Favicons vengono estratte con le API <a href="https://getfavicon.appspot.com/">getFavicon</a>. FreshRSS è basato su <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, un framework PHP.',
+		'freshrss_description' => 'FreshRSS è un aggregatore di feeds RSS da installare sul proprio host come <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> o <a href="http://projet.idleman.fr/leed/">Leed</a>. Leggero e facile da mantenere pur essendo molto configurabile e potente.',
+		'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">su Github</a>',
+		'license' => 'Licenza',
+		'project_website' => 'Sito del progetto',
+		'title' => 'Informazioni',
+		'version' => 'Versione',
+		'website' => 'Sito',
+	),
+	'feed' => array(
+		'add' => 'Aggiungi un Feed RSS',
+		'empty' => 'Non ci sono articoli da mostrare.',
+		'rss_of' => 'RSS feed di %s',
+		'title' => 'RSS feeds',
+		'title_global' => 'Vista globale per categorie',
+		'title_fav' => 'Preferiti',
+	),
+	'log' => array(
+		'_' => 'Logs',
+		'clear' => 'Svuota logs',
+		'empty' => 'File di log vuoto',
+		'title' => 'Logs',
+	),
+	'menu' => array(
+		'about' => 'Informazioni',
+		'add_query' => 'Aggiungi ricerca',
+		'before_one_day' => 'Giorno precedente',
+		'before_one_week' => 'Settimana precedente',
+		'favorites' => 'Preferiti (%s)',
+		'global_view' => 'Vista globale per categorie',
+		'main_stream' => 'Flusso principale',
+		'mark_all_read' => 'Segna tutto come letto',
+		'mark_cat_read' => 'Segna la categoria come letta',
+		'mark_feed_read' => 'Segna il feed come letto',
+		'newer_first' => 'Mostra prima i recenti',
+		'non-starred' => 'Escludi preferiti',
+		'normal_view' => 'Vista elenco',
+		'older_first' => 'Ordina per meno recenti',
+		'queries' => 'Chiavi di ricerca',
+		'read' => 'Mostra solo letti',
+		'reader_view' => 'Modalità di lettura',
+		'rss_view' => 'RSS feed',
+		'search_short' => 'Cerca',
+		'starred' => 'Mostra solo preferiti',
+		'stats' => 'Statistiche',
+		'subscription' => 'Gestione sottoscrizioni',
+		'unread' => 'Mostra solo non letti',
+	),
+	'share' => 'Condividi',
+	'tag' => array(
+		'related' => 'Tags correlati',
+	),
+);

+ 114 - 0
app/i18n/it/install.php

@@ -0,0 +1,114 @@
+<?php
+
+return array(
+	'action' => array(
+		'finish' => 'Installazione completata',
+		'fix_errors_before' => 'Per favore correggi gli errori prima di passare al passaggio successivo.',
+		'keep_install' => 'Mantieni installazione precedente',
+		'next_step' => 'Vai al prossimo passaggio',
+		'reinstall' => 'Reinstalla FreshRSS',
+	),
+	'auth' => array(
+		'email_persona' => 'Indirizzo mail<br /><small>(per <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>',
+		'form' => 'Web form (tradizionale, richiede JavaScript)',
+		'http' => 'HTTP (per gli utenti avanzati con HTTPS)',
+		'none' => 'Nessuno (pericoloso)',
+		'password_form' => 'Password<br /><small>(per il login tramite Web-form tradizionale)</small>',
+		'password_format' => 'Almeno 7 caratteri',
+		'persona' => 'Mozilla Persona (moderno, richiede JavaScript)',
+		'type' => 'Metodo di autenticazione',
+	),
+	'bdd' => array(
+		'_' => 'Database',
+		'conf' => array(
+			'_' => 'Configurazione database',
+			'ko' => 'Verifica le informazioni del database.',
+			'ok' => 'Le configurazioni del database sono state salvate.',
+		),
+		'host' => 'Host',
+		'prefix' => 'Prefisso tabella',
+		'password' => 'HTTP password',
+		'type' => 'Tipo di database',
+		'username' => 'HTTP username',
+	),
+	'check' => array(
+		'_' => 'Controlli',
+		'already_installed' => 'FreshRSS risulta già installato!',
+		'cache' => array(
+			'nok' => 'Verifica i permessi sulla cartella <em>./data/cache</em>. Il server HTTP deve avere i permessi per scriverci dentro',
+			'ok' => 'I permessi sulla cartella della cache sono corretti.',
+		),
+		'ctype' => array(
+			'nok' => 'Manca una libreria richiesta per il controllo dei caratteri (php-ctype).',
+			'ok' => 'Libreria richiesta per il controllo dei caratteri presente (ctype).',
+		),
+		'curl' => array(
+			'nok' => 'Manca il supporto per cURL (pacchetto php5-curl).',
+			'ok' => 'Estensione cURL presente.',
+		),
+		'data' => array(
+			'nok' => 'Verifica i permessi sulla cartella <em>./data</em>. Il server HTTP deve avere i permessi per scriverci dentro',
+			'ok' => 'I permessi sulla cartella data sono corretti.',
+		),
+		'dom' => array(
+			'nok' => 'Manca una libreria richiesta per leggere DOM (pacchetto php-xml).',
+			'ok' => 'Libreria richiesta per leggere DOM presente.',
+		),
+		'favicons' => array(
+			'nok' => 'Verifica i permessi sulla cartella <em>./data/favicons</em>. Il server HTTP deve avere i permessi per scriverci dentro',
+			'ok' => 'I permessi sulla cartella favicons sono corretti.',
+		),
+		'http_referer' => array(
+			'nok' => 'Per favore verifica che non stai alterando il tuo HTTP REFERER.',
+			'ok' => 'Il tuo HTTP REFERER riconosciuto corrisponde al tuo server.',
+		),
+		'minz' => array(
+			'nok' => 'Manca il framework Minz.',
+			'ok' => 'Framework Minz presente.',
+		),
+		'pcre' => array(
+			'nok' => 'Manca una libreria richiesta per le regular expressions (php-pcre).',
+			'ok' => 'Libreria richiesta per le regular expressions presente (PCRE).',
+		),
+		'pdo' => array(
+			'nok' => 'Manca PDO o uno degli altri driver supportati (pdo_mysql, pdo_sqlite).',
+			'ok' => 'PDO e altri driver supportati (pdo_mysql, pdo_sqlite).',
+		),
+		'persona' => array(
+			'nok' => 'Verifica i permessi sulla cartella <em>./data/persona</em>. Il server HTTP deve avere i permessi per scriverci dentro',
+			'ok' => 'I permessi sulla cartella Mozilla Persona sono corretti.',
+		),
+		'php' => array(
+			'_' => 'Installazione PHP',
+			'nok' => 'Versione di PHP %s FreshRSS richiede almeno la versione %s.',
+			'ok' => 'Versione di PHP %s, compatibile con FreshRSS.',
+		),
+		'users' => array(
+			'nok' => 'Verifica i permessi sulla cartella <em>./data/users</em>. Il server HTTP deve avere i permessi per scriverci dentro',
+			'ok' => 'I permessi sulla cartella users sono corretti.',
+		),
+	),
+	'conf' => array(
+		'_' => 'Configurazioni generali',
+		'ok' => 'Configurazioni generali salvate.',
+	),
+	'congratulations' => 'Congratulazione!',
+	'default_user' => 'Username utente predefinito <small>(massimo 16 caratteri alfanumerici)</small>',
+	'delete_articles_after' => 'Rimuovi articoli dopo',
+	'fix_errors_before' => 'Per favore correggi gli errori prima di passare al passaggio successivo.',
+	'javascript_is_better' => 'FreshRSS funziona meglio con JavaScript abilitato',
+	'js' => array(
+		'confirm_reinstall' => 'Reinstallando FreshRSS perderai la configurazione precedente. Sei sicuro di voler procedere?',
+	),
+	'language' => array(
+		'_' => 'Lingua',
+		'choose' => 'Seleziona la lingua per FreshRSS',
+		'defined' => 'Lingua impostata.',
+	),
+	'not_deleted' => 'Qualcosa non ha funzionato; devi cancellare il file <em>%s</em> manualmente.',
+	'ok' => 'Processo di installazione terminato con successo.',
+	'step' => 'Passaggio %d',
+	'steps' => 'Passaggi',
+	'title' => 'Installazione · FreshRSS',
+	'this_is_the_end' => 'Fine',
+);

+ 62 - 0
app/i18n/it/sub.php

@@ -0,0 +1,62 @@
+<?php
+
+return array(
+	'category' => array(
+		'_' => 'Categoria',
+		'add' => 'Aggiungi una categoria',
+		'empty' => 'Categoria vuota',
+		'new' => 'Nuova categoria',
+	),
+	'feed' => array(
+		'add' => 'Aggiungi un Feed RSS',
+		'advanced' => 'Avanzate',
+		'archiving' => 'Archiviazione',
+		'auth' => array(
+			'configuration' => 'Autenticazione',
+			'help' => 'Accesso per feeds protetti',
+			'http' => 'Autenticazione HTTP',
+			'password' => 'HTTP password',
+			'username' => 'HTTP username',
+		),
+		'css_help' => 'In caso di RSS feeds troncati (attenzione, richiede molto tempo!)',
+		'css_path' => 'Percorso del foglio di stile CSS del sito di origine',
+		'description' => 'Descrizione',
+		'empty' => 'Questo feed non contiene articoli. Per favore verifica il sito direttamente.',
+		'error' => 'Questo feed ha generato un errore. Per favore verifica se ancora disponibile.',
+		'in_main_stream' => 'Mostra in homepage',
+		'informations' => 'Informazioni',
+		'keep_history' => 'Numero minimo di articoli da mantenere',
+		'moved_category_deleted' => 'Cancellando una categoria i feed al suo interno verranno classificati automaticamente come <em>%s</em>.',
+		'no_selected' => 'Nessun feed selezionato.',
+		'number_entries' => '%d articoli',
+		'stats' => 'Statistiche',
+		'think_to_add' => 'Aggiungi feed.',
+		'title' => 'Titolo',
+		'title_add' => 'Aggiungi RSS feed',
+		'ttl' => 'Non aggiornare automaticamente piu di',
+		'url' => 'Feed URL',
+		'validator' => 'Controlla la validita del feed ',
+		'website' => 'URL del sito',
+		'pubsubhubbub' => 'Notifica istantanea con PubSubHubbub',
+	),
+	'import_export' => array(
+		'export' => 'Esporta',
+		'export_opml' => 'Esporta tutta la lista dei feed (OPML)',
+		'export_starred' => 'Esporta i tuoi preferiti',
+		'feed_list' => 'Elenco di %s articoli',
+		'file_to_import' => 'File da importare<br />(OPML, Json o Zip)',
+		'file_to_import_no_zip' => 'File da importare<br />(OPML o Json)',
+		'import' => 'Importa',
+		'starred_list' => 'Elenco articoli preferiti',
+		'title' => 'Importa / esporta',
+	),
+	'menu' => array(
+		'bookmark' => 'Bookmark (trascina nei preferiti)',
+		'import_export' => 'Importa / esporta',
+		'subscription_management' => 'Gestione sottoscrizioni',
+	),
+	'title' => array(
+		'_' => 'Gestione sottoscrizioni',
+		'feed_management' => 'Gestione RSS feeds',
+	),
+);

+ 11 - 0
app/i18n/nl/admin.php

@@ -146,6 +146,17 @@ return array(
 		'title' => 'Statistieken',
 		'top_feed' => 'Top tien feeds',
 	),
+	'system' => array(
+		'_' => 'System configuration', // @todo translate
+		'auto-update-url' => 'Auto-update server URL', // @todo translate
+		'instance-name' => 'Instance name', // @todo translate
+		'max-categories' => 'Categories per user limit', // @todo translate
+		'max-feeds' => 'Feeds per user limit', // @todo translate
+		'registration' => array(
+			'help' => '0 means that there is no account limit', // @todo translate
+			'number' => 'Max number of accounts', // @todo translate
+		),
+	),
 	'update' => array(
 		'_' => 'Versie controle',
 		'apply' => 'Toepassen',

+ 3 - 0
app/i18n/nl/gen.php

@@ -120,6 +120,7 @@ return array(
 		'de' => 'Deutsch',
 		'en' => 'English',
 		'fr' => 'Français',
+		'it' => 'Italiano',
 		'nl' => 'Nederlands',
 	),
 	'menu' => array(
@@ -138,6 +139,7 @@ return array(
 		'sharing' => 'Delen',
 		'shortcuts' => 'Snelle toegang',
 		'stats' => 'Statistieken',
+		'system' => 'System configuration', // @todo translate
 		'update' => 'Versie controle',
 		'user_management' => 'Beheer gebruikers',
 		'user_profile' => 'Profiel',
@@ -157,6 +159,7 @@ return array(
 		'email' => 'Email',
 		'facebook' => 'Facebook',
 		'g+' => 'Google+',
+		'movim' => 'Movim',
 		'print' => 'Print',
 		'shaarli' => 'Shaarli',
 		'twitter' => 'Twitter',

+ 4 - 12
app/install.php

@@ -122,7 +122,8 @@ function saveStep1() {
 function saveStep2() {
 	$user_default_config = Minz_Configuration::get('default_user');
 	if (!empty($_POST)) {
-		$_SESSION['title'] = substr(trim(param('title', _t('gen.freshrss'))), 0, 25);
+		$system_default_config = Minz_Configuration::get('default_system');
+		$_SESSION['title'] = $system_default_config->title;
 		$_SESSION['old_entries'] = param('old_entries', $user_default_config->old_entries);
 		$_SESSION['auth_type'] = param('auth_type', 'form');
 		$_SESSION['default_user'] = substr(preg_replace('/[^a-zA-Z0-9]/', '', param('default_user', '')), 0, 16);
@@ -138,8 +139,7 @@ function saveStep2() {
 			$_SESSION['passwordHash'] = $passwordHash;
 		}
 
-		if (empty($_SESSION['title']) ||
-		    empty($_SESSION['old_entries']) ||
+		if (empty($_SESSION['old_entries']) ||
 		    empty($_SESSION['auth_type']) ||
 		    empty($_SESSION['default_user'])) {
 			return false;
@@ -374,8 +374,7 @@ function freshrss_already_installed() {
 }
 
 function checkStep2() {
-	$conf = !empty($_SESSION['title']) &&
-	        !empty($_SESSION['old_entries']) &&
+	$conf = !empty($_SESSION['old_entries']) &&
 	        isset($_SESSION['mail_login']) &&
 	        !empty($_SESSION['default_user']);
 
@@ -658,13 +657,6 @@ function printStep2() {
 	<form action="index.php?step=2" method="post">
 		<legend><?php echo _t('install.conf'); ?></legend>
 
-		<div class="form-group">
-			<label class="group-name" for="title"><?php echo _t('install.title'); ?></label>
-			<div class="group-controls">
-				<input type="text" id="title" name="title" value="<?php echo isset($_SESSION['title']) ? $_SESSION['title'] : _t('gen.freshrss'); ?>" tabindex="1" />
-			</div>
-		</div>
-
 		<div class="form-group">
 			<label class="group-name" for="old_entries"><?php echo _t('install.delete_articles_after'); ?></label>
 			<div class="group-controls">

+ 1 - 1
app/layout/aside_feed.phtml

@@ -45,7 +45,7 @@
 		<li class="tree-folder category<?php echo $c_active ? ' active' : ''; ?>" data-unread="<?php echo $cat->nbNotRead(); ?>">
 			<div class="tree-folder-title">
 				<a class="dropdown-toggle" href="#"><?php echo _i($c_show ? 'up' : 'down'); ?></a>
-				<a class="title" data-unread="<?php echo format_number($cat->nbNotRead()); ?>" href="<?php echo _url('index', 'index', 'get', 'c_' . $cat->id()); ?>"><?php echo $cat->name(); ?></a>
+				<a class="title<?php echo $cat->hasFeedsWithError() ? ' error' : ''; ?>" data-unread="<?php echo format_number($cat->nbNotRead()); ?>" href="<?php echo _url('index', 'index', 'get', 'c_' . $cat->id()); ?>"><?php echo $cat->name(); ?></a>
 			</div>
 
 			<ul class="tree-folder-items<?php echo $c_show ? ' active' : ''; ?>">

+ 4 - 0
app/layout/layout.phtml

@@ -36,7 +36,11 @@
 		<meta name="apple-mobile-web-app-status-bar-style" content="black" />
 		<meta name="apple-mobile-web-app-title" content="<?php echo FreshRSS_Context::$system_conf->title; ?>">
 		<meta name="msapplication-TileColor" content="#FFF" />
+<?php if (FreshRSS_Context::$system_conf->allow_robots) { ?>
+		<meta name="description" content="<?php echo htmlspecialchars(FreshRSS_Context::$name . ' | ' . FreshRSS_Context::$description, ENT_COMPAT, 'UTF-8'); ?>" />
+<?php } else { ?>
 		<meta name="robots" content="noindex,nofollow" />
+<?php } ?>
 	</head>
 	<body class="<?php echo Minz_Request::param('output', 'normal'); ?>">
 <?php $this->partial('header'); ?>

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

@@ -9,7 +9,14 @@
 		<div class="form-group">
 			<label class="group-name" for="instance-name"><?php echo _t('admin.system.instance-name'); ?></label>
 			<div class="group-controls">
-			    <input type="text" id="max-feeds" name="instance-name" value="<?php echo FreshRSS_Context::$system_conf->title; ?>" min="1" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->title; ?>"/>
+			    <input type="text" class="extend" id="instance-name" name="instance-name" value="<?php echo FreshRSS_Context::$system_conf->title; ?>" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->title; ?>"/>
+			</div>
+		</div>
+
+		<div class="form-group">
+			<label class="group-name" for="auto-update-url"><?php echo _t('admin.system.auto-update-url'); ?></label>
+			<div class="group-controls">
+			    <input type="text" class="extend" id="auto-update-url" name="auto-update-url" value="<?php echo FreshRSS_Context::$system_conf->auto_update_url; ?>" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->auto_update_url; ?>"/>
 			</div>
 		</div>
 

+ 1 - 2
constants.php

@@ -1,7 +1,6 @@
 <?php
-define('FRESHRSS_VERSION', '1.1.3-beta');
+define('FRESHRSS_VERSION', '1.1.4-dev');
 define('FRESHRSS_WEBSITE', 'http://freshrss.org');
-define('FRESHRSS_UPDATE_WEBSITE', 'https://update.freshrss.org?v=' . FRESHRSS_VERSION);
 define('FRESHRSS_WIKI', 'http://doc.freshrss.org');
 
 // PHP text output compression http://php.net/ob_gzhandler (better to do it at Web server level)

+ 6 - 0
data/config.default.php

@@ -18,6 +18,9 @@ return array(
 	# https://freshrss.example.net/
 	'base_url' => '',
 
+	# Specify address of the FreshRSS auto-update server.
+	'auto_update_url' => 'https://update.freshrss.org',
+
 	# Natural language of the user interface, e.g. `en`, `fr`.
 	'language' => 'en',
 
@@ -61,6 +64,9 @@ return array(
 	# /!\ It should NOT be enabled if base_url is not reachable by an external server.
 	'pubsubhubbub_enabled' => false,
 
+	# Allow or not Web robots (e.g. search engines) in HTML headers.
+	'allow_robots' => false,
+
 	'limits' => array(
 
 		# Duration in seconds of the SimplePie cache,

+ 6 - 0
data/shares.php

@@ -44,6 +44,12 @@ return array(
 		'help' => 'https://diasporafoundation.org/',
 		'form' => 'advanced',
 	),
+	'movim' => array(
+		'url' => '~URL~/index.php/share/~LINK~',
+		'transform' => array('rawurlencode', 'urlencode'),
+		'help' => 'https://github.com/edhelas/movim',
+		'form' => 'advanced',
+	),
 	'twitter' => array(
 		'url' => 'https://twitter.com/share?url=~LINK~&amp;text=~TITLE~',
 		'transform' => array('rawurlencode'),

+ 1 - 1
index.html

@@ -4,7 +4,7 @@
 <meta charset="UTF-8" />
 <meta http-equiv="Refresh" content="0; url=p/" />
 <title>Redirection</title>
-<meta name="robots" content="noindex,nofollow" />
+<meta name="robots" content="noindex" />
 </head>
 
 <body>

+ 24 - 3
lib/Minz/Request.php

@@ -91,9 +91,30 @@ class Minz_Request {
 	 */
 	public static function guessBaseUrl() {
 		$url = 'http';
-		$host = empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST'];
-		$port = empty($_SERVER['SERVER_PORT']) ? 80 : $_SERVER['SERVER_PORT'];
-		if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') {
+
+		if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
+			$https = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https';
+		} else {
+			$https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on';
+		}
+
+		if (!empty($_SERVER['HTTP_HOST'])) {
+			$host = $_SERVER['HTTP_HOST'];
+		} elseif (!empty($_SERVER['SERVER_NAME'])) {
+			$host = $_SERVER['SERVER_NAME'];
+		} else {
+			$host = 'localhost';
+		}
+
+		if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
+			$port = intval($_SERVER['HTTP_X_FORWARDED_PORT']);
+		} elseif (!empty($_SERVER['SERVER_PORT'])) {
+			$port = intval($_SERVER['SERVER_PORT']);
+		} else {
+			$port = $https ? 443 : 80;
+		}
+
+		if ($https) {
 			$url .= 's://' . $host . ($port == 443 ? '' : ':' . $port);
 		} else {
 			$url .= '://' . $host . ($port == 80 ? '' : ':' . $port);

+ 19 - 19
p/api/greader.php

@@ -77,7 +77,7 @@ class MyPDO extends Minz_ModelPdo {
 }
 
 function logMe($text) {
-	file_put_contents(join_path(USERS_PATH, '_', 'log_api.txt'), $text, FILE_APPEND);
+	file_put_contents(join_path(USERS_PATH, '_', 'log_api.txt'), date('c') . "\t" . $text . "\n", FILE_APPEND);
 }
 
 function debugInfo() {
@@ -96,7 +96,7 @@ function debugInfo() {
 }
 
 function badRequest() {
-	logMe("badRequest()\n");
+	logMe("badRequest()");
 	logMe(debugInfo());
 	header('HTTP/1.1 400 Bad Request');
 	header('Content-Type: text/plain; charset=UTF-8');
@@ -104,7 +104,7 @@ function badRequest() {
 }
 
 function unauthorized() {
-	logMe("unauthorized()\n");
+	logMe("unauthorized()");
 	logMe(debugInfo());
 	header('HTTP/1.1 401 Unauthorized');
 	header('Content-Type: text/plain; charset=UTF-8');
@@ -113,7 +113,7 @@ function unauthorized() {
 }
 
 function notImplemented() {
-	logMe("notImplemented()\n");
+	logMe("notImplemented()");
 	logMe(debugInfo());
 	header('HTTP/1.1 501 Not Implemented');
 	header('Content-Type: text/plain; charset=UTF-8');
@@ -121,14 +121,14 @@ function notImplemented() {
 }
 
 function serviceUnavailable() {
-	logMe("serviceUnavailable()\n");
+	logMe("serviceUnavailable()");
 	header('HTTP/1.1 503 Service Unavailable');
 	header('Content-Type: text/plain; charset=UTF-8');
 	die('Service Unavailable!');
 }
 
 function checkCompatibility() {
-	logMe("checkCompatibility()\n");
+	logMe("checkCompatibility()");
 	header('Content-Type: text/plain; charset=UTF-8');
 	if (PHP_INT_SIZE < 8 && !function_exists('gmp_init')) {
 		die('FAIL 64-bit or GMP extension!');
@@ -159,7 +159,7 @@ function authorizationToUser() {
 				if ($headerAuthX[1] === sha1($system_conf->salt . $user . $conf->apiPasswordHash)) {
 					return $user;
 				} else {
-					logMe('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1] . "\n");
+					logMe('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1]);
 					Minz_Log::warning('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1]);
 					unauthorized();
 				}
@@ -172,7 +172,7 @@ function authorizationToUser() {
 }
 
 function clientLogin($email, $pass) {	//http://web.archive.org/web/20130604091042/http://undoc.in/clientLogin.html
-	logMe('clientLogin(' . $email . ")\n");
+	//logMe('clientLogin(' . $email . ")");
 	if (ctype_alnum($email)) {
 		if (!function_exists('password_verify')) {
 			include_once(LIB_PATH . '/password_compat.php');
@@ -205,7 +205,7 @@ function token($conf) {
 //http://blog.martindoms.com/2009/08/15/using-the-google-reader-api-part-1/
 //https://github.com/ericmann/gReader-Library/blob/master/greader.class.php
 	$user = Minz_Session::param('currentUser', '_');
-	logMe('token('. $user . ")\n");	//TODO: Implement real token that expires
+	//logMe('token('. $user . ")");	//TODO: Implement real token that expires
 	$system_conf = Minz_Configuration::get('system');
 	$token = str_pad(sha1($system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z');	//Must have 57 characters
 	echo $token, "\n";
@@ -215,7 +215,7 @@ function token($conf) {
 function checkToken($conf, $token) {
 //http://code.google.com/p/google-reader-api/wiki/ActionToken
 	$user = Minz_Session::param('currentUser', '_');
-	logMe('checkToken(' . $token . ")\n");
+	//logMe('checkToken(' . $token . ")");
 	$system_conf = Minz_Configuration::get('system');
 	if ($token === str_pad(sha1($system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z')) {
 		return true;
@@ -224,7 +224,7 @@ function checkToken($conf, $token) {
 }
 
 function tagList() {
-	logMe("tagList()\n");
+	//logMe("tagList()");
 	header('Content-Type: application/json; charset=UTF-8');
 
 	$pdo = new MyPDO();
@@ -249,7 +249,7 @@ function tagList() {
 }
 
 function subscriptionList() {
-	logMe("subscriptionList()\n");
+	//logMe("subscriptionList()");
 	header('Content-Type: application/json; charset=UTF-8');
 
 	$pdo = new MyPDO();
@@ -283,7 +283,7 @@ function subscriptionList() {
 }
 
 function unreadCount() {	//http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#unread-count
-	logMe("unreadCount()\n");
+	//logMe("unreadCount()");
 	header('Content-Type: application/json; charset=UTF-8');
 
 	$totalUnreads = 0;
@@ -330,7 +330,7 @@ function unreadCount() {	//http://blog.martindoms.com/2009/10/16/using-the-googl
 function streamContents($path, $include_target, $start_time, $count, $order, $exclude_target, $continuation) {
 //http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI
 //http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#feed
-	logMe("streamContents($path, $include_target, $start_time, $count, $order, $exclude_target, $continuation)\n");
+	//logMe("streamContents($path, $include_target, $start_time, $count, $order, $exclude_target, $continuation)");
 	header('Content-Type: application/json; charset=UTF-8');
 
 	$feedDAO = FreshRSS_Factory::createFeedDao();
@@ -436,7 +436,7 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude
 //http://code.google.com/p/google-reader-api/wiki/ApiStreamItemsIds
 //http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI
 //http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#feed
-	logMe("streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target)\n");
+	//logMe("streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target)");
 
 	$type = 'A';
 	$id = '';
@@ -484,7 +484,7 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude
 }
 
 function editTag($e_ids, $a, $r) {
-	logMe("editTag()\n");
+	//logMe("editTag()");
 
 	foreach ($e_ids as $i => $e_id) {
 		$e_ids[$i] = hex2dec(basename($e_id));	//Strip prefix 'tag:google.com,2005:reader/item/'
@@ -520,7 +520,7 @@ function editTag($e_ids, $a, $r) {
 }
 
 function markAllAsRead($streamId, $olderThanId) {
-	logMe("markAllAsRead($streamId, $olderThanId)\n");
+	//logMe("markAllAsRead($streamId, $olderThanId)");
 	$entryDAO = FreshRSS_Factory::createEntryDao();
 	if (strpos($streamId, 'feed/') === 0) {
 		$f_id = basename($streamId);
@@ -538,7 +538,7 @@ function markAllAsRead($streamId, $olderThanId) {
 	exit();
 }
 
-logMe('----------------------------------------------------------------'."\n");
+//logMe('----------------------------------------------------------------');
 //logMe(debugInfo());
 
 $pathInfo = empty($_SERVER['PATH_INFO']) ? '/Error' : urldecode($_SERVER['PATH_INFO']);
@@ -560,7 +560,7 @@ if ($user !== '') {
 	$conf = get_user_configuration($user);
 }
 
-logMe('User => ' . $user . "\n");
+//logMe('User => ' . $user);
 
 Minz_Session::_param('currentUser', $user);
 

+ 1 - 1
p/index.html

@@ -8,7 +8,7 @@
 <link rel="shortcut icon" type="image/x-icon" sizes="16x16 64x64" href="favicon.ico" />
 <link rel="icon msapplication-TileImage apple-touch-icon" type="image/png" sizes="256x256" href="themes/icons/favicon-256.png" />
 <meta name="msapplication-TileColor" content="#FFF" />
-<meta name="robots" content="noindex,nofollow" />
+<meta name="robots" content="noindex" />
 <style>
 body {
 	font-family: sans-serif;

+ 6 - 6
p/themes/BlueLagoon/BlueLagoon.css

@@ -349,7 +349,7 @@ a.btn {
 	text-align: left;
 	background: #222;
 }
-.dropdown-menu:after {
+.dropdown-menu::after {
 	content: "";
 	position: absolute;
 	top: -6px;
@@ -384,7 +384,7 @@ a.btn {
 	background: -webkit-linear-gradient(top,  #0090FF 0%, #0062BE 100%);
 	color: #fff;
 }
-.dropdown-menu > .item[aria-checked="true"] > a:before {
+.dropdown-menu > .item[aria-checked="true"] > a::before {
 	font-weight: bold;
 	margin: 0 0 0 -14px;
 }
@@ -626,7 +626,7 @@ a.btn {
 }
 
 /*=== Aside main page (categories) */
-.aside_feed .tree-folder-title > .title:not([data-unread="0"]):after {
+.aside_feed .tree-folder-title > .title:not([data-unread="0"])::after {
 	position: absolute;
 	right: 3px;
 	padding: 1px 5px;
@@ -646,7 +646,7 @@ a.btn {
 .feed.item.error > a {
 	color: #BD362F;
 }
-.aside_feed .tree-folder-items .dropdown-menu:after {
+.aside_feed .tree-folder-items .dropdown-menu::after {
 	left: 2px;
 }
 .aside_feed .tree-folder-items .item .dropdown-target:target ~ .dropdown-toggle > .icon,
@@ -988,7 +988,7 @@ opacity: 1;
 	color: #222;
 	font-weight: bold;
 }
-.box.category .title:not([data-unread="0"]):after {
+.box.category .title:not([data-unread="0"])::after {
 	position: absolute;
 	top: 5px; right: 10px;
 	border: 0;
@@ -1018,7 +1018,7 @@ opacity: 1;
 .aside.aside_feed .nav-form .dropdown .dropdown-menu {
 	right: -20px;
 }
-.aside.aside_feed .nav-form .dropdown .dropdown-menu:after {
+.aside.aside_feed .nav-form .dropdown .dropdown-menu::after {
 	right: 33px;
 }
 

+ 5 - 5
p/themes/BlueLagoon/template.css

@@ -92,7 +92,7 @@ input.extend:focus {
 /*=== COMPONENTS */
 /*===============*/
 /*=== Forms */
-.form-group:after {
+.form-group::after {
 	content: "";
 	display: block;
 	clear: both;
@@ -184,7 +184,7 @@ a.btn {
 .dropdown-menu > .item > span {
 	display: block;
 }
-.dropdown-menu > .item[aria-checked="true"] > a:before {
+.dropdown-menu > .item[aria-checked="true"] > a::before {
 	content: '✓';
 }
 .dropdown-menu .input {
@@ -315,7 +315,7 @@ a.btn {
 	white-space: nowrap;
 	text-overflow: ellipsis;
 }
-.category .btn:not([data-unread="0"]):after {
+.category .btn:not([data-unread="0"])::after {
 	content: attr(data-unread);
 }
 
@@ -334,7 +334,7 @@ a.btn {
 	text-overflow: ellipsis;
 	vertical-align: middle;
 }
-.categories .feeds .feed:not([data-unread="0"]):before {
+.categories .feeds .feed:not([data-unread="0"])::before {
 	content: "(" attr(data-unread) ") ";
 }
 .categories .feeds .dropdown-menu {
@@ -688,7 +688,7 @@ a.btn {
 	.flux_content .content a {
 		color: #000;
 	}
-	.flux_content .content a:after {
+	.flux_content .content a::after {
 		content: " [" attr(href) "] ";
 		font-style: italic;
 	}

+ 6 - 6
p/themes/Dark/dark.css

@@ -312,7 +312,7 @@ a.btn {
 	border: 1px solid #888;
 	border-radius: 5px;
 }
-.dropdown-menu:after {
+.dropdown-menu::after {
 	content: "";
 	position: absolute;
 	top: -6px;
@@ -347,7 +347,7 @@ a.btn {
 	background: #26303F;
 	color: #888;
 }
-.dropdown-menu > .item[aria-checked="true"] > a:before {
+.dropdown-menu > .item[aria-checked="true"] > a::before {
 	font-weight: bold;
 	margin: 0 0 0 -14px;
 }
@@ -551,7 +551,7 @@ a.btn {
 }
 
 /*=== Aside main page (categories) */
-.aside_feed .tree-folder-title > .title:not([data-unread="0"]):after {
+.aside_feed .tree-folder-title > .title:not([data-unread="0"])::after {
 	position: absolute;
 	right: 0;
 	margin: 10px 0;
@@ -584,7 +584,7 @@ a.btn {
 .feed.item.error.active > a {
 	color: #fff;
 }
-.aside_feed .tree-folder-items .dropdown-menu:after {
+.aside_feed .tree-folder-items .dropdown-menu::after {
 	left: 2px;
 }
 .aside_feed .tree-folder-items .item .dropdown-target:target ~ .dropdown-toggle > .icon,
@@ -858,7 +858,7 @@ a.btn {
 	color: #fff;
 	font-weight: bold;
 }
-.box.category .title:not([data-unread="0"]):after {
+.box.category .title:not([data-unread="0"])::after {
 	position: absolute;
 	top: 5px; right: 10px;
 	border: 0;
@@ -894,7 +894,7 @@ a.btn {
 .aside.aside_feed .nav-form .dropdown .dropdown-menu {
 	right: -20px;
 }
-.aside.aside_feed .nav-form .dropdown .dropdown-menu:after {
+.aside.aside_feed .nav-form .dropdown .dropdown-menu::after {
 	right: 33px;
 }
 

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

@@ -102,7 +102,7 @@ form th {
 	border: 1px solid transparent;
 	border-radius: 3px;
 }
-.form-group:after {
+.form-group::after {
 	content: "";
 	display: block;
 	clear: both;
@@ -312,7 +312,7 @@ a.btn {
 	border: 1px solid #95a5a6;
 	border-radius: 3px;
 }
-.dropdown-menu:after {
+.dropdown-menu::after {
 	content: "";
 	position: absolute;
 	top: -6px;
@@ -347,7 +347,7 @@ a.btn {
 	background: #2980b9;
 	color: #fff;
 }
-.dropdown-menu > .item[aria-checked="true"] > a:before {
+.dropdown-menu > .item[aria-checked="true"] > a::before {
 	font-weight: bold;
 	margin: 0 0 0 -14px;
 }
@@ -557,7 +557,7 @@ a.btn {
 }
 
 /*=== Aside main page (categories) */
-.aside_feed .tree-folder-title > .title:not([data-unread="0"]):after {
+.aside_feed .tree-folder-title > .title:not([data-unread="0"])::after {
 	position: absolute;
 	right: 0;
 	margin: 10px 0;
@@ -588,7 +588,7 @@ a.btn {
 .feed.item.error.active > a {
 	color: #fff;
 }
-.aside_feed .tree-folder-items .dropdown-menu:after {
+.aside_feed .tree-folder-items .dropdown-menu::after {
 	left: 2px;
 }
 .aside_feed .tree-folder-items .item .dropdown-target:target ~ .dropdown-toggle > .icon,
@@ -864,7 +864,7 @@ a.btn {
 	font-weight: bold;
 	color: #fff;
 }
-.box.category .title:not([data-unread="0"]):after {
+.box.category .title:not([data-unread="0"])::after {
 	position: absolute;
 	top: 5px; right: 10px;
 	border: 0;
@@ -887,7 +887,7 @@ a.btn {
 .aside.aside_feed .nav-form .dropdown .dropdown-menu {
 	right: -20px;
 }
-.aside.aside_feed .nav-form .dropdown .dropdown-menu:after {
+.aside.aside_feed .nav-form .dropdown .dropdown-menu::after {
 	right: 33px;
 }
 

+ 6 - 6
p/themes/Origine/origine.css

@@ -336,7 +336,7 @@ a.btn {
 	font-size: 0.8rem;
 	text-align: left;
 }
-.dropdown-menu:after {
+.dropdown-menu::after {
 	content: "";
 	position: absolute;
 	top: -6px;
@@ -373,7 +373,7 @@ a.btn {
 	background: #0062BE;
 	color: #fff;
 }
-.dropdown-menu > .item[aria-checked="true"] > a:before {
+.dropdown-menu > .item[aria-checked="true"] > a::before {
 	font-weight: bold;
 	margin: 0 0 0 -14px;
 }
@@ -591,7 +591,7 @@ a.btn {
 }
 
 /*=== Aside main page (categories) */
-.aside_feed .category .title:not([data-unread="0"]):after {
+.aside_feed .category .title:not([data-unread="0"])::after {
 	position: absolute;
 	right: 0;
 	margin: 10px 0;
@@ -622,7 +622,7 @@ a.btn {
 .feed.item.error.active > a {
 	color: #fff;
 }
-.aside_feed .tree-folder-items .dropdown-menu:after {
+.aside_feed .tree-folder-items .dropdown-menu::after {
 	left: 2px;
 }
 .aside_feed .tree-folder-items .item .dropdown-target:target ~ .dropdown-toggle > .icon,
@@ -908,7 +908,7 @@ a.btn {
 	color: #fff;
 	font-weight: bold;
 }
-.box.category .title:not([data-unread="0"]):after {
+.box.category .title:not([data-unread="0"])::after {
 	position: absolute;
 	top: 5px; right: 10px;
 	border: 0;
@@ -932,7 +932,7 @@ a.btn {
 .aside.aside_feed .nav-form .dropdown .dropdown-menu {
 	right: -20px;
 }
-.aside.aside_feed .nav-form .dropdown .dropdown-menu:after {
+.aside.aside_feed .nav-form .dropdown .dropdown-menu::after {
 	right: 33px;
 }
 

+ 6 - 6
p/themes/Pafat/pafat.css

@@ -319,7 +319,7 @@ a.btn {
 	font-size: 0.8rem;
 	text-align: left;
 }
-.dropdown-menu:after {
+.dropdown-menu::after {
 	content: "";
 	position: absolute;
 	top: -6px;
@@ -362,7 +362,7 @@ a.btn {
 	background: #eee;
 	color: #666;
 }
-.dropdown-menu > .item[aria-checked="true"] > a:before {
+.dropdown-menu > .item[aria-checked="true"] > a::before {
 	font-weight: bold;
 	margin: 0 0 0 -14px;
 }
@@ -590,7 +590,7 @@ a.btn {
 }
 
 /*=== Aside main page (categories) */
-.aside_feed .tree-folder-title > .title:not([data-unread="0"]):after {
+.aside_feed .tree-folder-title > .title:not([data-unread="0"])::after {
 	position: absolute;
 	top: 0.25rem; right: 3px;
 	padding: 0px 5px;
@@ -633,7 +633,7 @@ a.btn {
 .feed.item.error.active > a {
 	color: #fff;
 }
-.aside_feed .tree-folder-items .dropdown-menu:after {
+.aside_feed .tree-folder-items .dropdown-menu::after {
 	left: 2px;
 }
 .aside_feed .tree-folder-items .item .dropdown-target:target ~ .dropdown-toggle > .icon,
@@ -918,7 +918,7 @@ a.btn {
 	font-weight: bold;
 	color: #fff;
 }
-.box.category .title:not([data-unread="0"]):after {
+.box.category .title:not([data-unread="0"])::after {
 	position: absolute;
 	top: 5px; right: 10px;
 	border: 0;
@@ -943,7 +943,7 @@ a.btn {
 .aside.aside_feed .nav-form .dropdown .dropdown-menu {
 	right: -20px;
 }
-.aside.aside_feed .nav-form .dropdown .dropdown-menu:after {
+.aside.aside_feed .nav-form .dropdown .dropdown-menu::after {
 	right: 33px;
 }
 

+ 6 - 6
p/themes/Screwdriver/screwdriver.css

@@ -349,7 +349,7 @@ a.btn {
 	text-align: left;
 	background: #222;
 }
-.dropdown-menu:after {
+.dropdown-menu::after {
 	content: "";
 	position: absolute;
 	top: -6px;
@@ -385,7 +385,7 @@ a.btn {
 	background: #171717;
 	color: #fff;
 }
-.dropdown-menu > .item[aria-checked="true"] > a:before {
+.dropdown-menu > .item[aria-checked="true"] > a::before {
 	font-weight: bold;
 	margin: 0 0 0 -14px;
 }
@@ -621,7 +621,7 @@ a.btn {
 }
 
 /*=== Aside main page (categories) */
-.aside_feed .tree-folder-title > .title:not([data-unread="0"]):after {
+.aside_feed .tree-folder-title > .title:not([data-unread="0"])::after {
 	position: absolute;
 	right: 3px;
 	padding: 1px 5px;
@@ -641,7 +641,7 @@ a.btn {
 .feed.item.error > a {
 	color: #BD362F;
 }
-.aside_feed .tree-folder-items .dropdown-menu:after {
+.aside_feed .tree-folder-items .dropdown-menu::after {
 	left: 2px;
 }
 .aside_feed .tree-folder-items .item .dropdown-target:target ~ .dropdown-toggle > .icon,
@@ -986,7 +986,7 @@ opacity: 1;
 	color: #222;
 	font-weight: bold;
 }
-.box.category .title:not([data-unread="0"]):after {
+.box.category .title:not([data-unread="0"])::after {
 	position: absolute;
 	top: 5px; right: 10px;
 	border: 0;
@@ -1016,7 +1016,7 @@ opacity: 1;
 .aside.aside_feed .nav-form .dropdown .dropdown-menu {
 	right: -20px;
 }
-.aside.aside_feed .nav-form .dropdown .dropdown-menu:after {
+.aside.aside_feed .nav-form .dropdown .dropdown-menu::after {
 	right: 33px;
 }
 

+ 6 - 6
p/themes/base-theme/base.css

@@ -231,7 +231,7 @@ a.btn {
 	font-size: 0.8rem;
 	text-align: left;
 }
-.dropdown-menu:after {
+.dropdown-menu::after {
 	content: "";
 	position: absolute;
 	top: -6px;
@@ -262,7 +262,7 @@ a.btn {
 }
 .dropdown-menu > .item:hover {
 }
-.dropdown-menu > .item[aria-checked="true"] > a:before {
+.dropdown-menu > .item[aria-checked="true"] > a::before {
 	font-weight: bold;
 	margin: 0 0 0 -14px;
 }
@@ -431,7 +431,7 @@ a.btn {
 }
 
 /*=== Aside main page (categories) */
-.aside_feed .tree-folder-title > .title:not([data-unread="0"]):after {
+.aside_feed .tree-folder-title > .title:not([data-unread="0"])::after {
 	position: absolute;
 	right: 0;
 	margin: 10px 0;
@@ -456,7 +456,7 @@ a.btn {
 .feed.item.empty.active > a,
 .feed.item.error.active > a {
 }
-.aside_feed .tree-folder-items .dropdown-menu:after {
+.aside_feed .tree-folder-items .dropdown-menu::after {
 	left: 2px;
 }
 .aside_feed .tree-folder-items .item .dropdown-target:target ~ .dropdown-toggle > .icon,
@@ -675,7 +675,7 @@ a.btn {
 .box.category:not([data-unread="0"]) .box-title .title {
 	font-weight: bold;
 }
-.box.category .title:not([data-unread="0"]):after {
+.box.category .title:not([data-unread="0"])::after {
 	position: absolute;
 	top: 5px; right: 10px;
 	border: 0;
@@ -698,7 +698,7 @@ a.btn {
 .aside.aside_feed .nav-form .dropdown .dropdown-menu {
 	right: -20px;
 }
-.aside.aside_feed .nav-form .dropdown .dropdown-menu:after {
+.aside.aside_feed .nav-form .dropdown .dropdown-menu::after {
 	right: 33px;
 }
 

+ 9 - 5
p/themes/base-theme/template.css

@@ -111,7 +111,7 @@ td.numeric {
 /*=== COMPONENTS */
 /*===============*/
 /*=== Forms */
-.form-group:after {
+.form-group::after {
 	content: "";
 	display: block;
 	clear: both;
@@ -206,7 +206,7 @@ a.btn {
 	display: block;
 	min-width: 200px;
 }
-.dropdown-menu > .item[aria-checked="true"] > a:before {
+.dropdown-menu > .item[aria-checked="true"] > a::before {
 	content: '✓';
 }
 .dropdown-menu .input {
@@ -773,10 +773,14 @@ input:checked + .slide-container .properties {
 
 /*=== DIVERS */
 /*===========*/
-.category .title:not([data-unread="0"]):after {
+.category .title:not([data-unread="0"])::after {
 	content: attr(data-unread);
 }
-.feed .item-title:not([data-unread="0"]):before {
+.category .title.error::before {
+	content: "⚠ ";
+	color: #bd362f;
+}
+.feed .item-title:not([data-unread="0"])::before {
 	content: "(" attr(data-unread) ") ";
 }
 .feed .item-title:not([data-unread="0"]) {
@@ -889,7 +893,7 @@ input:checked + .slide-container .properties {
 	.flux_content .content a {
 		color: #000;
 	}
-	.flux_content .content a:after {
+	.flux_content .content a::after {
 		content: " [" attr(href) "] ";
 		font-style: italic;
 	}