Explorar o código

Merge branch 'dev' into dev-1.14.0

Alexandre Alapetite %!s(int64=7) %!d(string=hai) anos
pai
achega
ef6df8aeca

+ 12 - 2
CHANGELOG.md

@@ -1,27 +1,37 @@
 # FreshRSS changelog
 # FreshRSS changelog
 
 
-## 2019-01-XX FreshRSS 1.13.1-dev
+## 2019-0X-XX FreshRSS 1.13.2-dev
+
+
+## 2019-01-26 FreshRSS 1.13.1
 
 
 * Features
 * Features
 	* Include articles with custom labels during export [#2196](https://github.com/FreshRSS/FreshRSS/issues/2196)
 	* Include articles with custom labels during export [#2196](https://github.com/FreshRSS/FreshRSS/issues/2196)
+	* Export/import articles read/unread state [#2226](https://github.com/FreshRSS/FreshRSS/pull/2226)
+	* Import FeedBin, and more robust general import [#2228](https://github.com/FreshRSS/FreshRSS/pull/2228)
 * Bug fixing
 * Bug fixing
 	* Fix missing HTTP `X-Forwarded-Prefix` in cookie path behind a reverse-proxy [#2201](https://github.com/FreshRSS/FreshRSS/pull/2201)
 	* Fix missing HTTP `X-Forwarded-Prefix` in cookie path behind a reverse-proxy [#2201](https://github.com/FreshRSS/FreshRSS/pull/2201)
 * Deployment
 * Deployment
 	* Docker improvements [#2202](https://github.com/FreshRSS/FreshRSS/pull/2202)
 	* Docker improvements [#2202](https://github.com/FreshRSS/FreshRSS/pull/2202)
-		* Add `TZ` timezone parameter [#2153](https://github.com/FreshRSS/FreshRSS/issues/2153)
 		* Performance: Hard-include Apache .htaccess to avoid having to scan for changes in those files
 		* Performance: Hard-include Apache .htaccess to avoid having to scan for changes in those files
 		* Performance: Disable unused Apache security check of symlinks
 		* Performance: Disable unused Apache security check of symlinks
 		* Performance: Disable unused Apache modules
 		* Performance: Disable unused Apache modules
 		* Add option to mount custom `.htaccess` for HTTP authentication
 		* Add option to mount custom `.htaccess` for HTTP authentication
 		* Docker logs gets PHP syslog messages (e.g. from cron job and when fetching external content)
 		* Docker logs gets PHP syslog messages (e.g. from cron job and when fetching external content)
 	* New environment variable `COPY_SYSLOG_TO_STDERR` or in `constants.local.php` to copy PHP syslog messages to STDERR [#2213](https://github.com/FreshRSS/FreshRSS/pull/2213)
 	* New environment variable `COPY_SYSLOG_TO_STDERR` or in `constants.local.php` to copy PHP syslog messages to STDERR [#2213](https://github.com/FreshRSS/FreshRSS/pull/2213)
+	* New `TZ` timezone environment variable [#2153](https://github.com/FreshRSS/FreshRSS/issues/2153)
 	* Run Docker cron job with Apache user instead of root [#2208](https://github.com/FreshRSS/FreshRSS/pull/2208)
 	* Run Docker cron job with Apache user instead of root [#2208](https://github.com/FreshRSS/FreshRSS/pull/2208)
 	* Accept HTTP header `X-WebAuth-User` for delegated HTTP Authentication [#2204](https://github.com/FreshRSS/FreshRSS/pull/2204)
 	* Accept HTTP header `X-WebAuth-User` for delegated HTTP Authentication [#2204](https://github.com/FreshRSS/FreshRSS/pull/2204)
+* Extensions
+	* Trigger a `freshrss:openArticle` JavaScript event [#2222](https://github.com/FreshRSS/FreshRSS/pull/2222)
 * API
 * API
 	* Automatic test of API configuration [#2207](https://github.com/FreshRSS/FreshRSS/pull/2207)
 	* Automatic test of API configuration [#2207](https://github.com/FreshRSS/FreshRSS/pull/2207)
 	* Performance + compatibility: Use Apache `SetEnvIf` module if available and fall-back to `RewriteRule` [#2202](https://github.com/FreshRSS/FreshRSS/pull/2202)
 	* Performance + compatibility: Use Apache `SetEnvIf` module if available and fall-back to `RewriteRule` [#2202](https://github.com/FreshRSS/FreshRSS/pull/2202)
 * Security
 * Security
 	* Fixes when HTTP user does not exist in FreshRSS [#2204](https://github.com/FreshRSS/FreshRSS/pull/2204)
 	* Fixes when HTTP user does not exist in FreshRSS [#2204](https://github.com/FreshRSS/FreshRSS/pull/2204)
+* I18n
+	* Improve Dutch [#2221](https://github.com/FreshRSS/FreshRSS/pull/2221)
+	* Improve Occitan [#2230](https://github.com/FreshRSS/FreshRSS/pull/2230)
 * Accessibility
 * Accessibility
 	* Remove alt in logo [#2209](https://github.com/FreshRSS/FreshRSS/pull/2209)
 	* Remove alt in logo [#2209](https://github.com/FreshRSS/FreshRSS/pull/2209)
 
 

+ 65 - 13
app/Controllers/importExportController.php

@@ -41,7 +41,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 		$list_files = array(
 		$list_files = array(
 			'opml' => array(),
 			'opml' => array(),
 			'json_starred' => array(),
 			'json_starred' => array(),
-			'json_feed' => array()
+			'json_feed' => array(),
+			'ttrss_starred' => array(),
 		);
 		);
 
 
 		// We try to list all files according to their type
 		// We try to list all files according to their type
@@ -434,10 +435,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 			}
 			}
 			return false;
 			return false;
 		}
 		}
+		$items = isset($article_object['items']) ? $article_object['items'] : $article_object;
 
 
-		$is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0;
-
-		$google_compliant = strpos($article_object['id'], 'com.google') !== false;
+		$mark_as_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0;
 
 
 		$error = false;
 		$error = false;
 		$article_to_feed = array();
 		$article_to_feed = array();
@@ -447,9 +447,23 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 		$limits = FreshRSS_Context::$system_conf->limits;
 		$limits = FreshRSS_Context::$system_conf->limits;
 
 
 		// First, we check feeds of articles are in DB (and add them if needed).
 		// First, we check feeds of articles are in DB (and add them if needed).
-		foreach ($article_object['items'] as $item) {
-			$key = $google_compliant ? 'htmlUrl' : 'feedUrl';
-			$feed = new FreshRSS_Feed($item['origin'][$key]);
+		foreach ($items as $item) {
+			if (!isset($item['origin'])) {
+				$item['origin'] = array('title' => 'Import');
+			}
+			if (!empty($item['origin']['feedUrl'])) {
+				$feedUrl = $item['origin']['feedUrl'];
+			} elseif (!empty($item['origin']['streamId']) && strpos($item['origin']['streamId'], 'feed/') === 0) {
+				$feedUrl = substr($item['origin']['streamId'], 5);	//Google Reader
+				$item['origin']['feedUrl'] = $feedUrl;
+			} elseif (!empty($item['origin']['htmlUrl'])) {
+				$feedUrl = $item['origin']['htmlUrl'];
+			} else {
+				$feedUrl = 'http://import.localhost/import.xml';
+				$item['origin']['feedUrl'] = $feedUrl;
+				$item['origin']['disable'] = true;
+			}
+			$feed = new FreshRSS_Feed($feedUrl);
 			$feed = $this->feedDAO->searchByUrl($feed->url());
 			$feed = $this->feedDAO->searchByUrl($feed->url());
 
 
 			if ($feed == null) {
 			if ($feed == null) {
@@ -497,7 +511,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 		// Then, articles are imported.
 		// Then, articles are imported.
 		$newGuids = array();
 		$newGuids = array();
 		$this->entryDAO->beginTransaction();
 		$this->entryDAO->beginTransaction();
-		foreach ($article_object['items'] as $item) {
+		foreach ($items as $item) {
 			if (empty($article_to_feed[$item['id']])) {
 			if (empty($article_to_feed[$item['id']])) {
 				// Related feed does not exist for this entry, do nothing.
 				// Related feed does not exist for this entry, do nothing.
 				continue;
 				continue;
@@ -505,14 +519,19 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 
 
 			$feed_id = $article_to_feed[$item['id']];
 			$feed_id = $article_to_feed[$item['id']];
 			$author = isset($item['author']) ? $item['author'] : '';
 			$author = isset($item['author']) ? $item['author'] : '';
-			$is_starred = $starred;
-			$tags = $item['categories'];
+			$is_starred = false;
+			$is_read = null;
+			$tags = empty($item['categories']) ? array() : $item['categories'];
 			$labels = array();
 			$labels = array();
 			for ($i = count($tags) - 1; $i >= 0; $i --) {
 			for ($i = count($tags) - 1; $i >= 0; $i --) {
 				$tag = trim($tags[$i]);
 				$tag = trim($tags[$i]);
 				if (strpos($tag, 'user/-/') !== false) {
 				if (strpos($tag, 'user/-/') !== false) {
 					if ($tag === 'user/-/state/com.google/starred') {
 					if ($tag === 'user/-/state/com.google/starred') {
 						$is_starred = true;
 						$is_starred = true;
+					} elseif ($tag === 'user/-/state/com.google/read') {
+						$is_read = true;
+					} elseif ($tag === 'user/-/state/com.google/unread') {
+						$is_read = false;
 					} elseif (strpos($tag, 'user/-/label/') === 0) {
 					} elseif (strpos($tag, 'user/-/label/') === 0) {
 						$tag = trim(substr($tag, 13));
 						$tag = trim(substr($tag, 13));
 						if ($tag != '') {
 						if ($tag != '') {
@@ -522,19 +541,49 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 					unset($tags[$i]);
 					unset($tags[$i]);
 				}
 				}
 			}
 			}
+			if ($starred && !$is_starred) {
+				//If the article has no label, mark it as starred (old format)
+				$is_starred = empty($labels);
+			}
+			if ($is_read === null) {
+				$is_read = $mark_as_read;
+			}
+
+			if (isset($item['alternate'][0]['href'])) {
+				$url = $item['alternate'][0]['href'];
+			} elseif (isset($item['url'])) {
+				$url = $item['url'];	//FeedBin
+			} else {
+				$url = '';
+			}
 
 
-			$url = $item['alternate'][0]['href'];
 			if (!empty($item['content']['content'])) {
 			if (!empty($item['content']['content'])) {
 				$content = $item['content']['content'];
 				$content = $item['content']['content'];
 			} elseif (!empty($item['summary']['content'])) {
 			} elseif (!empty($item['summary']['content'])) {
 				$content = $item['summary']['content'];
 				$content = $item['summary']['content'];
+			} elseif (!empty($item['content'])) {
+				$content = $item['content'];	//FeedBin
+			} else {
+				$content = '';
 			}
 			}
 			$content = sanitizeHTML($content, $url);
 			$content = sanitizeHTML($content, $url);
 
 
+			if (!empty($item['published'])) {
+				$published = $item['published'];
+			} elseif (!empty($item['timestampUsec'])) {
+				$published = substr($item['timestampUsec'], 0, -6);
+			} elseif (!empty($item['updated'])) {
+				$published = $item['updated'];
+			} else {
+				$published = 0;
+			}
+			if (!ctype_digit('' . $published)) {
+				$published = strtotime($published);
+			}
+
 			$entry = new FreshRSS_Entry(
 			$entry = new FreshRSS_Entry(
 				$feed_id, $item['id'], $item['title'], $author,
 				$feed_id, $item['id'], $item['title'], $author,
-				$content, $url,
-				$item['published'], $is_read, $is_starred
+				$content, $url, $published, $is_read, $is_starred
 			);
 			);
 			$entry->_id(min(time(), $entry->date(true)) . uSecString());
 			$entry->_id(min(time(), $entry->date(true)) . uSecString());
 			$entry->_tags($tags);
 			$entry->_tags($tags);
@@ -626,6 +675,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 			$feed->_category(FreshRSS_CategoryDAO::DEFAULTCATEGORYID);
 			$feed->_category(FreshRSS_CategoryDAO::DEFAULTCATEGORYID);
 			$feed->_name($name);
 			$feed->_name($name);
 			$feed->_website($website);
 			$feed->_website($website);
+			if (!empty($origin['disable'])) {
+				$feed->_ttl(-1 * FreshRSS_Context::$user_conf->ttl_default);
+			}
 
 
 			// Call the extension hook
 			// Call the extension hook
 			$feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed);
 			$feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed);

+ 4 - 1
app/Models/FeedDAO.php

@@ -61,7 +61,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 			$valuesTmp['lastUpdate'],
 			$valuesTmp['lastUpdate'],
 			base64_encode($valuesTmp['httpAuth']),
 			base64_encode($valuesTmp['httpAuth']),
 			FreshRSS_Feed::KEEP_HISTORY_DEFAULT,
 			FreshRSS_Feed::KEEP_HISTORY_DEFAULT,
-			FreshRSS_Feed::TTL_DEFAULT,
+			isset($valuesTmp['ttl']) ? intval($valuesTmp['ttl']) : FreshRSS_Feed::TTL_DEFAULT,
 			isset($valuesTmp['attributes']) ? json_encode($valuesTmp['attributes']) : '',
 			isset($valuesTmp['attributes']) ? json_encode($valuesTmp['attributes']) : '',
 		);
 		);
 
 
@@ -95,6 +95,9 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 				'httpAuth' => $feed->httpAuth(),
 				'httpAuth' => $feed->httpAuth(),
 				'attributes' => $feed->attributes(),
 				'attributes' => $feed->attributes(),
 			);
 			);
+			if ($feed->mute() || $feed->ttl() != FreshRSS_Context::$user_conf->ttl_default) {
+				$values['ttl'] = $feed->ttl() * ($feed->mute() ? -1 : 1);
+			}
 
 
 			$id = $this->addFeed($values);
 			$id = $this->addFeed($values);
 			if ($id) {
 			if ($id) {

+ 2 - 2
app/i18n/nl/admin.php

@@ -163,8 +163,8 @@ return array(
 		'max-categories' => 'Categoriën limiet per gebruiker',
 		'max-categories' => 'Categoriën limiet per gebruiker',
 		'max-feeds' => 'Feed limiet per gebruiker',
 		'max-feeds' => 'Feed limiet per gebruiker',
 		'cookie-duration' => array(
 		'cookie-duration' => array(
-			'help' => 'in seconds', // @todo translate
-			'number' => 'Duration to keep logged in', // @todo translate
+			'help' => 'in seconden',
+			'number' => 'Tijdsduur om ingelogd te blijven',
 		),
 		),
 		'registration' => array(
 		'registration' => array(
 			'help' => '0 betekent geen account limiet',
 			'help' => '0 betekent geen account limiet',

+ 3 - 3
app/i18n/nl/conf.php

@@ -162,7 +162,7 @@ return array(
 		'mark_read' => 'Markeer als gelezen',
 		'mark_read' => 'Markeer als gelezen',
 		'navigation' => 'Navigatie',
 		'navigation' => 'Navigatie',
 		'navigation_help' => 'Met de "Shift" toets, kunt u navigatie verwijzingen voor feeds gebruiken.<br/>Met de "Alt" toets, kunt u navigatie verwijzingen voor categoriën gebruiken.',
 		'navigation_help' => 'Met de "Shift" toets, kunt u navigatie verwijzingen voor feeds gebruiken.<br/>Met de "Alt" toets, kunt u navigatie verwijzingen voor categoriën gebruiken.',
-		'navigation_no_mod_help' => 'The following navigation shortcuts do not support modifiers.',	//TODO - Translation
+		'navigation_no_mod_help' => 'De volgende navigatiesnelkoppelingen ondersteunen geen besturingstoetsen.',
 		'next_article' => 'Spring naar volgende artikel',
 		'next_article' => 'Spring naar volgende artikel',
 		'normal_view' => 'Schakel naar gewoon aanzicht',
 		'normal_view' => 'Schakel naar gewoon aanzicht',
 		'other_action' => 'Andere acties',
 		'other_action' => 'Andere acties',
@@ -171,8 +171,8 @@ return array(
 		'rss_view' => 'Open RSS-aanzicht in een nieuwe tab',
 		'rss_view' => 'Open RSS-aanzicht in een nieuwe tab',
 		'see_on_website' => 'Bekijk op originale website',
 		'see_on_website' => 'Bekijk op originale website',
 		'shift_for_all_read' => '+ <code>shift</code> om alle artikelen als gelezen te markeren',
 		'shift_for_all_read' => '+ <code>shift</code> om alle artikelen als gelezen te markeren',
-		'skip_next_article' => 'Focus next without opening',	//TODO - Translation
-		'skip_previous_article' => 'Focus previous without opening',	//TODO - Translation
+		'skip_next_article' => 'Volgend artikel focusen zonder openen',
+		'skip_previous_article' => 'Vorig artikel focusen zonder openen',
 		'title' => 'Verwijzingen',
 		'title' => 'Verwijzingen',
 		'user_filter' => 'Toegang gebruikers filters',
 		'user_filter' => 'Toegang gebruikers filters',
 		'user_filter_help' => 'Als er slechts één gebruikersfilter is, dan wordt die gebruikt. Anders zijn ze toegankelijk met hun nummer.',
 		'user_filter_help' => 'Als er slechts één gebruikersfilter is, dan wordt die gebruikt. Anders zijn ze toegankelijk met hun nummer.',

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

@@ -53,11 +53,11 @@ return array(
 		'starred' => 'Laat alleen favorieten zien',
 		'starred' => 'Laat alleen favorieten zien',
 		'stats' => 'Statistieken',
 		'stats' => 'Statistieken',
 		'subscription' => 'Abonnementen beheer',
 		'subscription' => 'Abonnementen beheer',
-		'tags' => 'My labels',	//TODO - Translation
+		'tags' => 'Mijn labels',
 		'unread' => 'Laat alleen ongelezen zien',
 		'unread' => 'Laat alleen ongelezen zien',
 	),
 	),
 	'share' => 'Delen',
 	'share' => 'Delen',
 	'tag' => array(
 	'tag' => array(
-		'related' => 'Verwante labels',	//TODO - Translation
+		'related' => 'Verwante labels',
 	),
 	),
 );
 );

+ 5 - 5
app/i18n/nl/sub.php

@@ -27,7 +27,7 @@ return array(
 			'password' => 'HTTP wachtwoord',
 			'password' => 'HTTP wachtwoord',
 			'username' => 'HTTP gebruikers naam',
 			'username' => 'HTTP gebruikers naam',
 		),
 		),
-		'clear_cache' => 'Always clear cache',	//TODO - Translation
+		'clear_cache' => 'Cache altijd leegmaken',
 		'css_help' => 'Haalt verstoorde RSS feeds op (attentie, heeft meer tijd nodig!)',
 		'css_help' => 'Haalt verstoorde RSS feeds op (attentie, heeft meer tijd nodig!)',
 		'css_path' => 'Artikelen CSS pad op originele website',
 		'css_path' => 'Artikelen CSS pad op originele website',
 		'description' => 'Omschrijving',
 		'description' => 'Omschrijving',
@@ -47,11 +47,11 @@ return array(
 		),
 		),
 		'websub' => 'Directe notificaties met WebSub',
 		'websub' => 'Directe notificaties met WebSub',
 		'show' => array(
 		'show' => array(
-			'all' => 'Show all feeds',	//TODO - Translation
-			'error' => 'Show only feeds with error',	//TODO - Translation
+			'all' => 'Alle feeds tonen',
+			'error' => 'Alleen feeds met een foutmelding tonen',
 		),
 		),
 		'showing' => array(
 		'showing' => array(
-			'error' => 'Showing only feeds with error',	//TODO - Translation
+			'error' => 'Alleen feeds met een foutmelding worden getoond',
 		),
 		),
 		'ssl_verify' => 'SSL-veiligheid controleren',
 		'ssl_verify' => 'SSL-veiligheid controleren',
 		'stats' => 'Statistieken',
 		'stats' => 'Statistieken',
@@ -72,7 +72,7 @@ return array(
 		'export' => 'Exporteer',
 		'export' => 'Exporteer',
 		'export_opml' => 'Exporteer lijst van feeds (OPML)',
 		'export_opml' => 'Exporteer lijst van feeds (OPML)',
 		'export_starred' => 'Exporteer je favorieten',
 		'export_starred' => 'Exporteer je favorieten',
-		'export_labelled' => 'Export your labelled articles',	//TODO
+		'export_labelled' => 'Exporteer gelabelde artikels',
 		'feed_list' => 'Lijst van %s artikelen',
 		'feed_list' => 'Lijst van %s artikelen',
 		'file_to_import' => 'Bestand om te importeren<br />(OPML, JSON of ZIP)',
 		'file_to_import' => 'Bestand om te importeren<br />(OPML, JSON of ZIP)',
 		'file_to_import_no_zip' => 'Bestand om te importeren<br />(OPML of JSON)',
 		'file_to_import_no_zip' => 'Bestand om te importeren<br />(OPML of JSON)',

+ 2 - 2
app/i18n/oc/conf.php

@@ -94,14 +94,14 @@ return array(
 		'display_articles_unfolded' => 'Mostrar los articles desplegats per defaut',
 		'display_articles_unfolded' => 'Mostrar los articles desplegats per defaut',
 		'display_categories_unfolded' => 'Mostrar las categorias plegadas per defaut',
 		'display_categories_unfolded' => 'Mostrar las categorias plegadas per defaut',
 		'hide_read_feeds' => 'Rescondre las categorias & fluxes sens articles pas legits (fonciona pas amb la configuracion « Mostrar totes los articles »)',
 		'hide_read_feeds' => 'Rescondre las categorias & fluxes sens articles pas legits (fonciona pas amb la configuracion « Mostrar totes los articles »)',
-		'img_with_lazyload' => 'Utilizar lo mòde “cargament tardiu” per las imatges',
+		'img_with_lazyload' => 'Utilizar lo mòde “cargament tardiu” pels imatges',
 		'jump_next' => 'sautar al vesin venent pas legit (flux o categoria)',
 		'jump_next' => 'sautar al vesin venent pas legit (flux o categoria)',
 		'mark_updated_article_unread' => 'Marcar los articles actualizats coma pas legits',
 		'mark_updated_article_unread' => 'Marcar los articles actualizats coma pas legits',
 		'number_divided_when_reader' => 'Devisat per 2 dins la vista de lectura.',
 		'number_divided_when_reader' => 'Devisat per 2 dins la vista de lectura.',
 		'read' => array(
 		'read' => array(
 			'article_open_on_website' => 'quand l’article es dobèrt sul site d’origina',
 			'article_open_on_website' => 'quand l’article es dobèrt sul site d’origina',
 			'article_viewed' => 'quand l’article es mostrat',
 			'article_viewed' => 'quand l’article es mostrat',
-			'scroll' => 'en davalant la pagina',
+			'scroll' => 'en davalar la pagina',
 			'upon_reception' => 'en recebre un article novèl',
 			'upon_reception' => 'en recebre un article novèl',
 			'when' => 'Marcar un article coma legit…',
 			'when' => 'Marcar un article coma legit…',
 		),
 		),

+ 16 - 16
app/i18n/oc/gen.php

@@ -43,29 +43,29 @@ return array(
 		),
 		),
 	),
 	),
 	'date' => array(
 	'date' => array(
-		'Apr' => 'a\b\r\i\a\l',
+		'Apr' => '\\a\\b\\r\\i\\a\\l',
 		'apr' => 'abr.',
 		'apr' => 'abr.',
 		'april' => 'abrial',
 		'april' => 'abrial',
-		'Aug' => 'a\g\o\s\t',
+		'Aug' => '\\a\\g\\o\\s\\t',
 		'aug' => 'agost',
 		'aug' => 'agost',
 		'august' => 'agost',
 		'august' => 'agost',
 		'before_yesterday' => 'Abans ièr',
 		'before_yesterday' => 'Abans ièr',
-		'Dec' => '\d\e\c\e\m\b\r\e',
+		'Dec' => '\\d\\e\\c\\e\\m\\b\\r\\e',
 		'dec' => 'dec.',
 		'dec' => 'dec.',
 		'december' => 'decembre',
 		'december' => 'decembre',
-		'Feb' => 'f\e\b\r\i\è\r',
+		'Feb' => '\\f\\e\\b\\r\\i\\è\\r',
 		'feb' => 'feb.',
 		'feb' => 'feb.',
 		'february' => 'febrièr',
 		'february' => 'febrièr',
-		'format_date' => 'j %s \de\ Y',
-		'format_date_hour' => 'j %s \de\ Y \a H\:i',
+		'format_date' => 'j \\d\\e %s \\d\\e Y',
+		'format_date_hour' => 'j \\d\\e %s \\d\\e Y \\a H\:i',
 		'fri' => 'dv',
 		'fri' => 'dv',
-		'Jan' => 'g\e\n\i\è\r',
+		'Jan' => '\\g\\e\\n\\i\\\r',
 		'jan' => 'gen.',
 		'jan' => 'gen.',
 		'january' => 'genièr',
 		'january' => 'genièr',
-		'Jul' => 'j\u\l\h\e\t',
+		'Jul' => '\\j\\u\\l\\h\\e\\t',
 		'jul' => 'julh',
 		'jul' => 'julh',
 		'july' => 'julhet',
 		'july' => 'julhet',
-		'Jun' => 'j\u\n\h',
+		'Jun' => '\\j\\u\\n\\h',
 		'jun' => 'junh',
 		'jun' => 'junh',
 		'june' => 'junh',
 		'june' => 'junh',
 		'last_3_month' => 'Dempuèi los tres darrièrs meses',
 		'last_3_month' => 'Dempuèi los tres darrièrs meses',
@@ -73,22 +73,22 @@ return array(
 		'last_month' => 'Dempuèi lo mes passat',
 		'last_month' => 'Dempuèi lo mes passat',
 		'last_week' => 'Dempuèi la setmana passada',
 		'last_week' => 'Dempuèi la setmana passada',
 		'last_year' => 'Dempuèi l’annada passada',
 		'last_year' => 'Dempuèi l’annada passada',
-		'Mar' => 'm\a\r\ç',
+		'Mar' => '\\m\\a\\r\\ç',
 		'mar' => 'març',
 		'mar' => 'març',
 		'march' => 'març',
 		'march' => 'març',
-		'May' => '\m\a\i',
+		'May' => '\\m\\a\\i',
 		'may' => 'mai',
 		'may' => 'mai',
 		'may_' => 'mai',
 		'may_' => 'mai',
 		'mon' => 'dl',
 		'mon' => 'dl',
 		'month' => 'meses',
 		'month' => 'meses',
-		'Nov' => '\n\o\v\e\m\b\r\e',
+		'Nov' => '\\n\\o\\v\\e\\m\\b\\r\\e',
 		'nov' => 'nov.',
 		'nov' => 'nov.',
 		'november' => 'novembre',
 		'november' => 'novembre',
-		'Oct' => '\o\c\t\ò\b\r\e',
+		'Oct' => '\\o\\c\\t\\ò\\b\\r\\e',
 		'oct' => 'oct.',
 		'oct' => 'oct.',
 		'october' => 'octòbre',
 		'october' => 'octòbre',
 		'sat' => 'ds',
 		'sat' => 'ds',
-		'Sep' => '\s\e\t\e\m\b\r\e',
+		'Sep' => '\\s\\e\\t\\e\\m\\b\\r\\e',
 		'sep' => 'set.',
 		'sep' => 'set.',
 		'september' => 'setembre',
 		'september' => 'setembre',
 		'sun' => 'dg',
 		'sun' => 'dg',
@@ -111,7 +111,7 @@ return array(
 			'request_failed' => 'Una requèsta a fach meuca, aquò pòt venir d’un problèma de connexion Internet.',
 			'request_failed' => 'Una requèsta a fach meuca, aquò pòt venir d’un problèma de connexion Internet.',
 			'title_new_articles' => 'FreshRSS : nòus articles !',
 			'title_new_articles' => 'FreshRSS : nòus articles !',
 		),
 		),
-		'new_article' => 'I a d’articles nòus disponibles, clicatz per actualizar la página.',
+		'new_article' => 'I a d’articles nòus disponibles, clicatz per actualizar la pagina.',
 		'should_be_activated' => 'JavaScript deu èsser activat',
 		'should_be_activated' => 'JavaScript deu èsser activat',
 	),
 	),
 	'lang' => array(
 	'lang' => array(
@@ -183,7 +183,7 @@ return array(
 	'short' => array(
 	'short' => array(
 		'attention' => 'Atencion !',
 		'attention' => 'Atencion !',
 		'blank_to_disable' => 'Daissar void per desactivar',
 		'blank_to_disable' => 'Daissar void per desactivar',
-		'by_author' => 'Per <em>%s</em>',
+		'by_author' => 'Per : ',
 		'by_default' => 'Per defaut',
 		'by_default' => 'Per defaut',
 		'damn' => 'Zut !',
 		'damn' => 'Zut !',
 		'default_category' => 'Pas triat',
 		'default_category' => 'Pas triat',

+ 7 - 7
app/i18n/oc/sub.php

@@ -1,7 +1,7 @@
 <?php
 <?php
 return array(
 return array(
 	'api' => array(
 	'api' => array(
-		'documentation' => 'Copiar l’URL seguenta per l’utilizaire dins d’una aisina extèrna.',
+		'documentation' => 'Copiatz l’URL seguenta per l’utilizaire dins d’una aisina extèrna.',
 		'title' => 'API',
 		'title' => 'API',
 	),
 	),
 	'bookmarklet' => array(
 	'bookmarklet' => array(
@@ -46,11 +46,11 @@ return array(
 		),
 		),
 		'websub' => 'Notificaciones instantáneas amb WebSub',
 		'websub' => 'Notificaciones instantáneas amb WebSub',
 		'show' => array(
 		'show' => array(
-			'all' => 'Show all feeds',	//TODO - Translation
-			'error' => 'Show only feeds with error',	//TODO - Translation
+			'all' => 'Mostrar totes los fluxes',
+			'error' => 'Mostrar pas que los fluxes amb errors',
 		),
 		),
 		'showing' => array(
 		'showing' => array(
-			'error' => 'Showing only feeds with error',	//TODO - Translation
+			'error' => 'Afichatge dels articles amb errors solament',
 		),
 		),
 		'ssl_verify' => 'Verificacion de la seguretat SSL',
 		'ssl_verify' => 'Verificacion de la seguretat SSL',
 		'stats' => 'Estatisticas',
 		'stats' => 'Estatisticas',
@@ -64,14 +64,14 @@ return array(
 		'website' => 'URL del site',
 		'website' => 'URL del site',
 	),
 	),
 	'firefox' => array(
 	'firefox' => array(
-		'documentation' => 'Seguir las etapas descrichas <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">aquí</a> per ajustar FreshRSS a la lista dels lectors de flux de Firefox.',
+		'documentation' => 'Seguissètz las etapas descrichas <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">aquí</a> per ajustar FreshRSS a la lista dels lectors de flux de Firefox.',
 		'title' => 'Lector de flux de Firefox',
 		'title' => 'Lector de flux de Firefox',
 	),
 	),
 	'import_export' => array(
 	'import_export' => array(
 		'export' => 'Exportar',
 		'export' => 'Exportar',
 		'export_opml' => 'Exportar la lista de fluxes (OPML)',
 		'export_opml' => 'Exportar la lista de fluxes (OPML)',
 		'export_starred' => 'Exportar los favorits',
 		'export_starred' => 'Exportar los favorits',
-		'export_labelled' => 'Export your labelled articles',	//TODO
+		'export_labelled' => 'Exportar los articles etiquetats',
 		'feed_list' => 'Lista dels %s articles',
 		'feed_list' => 'Lista dels %s articles',
 		'file_to_import' => 'Fichièr d’importar<br />(OPML, JSON o ZIP)',
 		'file_to_import' => 'Fichièr d’importar<br />(OPML, JSON o ZIP)',
 		'file_to_import_no_zip' => 'Fichièr d’importar<br />(OPML o JSON)',
 		'file_to_import_no_zip' => 'Fichièr d’importar<br />(OPML o JSON)',
@@ -87,7 +87,7 @@ return array(
 		'subscription_tools' => 'Aisinas d’abonament',
 		'subscription_tools' => 'Aisinas d’abonament',
 	),
 	),
 	'title' => array(
 	'title' => array(
-		'_' => 'Gestión dels abonaments',
+		'_' => 'Gestion dels abonaments',
 		'feed_management' => 'Gestion dels fluxes RSS',
 		'feed_management' => 'Gestion dels fluxes RSS',
 		'subscription_tools' => 'Aisinas d’abonament',
 		'subscription_tools' => 'Aisinas d’abonament',
 	),
 	),

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

@@ -72,7 +72,7 @@
 		</div>
 		</div>
 
 
 		<div class="form-group">
 		<div class="form-group">
-			<label class="group-name" for="theme"><?php echo _t('conf.display.icon.entry'); ?></label>
+			<label class="group-name"><?php echo _t('conf.display.icon.entry'); ?></label>
 			<table>
 			<table>
 				<thead>
 				<thead>
 					<tr>
 					<tr>

+ 1 - 0
app/views/helpers/export/articles.phtml

@@ -56,6 +56,7 @@ foreach ($this->entriesRaw as $entryRaw) {
 			'feedUrl' => $feed == null ? '' : $feed->url(),
 			'feedUrl' => $feed == null ? '' : $feed->url(),
 		)
 		)
 	);
 	);
+	$article['categories'][] = $entry->isRead() ? 'user/-/state/com.google/read' : 'user/-/state/com.google/unread';
 	if ($entry->isFavorite()) {
 	if ($entry->isFavorite()) {
 		$article['categories'][] = 'user/-/state/com.google/starred';
 		$article['categories'][] = 'user/-/state/com.google/starred';
 	}
 	}

+ 1 - 1
constants.php

@@ -2,7 +2,7 @@
 //NB: Do not edit; use ./constants.local.php instead.
 //NB: Do not edit; use ./constants.local.php instead.
 
 
 //<Not customisable>
 //<Not customisable>
-define('FRESHRSS_VERSION', '1.13.1-dev');
+define('FRESHRSS_VERSION', '1.13.2-dev');
 define('FRESHRSS_WEBSITE', 'https://freshrss.org');
 define('FRESHRSS_WEBSITE', 'https://freshrss.org');
 define('FRESHRSS_WIKI', 'https://freshrss.github.io/FreshRSS/');
 define('FRESHRSS_WIKI', 'https://freshrss.github.io/FreshRSS/');
 
 

+ 8 - 2
p/scripts/main.js

@@ -297,6 +297,9 @@ function mark_favorite(div) {
 	});
 	});
 }
 }
 
 
+var freshrssOpenArticleEvent = document.createEvent('Event');
+freshrssOpenArticleEvent.initEvent('freshrss:openArticle', true, true);
+
 function toggleContent(new_active, old_active, skipping) {
 function toggleContent(new_active, old_active, skipping) {
 	// If skipping, move current without activating or marking as read
 	// If skipping, move current without activating or marking as read
 	if (!new_active) {
 	if (!new_active) {
@@ -348,8 +351,11 @@ function toggleContent(new_active, old_active, skipping) {
 		box_to_move.scrollTop = new_pos;
 		box_to_move.scrollTop = new_pos;
 	}
 	}
 
 
-	if (context.auto_mark_article && new_active.classList.contains('active') && !skipping) {
-		mark_read(new_active, true);
+	if (new_active.classList.contains('active') && !skipping) {
+		if (context.auto_mark_article) {
+			mark_read(new_active, true);
+		}
+		new_active[0].dispatchEvent(freshrssOpenArticleEvent);
 	}
 	}
 	onScroll();
 	onScroll();
 }
 }