Explorar el Código

JSON column for feeds (#1838)

* Draft of JSON column for feeds
https://github.com/FreshRSS/FreshRSS/issues/1654

* Add some per-feed options
  * Feed cURL timeout
  * Mark updated articles as read https://github.com/FreshRSS/FreshRSS/issues/891
  * Mark as read upon reception https://github.com/FreshRSS/FreshRSS/issues/1702
  * Ignore SSL (unsafe) https://github.com/FreshRSS/FreshRSS/issues/1811

* Try PHPCS workaround
While waiting for a better syntax support
Alexandre Alapetite hace 8 años
padre
commit
b552abb332

+ 10 - 5
app/Controllers/feedController.php

@@ -84,6 +84,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 			'description' => $feed->description(),
 			'lastUpdate' => time(),
 			'httpAuth' => $feed->httpAuth(),
+			'attributes' => array(),
 		);
 
 		$id = $feedDAO->addFeed($values);
@@ -271,7 +272,6 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 
 		$updated_feeds = 0;
 		$nb_new_articles = 0;
-		$is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0;
 		foreach ($feeds as $feed) {
 			$url = $feed->url();	//For detection of HTTP 301
 
@@ -353,8 +353,10 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 						} else {	//This entry already exists but has been updated
 							//Minz_Log::debug('Entry with GUID `' . $entry->guid() . '` updated in feed ' . $feed->id() .
 								//', old hash ' . $existingHash . ', new hash ' . $entry->hash());
-							//TODO: Make an updated/is_read policy by feed, in addition to the global one.
-							$needFeedCacheRefresh = FreshRSS_Context::$user_conf->mark_updated_article_unread;
+							$mark_updated_article_unread = $feed->attributes('mark_updated_article_unread') !== null ? (
+									$feed->attributes('mark_updated_article_unread')
+								) : FreshRSS_Context::$user_conf->mark_updated_article_unread;
+							$needFeedCacheRefresh = $mark_updated_article_unread;
 							$entry->_isRead(FreshRSS_Context::$user_conf->mark_updated_article_unread ? false : null);	//Change is_read according to policy.
 							if (!$entryDAO->inTransaction()) {
 								$entryDAO->beginTransaction();
@@ -365,15 +367,18 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
 						// This entry should not be added considering configuration and date.
 						$oldGuids[] = $entry->guid();
 					} else {
+						$read_upon_reception = $feed->attributes('read_upon_reception') !== null ? (
+								$feed->attributes('read_upon_reception')
+							) : FreshRSS_Context::$user_conf->mark_when['reception'];
 						if ($isNewFeed) {
 							$id = min(time(), $entry_date) . uSecString();
-							$entry->_isRead($is_read);
+							$entry->_isRead($read_upon_reception);
 						} elseif ($entry_date < $date_min) {
 							$id = min(time(), $entry_date) . uSecString();
 							$entry->_isRead(true);	//Old article that was not in database. Probably an error, so mark as read
 						} else {
 							$id = uTimeString();
-							$entry->_isRead($is_read);
+							$entry->_isRead($read_upon_reception);
 						}
 						$entry->_id($id);
 

+ 14 - 5
app/Controllers/subscriptionController.php

@@ -15,7 +15,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
 		}
 
 		$catDAO = new FreshRSS_CategoryDAO();
-		$feedDAO = new FreshRSS_FeedDAO();
+		$feedDAO = FreshRSS_Factory::createFeedDao();
 
 		$catDAO->checkDefault();
 		$feedDAO->updateTTL();
@@ -74,9 +74,10 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
 			return;
 		}
 
-		$this->view->feed = $this->view->feeds[$id];
+		$feed = $this->view->feeds[$id];
+		$this->view->feed = $feed;
 
