Răsfoiți Sursa

Merge pull request #2633 from FreshRSS/dev

FreshRSS 1.15.1
Alexandre Alapetite 6 ani în urmă
părinte
comite
91cb165829

+ 17 - 0
CHANGELOG.md

@@ -1,5 +1,22 @@
 # FreshRSS changelog
 
+## 2019-11-06 FreshRSS 1.15.1
+
+* Features
+	* New approach based on OPML to definite default feeds for new users [#2627](https://github.com/FreshRSS/FreshRSS/pull/2627)
+* API
+	* Always send articles IDs as string, to fix compatibility with Reeder [#2621](https://github.com/FreshRSS/FreshRSS/pull/2621)
+* Bug fixing (regressions from 1.15.0)
+	* Fix database auto-creation at install [#2635](https://github.com/FreshRSS/FreshRSS/pull/2635)
+	* Fix bug in database size estimation with PostgreSQL for users with uppercase names [#2631](https://github.com/FreshRSS/FreshRSS/pull/2631)
+	* Reset name of default category (which cannot be customised anymore) [#2639](https://github.com/FreshRSS/FreshRSS/pull/2639)
+	* Fix UI style details [#2634](https://github.com/FreshRSS/FreshRSS/pull/2634)
+* Security
+	* Improve cookie security with policy `SameSite=Lax` [#2630](https://github.com/FreshRSS/FreshRSS/pull/2630)
+* Misc.
+	* Perform automatic git updates with safer fetch+reset instead of clean+fetch+merge [#2625](https://github.com/FreshRSS/FreshRSS/pull/2625)
+
+
 ## 2019-10-31 FreshRSS 1.15.0
 
 * CLI

+ 4 - 3
Docker/README.md

@@ -162,10 +162,11 @@ docker build --pull --tag freshrss/freshrss -f Docker/Dockerfile .
 ## Command line
 
 ```sh
-docker exec --user apache -it freshrss php ./cli/list-users.php
+docker exec --user www-data -it freshrss php ./cli/list-users.php
 ```
 
 See the [CLI documentation](../cli/) for all the other commands.
+You might have to replace `--user www-data` by `--user apache` when using our images based on Linux Alpine.
 
 
 ## Debugging
@@ -210,7 +211,7 @@ Remember not pass the `CRON_MIN` environment variable to your Docker run, to avo
 Example on Debian / Ubuntu: Create `/etc/cron.d/FreshRSS` with:
 
 ```
-7,37 * * * * root docker exec --user apache -it freshrss php ./app/actualize_script.php > /tmp/FreshRSS.log 2>&1
+7,37 * * * * root docker exec --user www-data -it freshrss php ./app/actualize_script.php > /tmp/FreshRSS.log 2>&1
 ```
 
 ### Option 3) Cron as another instance of the same FreshRSS Docker image
@@ -320,7 +321,7 @@ server {
 	# Other SSL stuff goes here
 
 	# Needed for Freshrss cookie/session :
-	proxy_cookie_path / "/; HTTPOnly; Secure";
+	proxy_cookie_path / "/; HTTPOnly; Secure; SameSite=Lax";
 
 	location / {
 		try_files $uri $uri/ =404;

+ 12 - 8
app/Controllers/updateController.php

@@ -23,26 +23,28 @@ class FreshRSS_update_Controller extends Minz_ActionController {
 		}
 		chdir($cwd);
 		$line = is_array($output) ? implode('; ', $output) : '' . $output;
-		return strpos($line, '[behind') !== false;
+		return strpos($line, '[behind') !== false || strpos($line, '[ahead') !== false;
 	}
 
 	public static function gitPull() {
 		$cwd = getcwd();
 		chdir(FRESHRSS_PATH);
-		$output = array();
+		$output = '';
 		$return = 1;
 		try {
-			exec('git clean -f -d -f', $output, $return);
+			exec('git fetch', $output, $return);
 			if ($return == 0) {
-				exec('git pull --ff-only', $output, $return);
-			} else {
-				$line = is_array($output) ? implode('; ', $output) : '' . $output;
-				Minz_Log::warning('git clean warning:' . $line);
+				exec('git reset --hard FETCH_HEAD', $output, $return);
 			}
 		} catch (Exception $e) {
-			Minz_Log::warning('git pull error:' . $e->getMessage());
+			Minz_Log::warning('Git error:' . $e->getMessage());
+			if ($output == '') {
+				$output = $e->getMessage();
+			}
+			$return = 1;
 		}
 		chdir($cwd);
+		deleteInstall();
 		$line = is_array($output) ? implode('; ', $output) : '' . $output;
 		return $return == 0 ? true : 'Git error: ' . $line;
 	}
@@ -52,6 +54,8 @@ class FreshRSS_update_Controller extends Minz_ActionController {
 			Minz_Error::error(403);
 		}
 
+		include_once(LIB_PATH . '/lib_install.php');
+
 		invalidateHttpCache();
 
 		$this->view->update_to_apply = false;

+ 15 - 1
app/Controllers/userController.php

@@ -248,7 +248,21 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 		}
 		if ($ok) {
 			$newUserDAO = FreshRSS_Factory::createUserDao($new_user_name);
-			$ok &= $newUserDAO->createUser($insertDefaultFeeds);
+			$ok &= $newUserDAO->createUser();
+
+			if ($ok && $insertDefaultFeeds) {
+				$opmlPath = DATA_PATH . '/opml.xml';
+				if (!file_exists($opmlPath)) {
+					$opmlPath = FRESHRSS_PATH . '/opml.default.xml';
+				}
+				$importController = new FreshRSS_importExport_Controller();
+				try {
+					$importController->importFile($opmlPath, $opmlPath, $new_user_name);
+				} catch (Exception $e) {
+					Minz_Log::error('Error while importing default OPML for user ' . $new_user_name . ': ' . $e->getMessage());
+				}
+			}
+
 			$ok &= self::updateUser($new_user_name, $email, $passwordPlain, $apiPasswordPlain);
 		}
 		return $ok;

+ 13 - 0
app/Models/CategoryDAO.php

@@ -4,6 +4,16 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
 
 	const DEFAULTCATEGORYID = 1;
 
+	public function resetDefaultCategoryName() {
+		//FreshRSS 1.15.1
+		$stm = $this->pdo->prepare('UPDATE `_category` SET name = :name WHERE id = :id');
+		if ($stm) {
+			$stm->bindValue(':id', self::DEFAULTCATEGORYID, PDO::PARAM_INT);
+			$stm->bindValue(':name', 'Uncategorized');
+		}
+		return $stm && $stm->execute();
+	}
+
 	protected function addColumn($name) {
 		Minz_Log::warning(__method__ . ': ' . $name);
 		try {
@@ -45,6 +55,9 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
 				} else {
 					$this->pdo->exec('DROP INDEX IF EXISTS feed_keep_history_index');	//SQLite at least drop index
 				}
+
+				$this->resetDefaultCategoryName();
+
 				return $ok;
 			}
 		} catch (Exception $e) {

+ 3 - 0
app/Models/DatabaseDAO.php

@@ -178,6 +178,9 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
 	}
 
 	public function minorDbMaintenance() {
+		$catDAO = FreshRSS_Factory::createCategoryDao();
+		$catDAO->resetDefaultCategoryName();
+
 		$this->ensureCaseInsensitiveGuids();
 	}
 

+ 9 - 6
app/Models/DatabaseDAOPGSQL.php

@@ -58,14 +58,17 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAOSQLite {
 			$stm->execute();
 		} else {
 			$sql = "SELECT "
-			     . "pg_total_relation_size('{$this->pdo->prefix()}category') + "
-			     . "pg_total_relation_size('{$this->pdo->prefix()}feed') + "
-			     . "pg_total_relation_size('{$this->pdo->prefix()}entry') + "
-			     . "pg_total_relation_size('{$this->pdo->prefix()}entrytmp') + "
-			     . "pg_total_relation_size('{$this->pdo->prefix()}tag') + "
-			     . "pg_total_relation_size('{$this->pdo->prefix()}entrytag')";
+			     . "pg_total_relation_size('`{$this->pdo->prefix()}category`') + "
+			     . "pg_total_relation_size('`{$this->pdo->prefix()}feed`') + "
+			     . "pg_total_relation_size('`{$this->pdo->prefix()}entry`') + "
+			     . "pg_total_relation_size('`{$this->pdo->prefix()}entrytmp`') + "
+			     . "pg_total_relation_size('`{$this->pdo->prefix()}tag`') + "
+			     . "pg_total_relation_size('`{$this->pdo->prefix()}entrytag`')";
 			$stm = $this->pdo->query($sql);
 		}
+		if ($stm == false) {
+			return 0;
+		}
 		$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
 		return $res[0];
 	}

+ 3 - 1
app/Models/FeedDAO.php

@@ -102,7 +102,9 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 				'httpAuth' => $feed->httpAuth(),
 				'attributes' => $feed->attributes(),
 			);
-			if ($feed->mute() || $feed->ttl() != FreshRSS_Context::$user_conf->ttl_default) {
+			if ($feed->mute() || (
+				FreshRSS_Context::$user_conf != null &&	//When creating a new user
+				$feed->ttl() != FreshRSS_Context::$user_conf->ttl_default)) {
 				$values['ttl'] = $feed->ttl() * ($feed->mute() ? -1 : 1);
 			}
 

+ 3 - 16
app/Models/UserDAO.php

@@ -1,34 +1,21 @@
 <?php
 
 class FreshRSS_UserDAO extends Minz_ModelPdo {
-	public function createUser($insertDefaultFeeds = false) {
+	public function createUser() {
 		require(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php');
 
 		try {
 			$sql = $SQL_CREATE_TABLES . $SQL_CREATE_TABLE_ENTRYTMP . $SQL_CREATE_TABLE_TAGS;
 			$ok = $this->pdo->exec($sql) !== false;	//Note: Only exec() can take multiple statements safely.
-			if ($ok && $insertDefaultFeeds) {
-				$default_feeds = FreshRSS_Context::$system_conf->default_feeds;
-				$stm = $this->pdo->prepare($SQL_INSERT_FEED);
-				foreach ($default_feeds as $feed) {
-					$parameters = [
-						':url' => $feed['url'],
-						':name' => $feed['name'],
-						':website' => $feed['website'],
-						':description' => $feed['description'],
-					];
-					$ok &= ($stm && $stm->execute($parameters));
-				}
-			}
 		} catch (Exception $e) {
-			Minz_Log::error('Error while creating database for user: ' . $e->getMessage());
+			Minz_Log::error('Error while creating database for user ' . $this->current_user . ': ' . $e->getMessage());
 		}
 
 		if ($ok) {
 			return true;
 		} else {
 			$info = empty($stm) ? $this->pdo->errorInfo() : $stm->errorInfo();
-			Minz_Log::error(__METHOD__ . ' error: ' . $info[2]);
+			Minz_Log::error(__METHOD__ . ' error: ' . json_encode($info));
 			return false;
 		}
 	}

+ 0 - 5
app/SQL/install.sql.mysql.php

@@ -112,11 +112,6 @@ CREATE TABLE IF NOT EXISTS `_entrytag` (	-- v1.12
 ENGINE = INNODB;
 SQL;
 
-$SQL_INSERT_FEED = <<<'SQL'
-INSERT IGNORE INTO `_feed` (url, category, name, website, description, ttl)
-	VALUES(:url, 1, :name, :website, :description, 86400);
-SQL;
-
 $SQL_DROP_TABLES = <<<'SQL'
 DROP TABLE IF EXISTS `_entrytag`, `_tag`, `_entrytmp`, `_entry`, `_feed`, `_category`;
 SQL;

+ 0 - 6
app/SQL/install.sql.pgsql.php

@@ -100,12 +100,6 @@ CREATE TABLE IF NOT EXISTS `_entrytag` (
 CREATE INDEX IF NOT EXISTS `_entrytag_id_entry_index` ON `_entrytag` ("id_entry");
 SQL;
 
-$SQL_INSERT_FEED = <<<'SQL'
-INSERT INTO `_feed` (url, category, name, website, description, ttl)
-	SELECT :url::VARCHAR, 1, :name, :website, :description, 86400
-		WHERE NOT EXISTS (SELECT id FROM `_feed` WHERE url = :url);
-SQL;
-
 $SQL_DROP_TABLES = <<<'SQL'
 DROP TABLE IF EXISTS `_entrytag`, `_tag`, `_entrytmp`, `_entry`, `_feed`, `_category`;
 SQL;

+ 0 - 5
app/SQL/install.sql.sqlite.php

@@ -102,11 +102,6 @@ CREATE TABLE IF NOT EXISTS `entrytag` (
 CREATE INDEX IF NOT EXISTS entrytag_id_entry_index ON `entrytag` (`id_entry`);
 SQL;
 
-$SQL_INSERT_FEED = <<<'SQL'
-INSERT OR IGNORE INTO `feed` (url, category, name, website, description, ttl)
-	VALUES(:url, 1, :name, :website, :description, 86400);
-SQL;
-
 $SQL_DROP_TABLES = <<<'SQL'
 DROP TABLE IF EXISTS `entrytag`;
 DROP TABLE IF EXISTS `tag`;

+ 8 - 2
app/install.php

@@ -132,7 +132,7 @@ function saveStep2() {
 		$config_array = [
 			'salt' => generateSalt(),
 			'base_url' => $base_url,
-			'default_user' => 'admin',
+			'default_user' => '_',
 			'db' => [
 				'type' => $_SESSION['bd_type'],
 				'host' => $_SESSION['bd_host'],
@@ -154,12 +154,18 @@ function saveStep2() {
 		@unlink(DATA_PATH . '/config.php');	//To avoid access-rights problems
 		file_put_contents(DATA_PATH . '/config.php', "<?php\n return " . var_export($config_array, true) . ";\n");
 
+		if (function_exists('opcache_reset')) {
+			opcache_reset();
+		}
+
 		Minz_Configuration::register('system', DATA_PATH . '/config.php', FRESHRSS_PATH . '/config.default.php');
 		FreshRSS_Context::$system_conf = Minz_Configuration::get('system');
 
 		$ok = false;
 		try {
-			$ok = checkDb();
+			Minz_Session::_param('currentUser', $config_array['default_user']);
+			$ok = initDb();
+			Minz_Session::_param('currentUser');
 		} catch (Exception $ex) {
 			$_SESSION['bd_error'] = $ex->getMessage();
 			$ok = false;

+ 18 - 1
cli/do-install.php

@@ -82,7 +82,24 @@ if (file_put_contents(join_path(DATA_PATH, 'config.php'),
 	fail('FreshRSS could not write configuration file!: ' . join_path(DATA_PATH, 'config.php'));
 }
 
-if (!checkDb()) {
+if (function_exists('opcache_reset')) {
+	opcache_reset();
+}
+
+Minz_Configuration::register('system', DATA_PATH . '/config.php', FRESHRSS_PATH . '/config.default.php');
+FreshRSS_Context::$system_conf = Minz_Configuration::get('system');
+
+Minz_Session::_param('currentUser', $config['default_user']);
+
+$ok = false;
+try {
+	$ok = initDb();
+} catch (Exception $ex) {
+	$_SESSION['bd_error'] = $ex->getMessage();
+	$ok = false;
+}
+
+if (!$ok) {
 	@unlink(join_path(DATA_PATH, 'config.php'));
 	fail('FreshRSS database error: ' . (empty($_SESSION['bd_error']) ? 'Unknown error' : $_SESSION['bd_error']));
 }

+ 0 - 10
config.default.php

@@ -156,16 +156,6 @@ return array(
 
 	],
 
-	# Configure the default feeds to which users will automatically be subscribed.
-	'default_feeds' => array(
-		array(
-			'url' => 'https://github.com/FreshRSS/FreshRSS/releases.atom',
-			'name' => 'FreshRSS releases',
-			'website' => 'https://github.com/FreshRSS/FreshRSS/',
-			'description' => 'FreshRSS releases @ GitHub',
-		),
-	),
-
 	# Configuration to send emails. Be aware that PHP < 5.5 are not supported.
 	# These options are basically a mapping of the PHPMailer class attributes
 	# from the PHPMailer library.

+ 1 - 1
constants.php

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

+ 1 - 0
data/.gitignore

@@ -7,3 +7,4 @@ last_update.txt
 no-cache.txt
 update.php
 tos.html
+opml.xml

+ 1 - 1
docs/en/admins/07_LinuxUpdate.md

@@ -35,7 +35,7 @@ If there's not an update, you're done! If there is, continue the following steps
 
 5. Discard manual changes and delete manual additions
 ```
-get reset --hard
+git reset --hard
 git clean -f -d
 ```
 

+ 3 - 1
lib/Minz/ModelPdo.php

@@ -28,6 +28,9 @@ class Minz_ModelPdo {
 		if ($currentUser === null) {
 			$currentUser = Minz_Session::param('currentUser');
 		}
+		if ($currentUser == '') {
+			throw new Minz_PDOConnectionException('Current user must not be empty!', '', Minz_Exception::ERROR);
+		}
 		if ($currentPdo != null) {
 			$this->pdo = $currentPdo;
 			return;
@@ -84,7 +87,6 @@ class Minz_ModelPdo {
 						'Invalid database type!',
 						$db['user'], Minz_Exception::ERROR
 					);
-					break;
 			}
 			self::$sharedPdo = $this->pdo;
 		} catch (Exception $e) {

+ 14 - 7
lib/lib_install.php

@@ -78,21 +78,28 @@ function generateSalt() {
 	return sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__)));
 }
 
-function checkDb() {
+function initDb() {
 	$conf = FreshRSS_Context::$system_conf;
 	$db = $conf->db;
 	if (empty($db['pdo_options'])) {
 		$db['pdo_options'] = [];
 	}
 	$db['pdo_options'][PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
-	$dbBase = isset($db['base']) ? $db['base'] : '';
+	$conf->db = $db;	//TODO: Remove this Minz limitation "Indirect modification of overloaded property"
 
-	$db['base'] = '';	//First connection without database name to create the database
-	Minz_ModelPdo::$usesSharedPdo = false;
-	$databaseDAO = FreshRSS_Factory::createDatabaseDAO();
-	$databaseDAO->create();
+	if ($db['type'] !== 'sqlite') {
+		Minz_ModelPdo::$usesSharedPdo = false;
+		$dbBase = isset($db['base']) ? $db['base'] : '';
+		$db['base'] = '';
+		$conf->db = $db;
+		//First connection without database name to create the database
+		$databaseDAO = FreshRSS_Factory::createDatabaseDAO();
+		$db['base'] = $dbBase;
+		$conf->db = $db;
+		$databaseDAO->create();
+	}
 
-	$db['base'] = $dbBase;	//New connection with the database name
+	//New connection with the database name
 	$databaseDAO = FreshRSS_Factory::createDatabaseDAO();
 	Minz_ModelPdo::$usesSharedPdo = true;
 	return $databaseDAO->testConnection();

+ 14 - 0
opml.default.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+	Configure the default feeds to which users will automatically be subscribed.
+	Do not edit this file but instead create your own ./data/opml.xml
+-->
+<opml version="2.0">
+	<head>
+		<title>FreshRSS default OPML</title>
+		<dateCreated>Sat, 02 Nov 2019 12:00:00</dateCreated>
+	</head>
+	<body>
+		<outline text="FreshRSS releases" type="rss" xmlUrl="https://github.com/FreshRSS/FreshRSS/releases.atom" htmlUrl="https://github.com/FreshRSS/FreshRSS/" description="FreshRSS releases @ GitHub"/>
+	</body>
+</opml>

+ 1 - 0
p/.htaccess

@@ -37,4 +37,5 @@ AddDefaultCharset	UTF-8
 	<FilesMatch "\.(css|gif|html|ico|js|png|svg|woff|woff2)$">
 		Header	merge Cache-Control "public"
 	</FilesMatch>
+	Header edit Set-Cookie ^(.*)$ "$1; SameSite=Lax"
 </IfModule>

+ 1 - 1
p/api/greader.php

@@ -696,7 +696,7 @@ function streamContentsItemsIds($streamId, $start_time, $stop_time, $count, $ord
 	$itemRefs = array();
 	foreach ($ids as $id) {
 		$itemRefs[] = array(
-			'id' => $id,	//64-bit decimal
+			'id' => '' . $id,	//64-bit decimal
 		);
 	}
 

+ 10 - 12
p/themes/Ansum/_list-view.scss

@@ -20,24 +20,19 @@
 		border-left-color: $main-first;
 	}
 
-	&.not_read {
+	&.not_read:not(.current) {
 		background: $unread-bg; //--------------------
-		// border-left-color: #FF5300;
-		&:hover {
-			background: $unread-bg-light; //--------------------
-		}
-
-		&:not(.current):hover .item.title {
-			background: $unread-bg-light;
-
 
+		&:hover .item.title {
+			background: $unread-bg;
 		}
+	}
 
+	&.not_read {
 		.item.title {
 			a {
 				color: $unread-font-color; //--------------------
 			}
-
 		}
 
 		.item.website {
@@ -52,12 +47,15 @@
 	}
 
 	&.favorite {
-		background: $fav-light;
 		border-left-color: $fav-bg;
 
 		@include transition(all, 0.15s, ease-in-out);
+	}
 
-		&:not(.current):hover .item.title {
+	&.favorite:not(.current) {
+		background: $fav-light;
+
+		&:hover .item.title {
 			background: $fav-light;
 		}
 	}

+ 10 - 11
p/themes/Ansum/ansum.css

@@ -963,25 +963,24 @@ form th {
   .flux.current {
     background: #fff;
     border-left-color: #ca7227; }
-  .flux.not_read {
+  .flux.not_read:not(.current) {
     background: #f2f6f8; }
-    .flux.not_read:hover {
-      background: #fdfdfe; }
     .flux.not_read:not(.current):hover .item.title {
-      background: #fdfdfe; }
-    .flux.not_read .item.title a {
-      color: #161a38; }
-    .flux.not_read .item.website a {
-      color: #161a38; }
-    .flux.not_read .item.date {
-      color: #161a3899; }
+      background: #f2f6f8; }
+  .flux.not_read .item.title a {
+    color: #161a38; }
+  .flux.not_read .item.website a {
+    color: #161a38; }
+  .flux.not_read .item.date {
+    color: #161a3899; }
   .flux.favorite {
-    background: #fff6da;
     border-left-color: #ffc300;
     -webkit-transition: all 0.15s ease-in-out;
     -moz-transition: all 0.15s ease-in-out;
     -o-transition: all 0.15s ease-in-out;
     transition: all 0.15s ease-in-out; }
+  .flux.favorite:not(.current) {
+    background: #fff6da; }
     .flux.favorite:not(.current):hover .item.title {
       background: #fff6da; }
   .flux .website a {

+ 4 - 1
p/themes/Flat/flat.css

@@ -803,10 +803,13 @@ a.btn {
 }
 
 .flux.favorite {
-	background: #fff6da;
 	border-left-color: #ffc300;
 }
 
+.flux.favorite:not(.current) {
+	background: #fff6da;
+}
+
 .flux.favorite:not(.current):hover .item.title {
 	background: #fff6da;
 }

+ 10 - 11
p/themes/Mapco/_list-view.scss

@@ -20,19 +20,15 @@
 		border-left-color: $main-first;
 	}
 
-	&.not_read {
+	&.not_read:not(.current) {
 		background: $unread-bg; //--------------------
-		// border-left-color: #FF5300;
-		&:hover {
-			background: $unread-bg-light; //--------------------
-		}
-
-		&:not(.current):hover .item.title {
-			background: $unread-bg-light;
-
 
+		&:hover .item.title {
+			background: $unread-bg;
 		}
+	}
 
+	&.not_read {
 		.item.title {
 			a {
 				color: $unread-font-color; //--------------------
@@ -52,12 +48,15 @@
 	}
 
 	&.favorite {
-		background: $fav-light;
 		border-left-color: $fav-bg;
 
 		@include transition(all, 0.15s, ease-in-out);
+	}
 
-		&:not(.current):hover .item.title {
+	&.favorite:not(.current) {
+		background: $fav-light;
+
+		&:hover .item.title {
 			background: $fav-light;
 		}
 	}

+ 10 - 11
p/themes/Mapco/mapco.css

@@ -970,26 +970,25 @@ form th {
   .flux.current {
     background: #f9fafb;
     border-left-color: #36c; }
-  .flux.not_read {
+  .flux.not_read:not(.current) {
     background: #f2f6f8; }
-    .flux.not_read:hover {
-      background: #fdfdfe; }
     .flux.not_read:not(.current):hover .item.title {
-      background: #fdfdfe; }
-    .flux.not_read .item.title a {
-      color: #36c; }
-    .flux.not_read .item.website a {
-      color: #36c; }
-    .flux.not_read .item.date {
-      color: #36c99; }
+      background: #f2f6f8; }
+  .flux.not_read .item.title a {
+    color: #36c; }
+  .flux.not_read .item.website a {
+    color: #36c; }
+  .flux.not_read .item.date {
+    color: #36c99; }
   .flux.favorite {
-    background: #fff6da;
     border-left-color: #ffc300;
     -webkit-transition: all 0.15s ease-in-out;
     -moz-transition: all 0.15s ease-in-out;
     -o-transition: all 0.15s ease-in-out;
     -ms-transition: all 0.15s ease-in-out;
     transition: all 0.15s ease-in-out; }
+  .flux.favorite:not(.current) {
+    background: #fff6da; }
     .flux.favorite:not(.current):hover .item.title {
       background: #fff6da; }
   .flux .website a {

+ 4 - 1
p/themes/Origine-compact/origine-compact.css

@@ -837,10 +837,13 @@ a.btn,
 }
 
 .flux.favorite {
-	background: #fff6da;
 	border-left: 2px solid #ffc300;
 }
 
+.flux.favorite:not(.current) {
+	background: #fff6da;
+}
+
 .flux.favorite:not(.current):hover .item.title {
 	background: #fff6da;
 }

+ 4 - 1
p/themes/Origine/origine.css

@@ -794,10 +794,13 @@ a.btn {
 }
 
 .flux.favorite {
-	background: #fff6da;
 	border-left: 2px solid #ffc300;
 }
 
+.flux.favorite:not(.current) {
+	background: #fff6da;
+}
+
 .flux.favorite:not(.current):hover .item.title {
 	background: #fff6da;
 }

+ 4 - 1
p/themes/Pafat/pafat.css

@@ -798,10 +798,13 @@ a.btn {
 }
 
 .flux.favorite {
-	background: #fff6da;
 	border-left: 2px solid #428bca;
 }
 
+.flux.favorite:not(.current) {
+	background: #fff6da;
+}
+
 .flux.favorite:not(.current):hover .item.title {
 	background: #fff6da;
 }

+ 5 - 5
p/themes/Swage/swage.css

@@ -585,14 +585,14 @@ form th {
     .flux:hover:not(.current):hover .item.title,
     .flux .current:not(.current):hover .item.title {
       background: #fff; }
-  .flux.not_read:not(.current) {
-    background: #fff3ed; }
-  .flux.not_read:not(.current):hover .item.title {
-    background: #fff3ed; }
-  .flux.favorite {
+  .flux.favorite:not(.current) {
     background: #fff6da; }
     .flux.favorite:not(.current):hover .item.title {
       background: #fff6da; }
+  .flux.not_read:not(.current) {
+    background: #fff3ed; }
+    .flux.not_read:not(.current):hover .item.title {
+      background: #fff3ed; }
   .flux .date {
     color: #969696;
     font-size: 0.7rem; }

+ 8 - 10
p/themes/Swage/swage.scss

@@ -928,21 +928,19 @@ form {
 		}
 	}
 
-	&.not_read {
-		&:not(.current) {
-			background: $color_unread;
-		}
+	&.favorite:not(.current) {
+		background: $color_stared;
 
-		&:not(.current):hover .item.title {
-			background: $color_unread;
+		&:hover .item.title {
+			background: $color_stared;
 		}
 	}
 
-	&.favorite, {
-		background: $color_stared;
+	&.not_read:not(.current) {
+		background: $color_unread;
 
-		&:not(.current):hover .item.title {
-			background: $color_stared;
+		&:hover .item.title {
+			background: $color_unread;
 		}
 	}