Pārlūkot izejas kodu

Merge branch 'dev' into beta

Marien Fressinaud 10 gadi atpakaļ
vecāks
revīzija
a1267baa0b
48 mainītis faili ar 1109 papildinājumiem un 107 dzēšanām
  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;
 	}