-		Minz_View::prependTitle(_t('sub.title.feed_management') . ' · ' . $this->view->feed->name() . ' · ');
+		Minz_View::prependTitle(_t('sub.title.feed_management') . ' · ' . $feed->name() . ' · ');
 
 		if (Minz_Request::isPost()) {
 			$user = trim(Minz_Request::param('http_user_feed' . $id, ''));
@@ -95,6 +96,13 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
 				$ttl = FreshRSS_Context::$user_conf->ttl_default;
 			}
 
+			$feed->_attributes('mark_updated_article_unread', Minz_Request::paramTernary('mark_updated_article_unread'));
+			$feed->_attributes('read_upon_reception', Minz_Request::paramTernary('read_upon_reception'));
+			$feed->_attributes('ssl_verify', Minz_Request::paramTernary('ssl_verify'));
+
+			$timeout = intval(Minz_Request::param('timeout', 0));
+			$feed->_attributes('timeout', $timeout > 0 ? $timeout : null);
+
 			$values = array(
 				'name' => Minz_Request::param('name', ''),
 				'description' => sanitizeHTML(Minz_Request::param('description', '', true)),
@@ -106,14 +114,15 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
 				'httpAuth' => $httpAuth,
 				'keep_history' => intval(Minz_Request::param('keep_history', FreshRSS_Feed::KEEP_HISTORY_DEFAULT)),
 				'ttl' => $ttl * ($mute ? -1 : 1),
+				'attributes' => $feed->attributes()
 			);
 
 			invalidateHttpCache();
 
 			$url_redirect = array('c' => 'subscription', 'params' => array('id' => $id));
 			if ($feedDAO->updateFeed($id, $values) !== false) {
-				$this->view->feed->_category($cat);
-				$this->view->feed->faviconPrepare();
+				$feed->_category($cat);
+				$feed->faviconPrepare();
 
 				Minz_Request::good(_t('feedback.sub.feed.updated'), $url_redirect);
 			} else {

+ 1 - 1
app/Models/DatabaseDAO.php

@@ -50,7 +50,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo {
 	public function feedIsCorrect() {
 		return $this->checkTable('feed', array(
 			'id', 'url', 'category', 'name', 'website', 'description', 'lastUpdate',
-			'priority', 'pathEntries', 'httpAuth', 'error', 'keep_history', 'ttl',
+			'priority', 'pathEntries', 'httpAuth', 'error', 'keep_history', 'ttl', 'attributes',
 			'cache_nbEntries', 'cache_nbUnreads'
 		));
 	}

+ 7 - 1
app/Models/Factory.php

@@ -3,7 +3,13 @@
 class FreshRSS_Factory {
 
 	public static function createFeedDao($username = null) {
-		return new FreshRSS_FeedDAO($username);
+		$conf = Minz_Configuration::get('system');
+		switch ($conf->db['type']) {
+			case 'sqlite':
+				return new FreshRSS_FeedDAOSQLite($username);
+			default:
+				return new FreshRSS_FeedDAO($username);
+		}
 	}
 
 	public static function createEntryDao($username = null) {

+ 25 - 1
app/Models/Feed.php

@@ -26,6 +26,7 @@ class FreshRSS_Feed extends Minz_Model {
 	private $error = false;
 	private $keep_history = self::KEEP_HISTORY_DEFAULT;
 	private $ttl = self::TTL_DEFAULT;
+	private $attributes = array();
 	private $mute = false;
 	private $hash = null;
 	private $lockPath = '';
@@ -114,6 +115,13 @@ class FreshRSS_Feed extends Minz_Model {
 	public function ttl() {
 		return $this->ttl;
 	}
+	public function attributes($key = '') {
+		if ($key == '') {
+			return $this->attributes;
+		} else {
+			return isset($this->attributes[$key]) ? $this->attributes[$key] : null;
+		}
+	}
 	public function mute() {
 		return $this->mute;
 	}
@@ -234,6 +242,22 @@ class FreshRSS_Feed extends Minz_Model {
 		$this->ttl = abs($value);
 		$this->mute = $value < self::TTL_DEFAULT;
 	}
+
+	public function _attributes($key, $value) {
+		if ($key == '') {
+			if (is_string($value)) {
+				$value = json_decode($value, true);
+			}
+			if (is_array($value)) {
+				$this->attributes = $value;
+			}
+		} elseif ($value === null) {
+			unset($this->attributes[$key]);
+		} else {
+			$this->attributes[$key] = $value;
+		}
+	}
+
 	public function _nbNotRead($value) {
 		$this->nbNotRead = intval($value);
 	}
@@ -253,7 +277,7 @@ class FreshRSS_Feed extends Minz_Model {
 				if ($this->httpAuth != '') {
 					$url = preg_replace('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url);
 				}
-				$feed = customSimplePie();
+				$feed = customSimplePie($this->attributes());
 				if (substr($url, -11) === '#force_feed') {
 					$feed->force_feed(true);
 					$url = substr($url, 0, -11);

+ 65 - 8
app/Models/FeedDAO.php

@@ -1,6 +1,33 @@
 <?php
 
 class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
+
+	protected function addColumn($name) {
+		Minz_Log::warning('FreshRSS_FeedDAO::addColumn: ' . $name);
+		try {
+			if ($name === 'attributes') {	//v1.11.0
+				$stm = $this->bd->prepare('ALTER TABLE `' . $this->prefix . 'feed` ADD COLUMN attributes TEXT');
+				return $stm && $stm->execute();
+			}
+		} catch (Exception $e) {
+			Minz_Log::error('FreshRSS_FeedDAO::addColumn error: ' . $e->getMessage());
+		}
+		return false;
+	}
+
+	protected function autoUpdateDb($errorInfo) {
+		if (isset($errorInfo[0])) {
+			if ($errorInfo[0] === '42S22' || $errorInfo[0] === '42703') {	//ER_BAD_FIELD_ERROR (Mysql), undefined_column (PostgreSQL)
+				foreach (array('attributes') as $column) {
+					if (stripos($errorInfo[2], $column) !== false) {
+						return $this->addColumn($column);
+					}
+				}
+			}
+		}
+		return false;
+	}
+
 	public function addFeed($valuesTmp) {
 		$sql = '
 			INSERT INTO `' . $this->prefix . 'feed`
@@ -15,10 +42,11 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 					`httpAuth`,
 					error,
 					keep_history,
-					ttl
+					ttl,
+					attributes
 				)
 				VALUES
-				(?, ?, ?, ?, ?, ?, 10, ?, 0, ?, ?)';
+				(?, ?, ?, ?, ?, ?, 10, ?, 0, ?, ?, ?)';
 		$stm = $this->bd->prepare($sql);
 
 		$valuesTmp['url'] = safe_ascii($valuesTmp['url']);
@@ -34,12 +62,16 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 			base64_encode($valuesTmp['httpAuth']),
 			FreshRSS_Feed::KEEP_HISTORY_DEFAULT,
 			FreshRSS_Feed::TTL_DEFAULT,
+			isset($valuesTmp['attributes']) ? json_encode($valuesTmp['attributes']) : '',
 		);
 
 		if ($stm && $stm->execute($values)) {
 			return $this->bd->lastInsertId('"' . $this->prefix . 'feed_id_seq"');
 		} else {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+			if ($this->autoUpdateDb($info)) {
+				return $this->addFeed($valuesTmp);
+			}
 			Minz_Log::error('SQL error addFeed: ' . $info[2]);
 			return false;
 		}
@@ -60,7 +92,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 				'website' => $feed->website(),
 				'description' => $feed->description(),
 				'lastUpdate' => 0,
-				'httpAuth' => $feed->httpAuth()
+				'httpAuth' => $feed->httpAuth(),
+				'attributes' => $feed->attributes(),
 			);
 
 			$id = $this->addFeed($values);
@@ -87,8 +120,10 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		foreach ($valuesTmp as $key => $v) {
 			$set .= '`' . $key . '`=?, ';
 
-			if ($key == 'httpAuth') {
+			if ($key === 'httpAuth') {
 				$valuesTmp[$key] = base64_encode($v);
+			} elseif ($key === 'attributes') {
+				$valuesTmp[$key] = json_encode($v);
 			}
 		}
 		$set = substr($set, 0, -2);
@@ -105,11 +140,25 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 			return $stm->rowCount();
 		} else {
 			$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
+			if ($this->autoUpdateDb($info)) {
+				return $this->updateFeed($id, $valuesTmp);
+			}
 			Minz_Log::error('SQL error updateFeed: ' . $info[2] . ' for feed ' . $id);
 			return false;
 		}
 	}
 
+	public function updateFeedAttribute($feed, $key, $value) {
+		if ($feed instanceof FreshRSS_Feed) {
+			$feed->_attributes($key, $value);
+			return $this->updateFeed(
+					$feed->id(),
+					array('attributes' => $feed->attributes())
+				);
+		}
+		return false;
+	}
+
 	public function updateLastUpdate($id, $inError = false, $mtime = 0) {	//See also updateCachedValue()
 		$sql = 'UPDATE `' . $this->prefix . 'feed` '
 		     . 'SET `lastUpdate`=?, error=? '
@@ -252,15 +301,22 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 	 */
 	public function listFeedsOrderUpdate($defaultCacheDuration = 3600) {
 		$this->updateTTL();
-		$sql = 'SELECT id, url, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, keep_history, ttl '
+		$sql = 'SELECT id, url, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, keep_history, ttl, attributes '
 		     . 'FROM `' . $this->prefix . 'feed` '
 		     . ($defaultCacheDuration < 0 ? '' : 'WHERE ttl >= ' . FreshRSS_Feed::TTL_DEFAULT
 		     . ' AND `lastUpdate` < (' . (time() + 60) . '-(CASE WHEN ttl=' . FreshRSS_Feed::TTL_DEFAULT . ' THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) ')
 		     . 'ORDER BY `lastUpdate`';
 		$stm = $this->bd->prepare($sql);
-		$stm->execute();
-
-		return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
+		if ($stm && $stm->execute()) {
+			return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
+		} else {
+			$info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo();
+			if ($this->autoUpdateDb($info)) {
+				return $this->listFeedsOrderUpdate($defaultCacheDuration);
+			}
+			Minz_Log::error('SQL error listFeedsOrderUpdate: ' . $info[2]);
+			return array();
+		}
 	}
 
 	public function listByCategory($cat) {
@@ -385,6 +441,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 			$myFeed->_error(isset($dao['error']) ? $dao['error'] : 0);
 			$myFeed->_keepHistory(isset($dao['keep_history']) ? $dao['keep_history'] : FreshRSS_Feed::KEEP_HISTORY_DEFAULT);
 			$myFeed->_ttl(isset($dao['ttl']) ? $dao['ttl'] : FreshRSS_Feed::TTL_DEFAULT);
+			$myFeed->_attributes('', isset($dao['attributes']) ? $dao['attributes'] : '');
 			$myFeed->_nbNotRead(isset($dao['cache_nbUnreads']) ? $dao['cache_nbUnreads'] : 0);
 			$myFeed->_nbEntries(isset($dao['cache_nbEntries']) ? $dao['cache_nbEntries'] : 0);
 			if (isset($dao['id'])) {

+ 17 - 0
app/Models/FeedDAOSQLite.php

@@ -0,0 +1,17 @@
+<?php
+
+class FreshRSS_FeedDAOSQLite extends FreshRSS_FeedDAO {
+
+	protected function autoUpdateDb($errorInfo) {
+		if ($tableInfo = $this->bd->query("PRAGMA table_info('feed')")) {
+			$columns = $tableInfo->fetchAll(PDO::FETCH_COLUMN, 1);
+			foreach (array('attributes') as $column) {
+				if (!in_array($column, $columns)) {
+					return $this->addColumn($column);
+				}
+			}
+		}
+		return false;
+	}
+
+}

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

@@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS `%1$sfeed` (
 	`error` boolean DEFAULT 0,
 	`keep_history` MEDIUMINT NOT NULL DEFAULT -2,	-- v0.7
 	`ttl` INT NOT NULL DEFAULT 0,	-- v0.7.3
+	`attributes` TEXT,	-- v1.11.0
 	`cache_nbEntries` int DEFAULT 0,	-- v0.7
 	`cache_nbUnreads` int DEFAULT 0,	-- v0.7
 	PRIMARY KEY (`id`),

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

@@ -22,6 +22,7 @@ $SQL_CREATE_TABLES = array(
 	"error" smallint DEFAULT 0,
 	"keep_history" INT NOT NULL DEFAULT -2,
 	"ttl" INT NOT NULL DEFAULT 0,
+	"attributes" TEXT,	-- v1.11.0
 	"cache_nbEntries" INT DEFAULT 0,
 	"cache_nbUnreads" INT DEFAULT 0,
 	FOREIGN KEY ("category") REFERENCES "%1$scategory" ("id") ON DELETE SET NULL ON UPDATE CASCADE

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

@@ -21,6 +21,7 @@ $SQL_CREATE_TABLES = array(
 	`error` boolean DEFAULT 0,
 	`keep_history` MEDIUMINT NOT NULL DEFAULT -2,
 	`ttl` INT NOT NULL DEFAULT 0,
+	`attributes` TEXT,	-- v1.11.0
 	`cache_nbEntries` int DEFAULT 0,
 	`cache_nbUnreads` int DEFAULT 0,
 	FOREIGN KEY (`category`) REFERENCES `category`(`id`) ON DELETE SET NULL ON UPDATE CASCADE,

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

@@ -44,8 +44,10 @@ return array(
 			'main_stream' => 'Zobrazit ve “Všechny kanály”',
 			'normal' => 'Show in its category', // TODO
 		),
+		'ssl_verify' => 'Verify SSL security',	//TODO
 		'stats' => 'Statistika',
 		'think_to_add' => 'Můžete přidat kanály.',
+		'timeout' => 'Timeout in seconds',	//TODO
 		'title' => 'Název',
 		'title_add' => 'Přidat RSS kanál',
 		'ttl' => 'Neobnovovat častěji než',

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

@@ -44,8 +44,10 @@ return array(
 			'main_stream' => 'In Haupt-Feeds zeigen',
 			'normal' => 'Zeige in eigener Kategorie',
 		),
+		'ssl_verify' => 'Verify SSL security',	//TODO
 		'stats' => 'Statistiken',
 		'think_to_add' => 'Sie können Feeds hinzufügen.',
+		'timeout' => 'Timeout in seconds',	//TODO
 		'title' => 'Titel',
 		'title_add' => 'Einen RSS-Feed hinzufügen',
 		'ttl' => 'Aktualisiere automatisch nicht öfter als',

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

@@ -44,8 +44,10 @@ return array(
 			'main_stream' => 'Show in main stream',
 			'normal' => 'Show in its category',
 		),
+		'ssl_verify' => 'Verify SSL security',
 		'stats' => 'Statistics',
 		'think_to_add' => 'You may add some feeds.',
+		'timeout' => 'Timeout in seconds',
 		'title' => 'Title',
 		'title_add' => 'Add a RSS feed',
 		'ttl' => 'Do not automatically refresh more often than',

+ 2 - 0
app/i18n/es/sub.php

@@ -39,8 +39,10 @@ return array(
 			'main_stream' => 'Mostrar en salida principal',
 			'normal' => 'Show in its category', // TODO
 		),
+		'ssl_verify' => 'Verify SSL security',	//TODO
 		'stats' => 'Estadísticas',
 		'think_to_add' => 'Puedes añadir fuentes.',
+		'timeout' => 'Timeout in seconds',	//TODO
 		'title' => 'Título',
 		'title_add' => 'Añadir fuente RSS',
 		'ttl' => 'No actualizar de forma automática con una frecuencia mayor a',

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

@@ -44,8 +44,10 @@ return array(
 			'main_stream' => 'Afficher dans le flux principal',
 			'normal' => 'Afficher dans sa catégorie',
 		),
+		'ssl_verify' => 'Vérification sécurité SSL',
 		'stats' => 'Statistiques',
 		'think_to_add' => 'Vous pouvez ajouter des flux.',
+		'timeout' => 'Délai d’attente en secondes',
 		'title' => 'Titre',
 		'title_add' => 'Ajouter un flux RSS',
 		'ttl' => 'Ne pas automatiquement rafraîchir plus souvent que',

+ 2 - 0
app/i18n/he/sub.php

@@ -44,8 +44,10 @@ return array(
 			'main_stream' => 'הצגה בזרם המרכזי',
 			'normal' => 'Show in its category', // TODO
 		),
+		'ssl_verify' => 'Verify SSL security',	//TODO
 		'stats' => 'סטטיסטיקות',
 		'think_to_add' => 'ניתן להוסיף הזנות חדשות.',
+		'timeout' => 'Timeout in seconds',	//TODO
 		'title' => 'כותרת',
 		'title_add' => 'הוספת הזנה',
 		'ttl' => 'אין לרענן אוטומטית יותר מ',

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

@@ -44,8 +44,10 @@ return array(
 			'main_stream' => 'Mostra in homepage', // TODO
 			'normal' => 'Show in its category', // TODO
 		),
+		'ssl_verify' => 'Verify SSL security',	//TODO
 		'stats' => 'Statistiche',
 		'think_to_add' => 'Aggiungi feed.',
+		'timeout' => 'Timeout in seconds',	//TODO
 		'title' => 'Titolo',
 		'title_add' => 'Aggiungi RSS feed',
 		'ttl' => 'Non aggiornare automaticamente piu di',

+ 2 - 0
app/i18n/kr/sub.php

@@ -44,8 +44,10 @@ return array(
 			'main_stream' => '메인 스트림에 표시하기',
 			'normal' => '피드가 속한 카테고리에만 표시하기',
 		),
+		'ssl_verify' => 'Verify SSL security',	//TODO
 		'stats' => '통계',
 		'think_to_add' => '피드를 추가할 수 있습니다.',
+		'timeout' => 'Timeout in seconds',	//TODO
 		'title' => '제목',
 		'title_add' => 'RSS 피드 추가',
 		'ttl' => '다음 시간이 지나기 전에 새로고침 금지',

+ 2 - 0
app/i18n/nl/sub.php

@@ -45,8 +45,10 @@ return array(
 			'normal' => 'Show in its category', // TODO
 		),
 		'pubsubhubbub' => 'Directe notificaties met PubSubHubbub',
+		'ssl_verify' => 'Verify SSL security',	//TODO
 		'stats' => 'Statistieken',
 		'think_to_add' => 'Voeg wat feeds toe.',
+		'timeout' => 'Timeout in seconds',	//TODO
 		'title' => 'Titel',
 		'title_add' => 'Voeg een RSS feed toe',
 		'ttl' => 'Vernieuw automatisch niet vaker dan',

+ 2 - 0
app/i18n/pt-br/sub.php

@@ -44,8 +44,10 @@ return array(
 			'main_stream' => 'Mostrar na tela principal',
 			'normal' => 'Show in its category', // TODO
 		),
+		'ssl_verify' => 'Verify SSL security',	//TODO
 		'stats' => 'Estatísticas',
 		'think_to_add' => 'Você deve adicionar alguns feeds.',
+		'timeout' => 'Timeout in seconds',	//TODO
 		'title' => 'Título',
 		'title_add' => 'Adicionar o RSS feed',
 		'ttl' => 'Não atualize automáticamente mais que',

+ 2 - 0
app/i18n/ru/sub.php

@@ -44,8 +44,10 @@ return array(
 			'main_stream' => 'Show in main stream', // TODO
 			'normal' => 'Show in its category', // TODO
 		),
+		'ssl_verify' => 'Verify SSL security',	//TODO
 		'stats' => 'Statistics',// TODO
 		'think_to_add' => 'You may add some feeds.',// TODO
+		'timeout' => 'Timeout in seconds',	//TODO
 		'title' => 'Title',// TODO
 		'title_add' => 'Add a RSS feed',// TODO
 		'ttl' => 'Do not automatically refresh more often than',// TODO

+ 2 - 0
app/i18n/tr/sub.php

@@ -44,8 +44,10 @@ return array(
 			'main_stream' => 'Ana akışda göster',
 			'normal' => 'Show in its category', // TODO
 		),
+		'ssl_verify' => 'Verify SSL security',	//TODO
 		'stats' => 'İstatistikler',
 		'think_to_add' => 'Akış ekleyebilirsiniz.',
+		'timeout' => 'Timeout in seconds',	//TODO
 		'title' => 'Başlık',
 		'title_add' => 'RSS akışı ekle',
 		'ttl' => 'Şu kadar süreden fazla otomatik yenileme yapma',

+ 2 - 0
app/i18n/zh-cn/sub.php

@@ -44,8 +44,10 @@ return array(
 			'main_stream' => '在首页中显示',
 			'normal' => '在分类中显示',
 		),
+		'ssl_verify' => 'Verify SSL security',	//TODO
 		'stats' => '统计',
 		'think_to_add' => '你可以添加一些 RSS 源。',
+		'timeout' => 'Timeout in seconds',	//TODO
 		'title' => '标题',
 		'title_add' => '添加 RSS 源',
 		'ttl' => '最小自动更新时间',

+ 47 - 0
app/views/helpers/feed/update.phtml

@@ -178,6 +178,53 @@
 			</div>
 		</div>
 
+		<div class="form-group">
+			<label class="group-name" for="mark_updated_article_unread"><?php echo _t('conf.reading.mark_updated_article_unread'); ?></label>
+			<div class="group-controls">
+				<label class="checkbox" for="mark_updated_article_unread">
+					<select name="mark_updated_article_unread" id="mark_updated_article_unread">
+						<option value=""<?php echo $this->feed->attributes('mark_updated_article_unread') === null ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.by_default'); ?></option>
+						<option value="0"<?php echo $this->feed->attributes('mark_updated_article_unread') === false ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.no'); ?></option>
+						<option value="1"<?php echo $this->feed->attributes('mark_updated_article_unread') === true ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.yes'); ?></option>
+					</select>
+				</label>
+			</div>
+		</div>
+
+		<div class="form-group">
+			<label class="group-name" for="read_upon_reception"><?php echo _t('conf.reading.read.when'); ?></label>
+			<div class="group-controls">
+				<label class="checkbox" for="read_upon_reception">
+					<select name="read_upon_reception" id="read_upon_reception">
+						<option value=""<?php echo $this->feed->attributes('read_upon_reception') === null ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.by_default'); ?></option>
+						<option value="0"<?php echo $this->feed->attributes('read_upon_reception') === false ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.no'); ?></option>
+						<option value="1"<?php echo $this->feed->attributes('read_upon_reception') === true ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.yes'); ?></option>
+					</select>
+					<?php echo _t('conf.reading.read.upon_reception'); ?>
+				</label>
+			</div>
+		</div>
+
+		<div class="form-group">
+			<label class="group-name" for="timeout"><?php echo _t('sub.feed.timeout'); ?></label>
+			<div class="group-controls">
+				<input type="number" name="timeout" id="timeout" min="3" max="120" value="<?php echo $this->feed->attributes('timeout'); ?>" placeholder="<?php echo _t('gen.short.by_default'); ?>" />
+			</div>
+		</div>
+
+		<div class="form-group">
+			<label class="group-name" for="ssl_verify"><?php echo _t('sub.feed.ssl_verify'); ?></label>
+			<div class="group-controls">
+				<label class="checkbox" for="ssl_verify">
+					<select name="ssl_verify" id="ssl_verify">
+						<option value=""<?php echo $this->feed->attributes('ssl_verify') === null ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.by_default'); ?></option>
+						<option value="0"<?php echo $this->feed->attributes('ssl_verify') === false ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.no'); ?></option>
+						<option value="1"<?php echo $this->feed->attributes('ssl_verify') === true ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.yes'); ?></option>
+					</select>
+				</label>
+			</div>
+		</div>
+
 		<div class="form-group form-actions">
 			<div class="group-controls">
 				<button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button>

+ 2 - 2
lib/Minz/ModelPdo.php

@@ -69,7 +69,7 @@ class Minz_ModelPdo {
 				case 'sqlite':
 					$string = 'sqlite:' . join_path(DATA_PATH, 'users', $currentUser, 'db.sqlite');
 					$this->prefix = '';
-					$this->bd = new MinzPDOMSQLite($string, $db['user'], $db['password'], $driver_options);
+					$this->bd = new MinzPDOSQLite($string, $db['user'], $db['password'], $driver_options);
 					$this->bd->exec('PRAGMA foreign_keys = ON;');
 					break;
 				case 'pgsql':
@@ -160,7 +160,7 @@ class MinzPDOMySql extends MinzPDO {
 	}
 }
 
-class MinzPDOMSQLite extends MinzPDO {
+class MinzPDOSQLite extends MinzPDO {
 	public function lastInsertId($name = null) {
 		return parent::lastInsertId();	//We discard the name, only used by PostgreSQL
 	}

+ 13 - 0
lib/Minz/Request.php

@@ -39,6 +39,19 @@ class Minz_Request {
 			return $default;
 		}
 	}
+	public static function paramTernary($key) {
+		if (isset(self::$params[$key])) {
+			$p = self::$params[$key];
+			$tp = trim($p);
+			if ($p === null || $tp === '' || $tp === 'null') {
+				return null;
+			} elseif ($p == false || $tp == '0' || $tp === 'false' || $tp === 'no') {
+				return false;
+			}
+			return true;
+		}
+		return null;
+	}
 	public static function defaultControllerName() {
 		return self::$default_controller_name;
 	}

+ 12 - 3
lib/lib_rss.php

@@ -175,7 +175,7 @@ function html_only_entity_decode($text) {
 	return strtr($text, $htmlEntitiesOnly);
 }
 
-function customSimplePie() {
+function customSimplePie($attributes = array()) {
 	$system_conf = Minz_Configuration::get('system');
 	$limits = $system_conf->limits;
 	$simplePie = new SimplePie();
@@ -183,8 +183,17 @@ function customSimplePie() {
 	$simplePie->set_syslog($system_conf->simplepie_syslog_enabled);
 	$simplePie->set_cache_location(CACHE_PATH);
 	$simplePie->set_cache_duration($limits['cache_duration']);
-	$simplePie->set_timeout($limits['timeout']);
-	$simplePie->set_curl_options($system_conf->curl_options);
+
+	$feed_timeout = empty($attributes['timeout']) ? 0 : intval($attributes['timeout']);
+	$simplePie->set_timeout($feed_timeout > 0 ? $feed_timeout : $limits['timeout']);
+
+	$curl_options = $system_conf->curl_options;
+	if (isset($attributes['ssl_verify'])) {
+		$curl_options[CURLOPT_SSL_VERIFYHOST] = $attributes['ssl_verify'] ? 2 : 0;
+		$curl_options[CURLOPT_SSL_VERIFYPEER] = $attributes['ssl_verify'] ? true : false;
+	}
+	$simplePie->set_curl_options($curl_options);
+
 	$simplePie->strip_htmltags(array(
 		'base', 'blink', 'body', 'doctype', 'embed',
 		'font', 'form', 'frame', 'frameset', 'html',