From 7313f9f3a306d16fac78ab587e3055482398ceac Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 21 Feb 2014 22:32:29 +0100 Subject: Bug "mark all as read" when using DESC and pagination and no scroll https://github.com/marienfressinaud/FreshRSS/issues/431#issuecomment-35774488 --- app/Models/EntryDAO.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'app/Models/EntryDAO.php') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index aaf4dcf6a..f41d6c560 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -65,7 +65,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } } public function markReadEntries ($idMax = 0, $favorites = false) { - if ($idMax === 0) { + if ($idMax == 0) { $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' . 'SET e.is_read = 1, f.cache_nbUnreads=0 ' . 'WHERE e.is_read = 0 AND '; @@ -127,7 +127,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } } public function markReadCat ($id, $idMax = 0) { - if ($idMax === 0) { + if ($idMax == 0) { $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' . 'SET e.is_read = 1, f.cache_nbUnreads=0 ' . 'WHERE f.category = ? AND e.is_read = 0'; @@ -182,7 +182,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } } public function markReadFeed ($id, $idMax = 0) { - if ($idMax === 0) { + if ($idMax == 0) { $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' . 'SET e.is_read = 1, f.cache_nbUnreads=0 ' . 'WHERE f.id=? AND e.is_read = 0'; -- cgit v1.2.3 From 6dffb8706f096acdbcda367210d0b7ad41937174 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 27 Feb 2014 22:48:11 +0100 Subject: Alpha version of Google Reader compatible API https://github.com/marienfressinaud/FreshRSS/issues/13 Hardcoded passwords, no possibility to add/delete feeds or edit categories yet. --- app/Models/EntryDAO.php | 88 +++++++- app/Models/FeedDAO.php | 13 ++ lib/Minz/Configuration.php | 15 ++ p/api/greader.php | 552 +++++++++++++++++++++++++++++++++++++++++++++ p/api/index.html | 17 ++ 5 files changed, 674 insertions(+), 11 deletions(-) create mode 100644 p/api/greader.php create mode 100644 p/api/index.html (limited to 'app/Models/EntryDAO.php') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index f41d6c560..b25ae444b 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -64,15 +64,15 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return false; } } - public function markReadEntries ($idMax = 0, $favorites = false) { + public function markReadEntries ($idMax = 0, $onlyFavorites = false, $priorityMin = 0) { if ($idMax == 0) { $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' . 'SET e.is_read = 1, f.cache_nbUnreads=0 ' - . 'WHERE e.is_read = 0 AND '; - if ($favorites) { - $sql .= 'e.is_favorite = 1'; - } else { - $sql .= 'f.priority > 0'; + . 'WHERE e.is_read = 0'; + if ($onlyFavorites) { + $sql .= ' AND e.is_favorite = 1'; + } elseif ($priorityMin >= 0) { + $sql .= ' AND f.priority > ' . intval($priorityMin); } $stm = $this->bd->prepare ($sql); if ($stm && $stm->execute ()) { @@ -87,11 +87,11 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' . 'SET e.is_read = 1 ' - . 'WHERE e.is_read = 0 AND e.id <= ? AND '; - if ($favorites) { - $sql .= 'e.is_favorite = 1'; - } else { - $sql .= 'f.priority > 0'; + . 'WHERE e.is_read = 0 AND e.id <= ?'; + if ($onlyFavorites) { + $sql .= ' AND e.is_favorite = 1'; + } elseif ($priorityMin >= 0) { + $sql .= ' AND f.priority > ' . intval($priorityMin); } $values = array ($idMax); $stm = $this->bd->prepare ($sql); @@ -126,6 +126,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return $affected; } } + public function markReadCat ($id, $idMax = 0) { if ($idMax == 0) { $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' @@ -181,6 +182,68 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return $affected; } } + + public function markReadCatName($name, $idMax = 0) { + if ($idMax == 0) { + $sql = 'UPDATE `' . $this->prefix . 'entry` e ' + . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' + . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category ' + . 'SET e.is_read = 1, f.cache_nbUnreads=0 ' + . 'WHERE c.name = ?'; + $values = array($name); + $stm = $this->bd->prepare($sql); + if ($stm && $stm->execute($values)) { + return $stm->rowCount(); + } else { + $info = $stm->errorInfo(); + Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + return false; + } + } else { + $this->bd->beginTransaction(); + + $sql = 'UPDATE `' . $this->prefix . 'entry` e ' + . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' + . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category ' + . 'SET e.is_read = 1 ' + . 'WHERE c.name = ? AND e.id <= ?'; + $values = array($name, $idMax); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute($values))) { + $info = $stm->errorInfo(); + Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack(); + return false; + } + $affected = $stm->rowCount(); + + if ($affected > 0) { + $sql = 'UPDATE `' . $this->prefix . 'feed` f ' + . 'LEFT OUTER JOIN (' + . 'SELECT e.id_feed, ' + . 'COUNT(*) AS nbUnreads ' + . 'FROM `' . $this->prefix . 'entry` e ' + . 'WHERE e.is_read = 0 ' + . 'GROUP BY e.id_feed' + . ') x ON x.id_feed=f.id ' + . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category ' + . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0) ' + . 'WHERE c.name = ?'; + $values = array($name); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute($values))) { + $info = $stm->errorInfo(); + Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack(); + return false; + } + } + + $this->bd->commit(); + return $affected; + } + } + public function markReadFeed ($id, $idMax = 0) { if ($idMax == 0) { $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' @@ -281,6 +344,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $where .= 'e1.id_feed = ? '; $values[] = intval($id); break; + case 'A': + $where .= '1 '; + break; default: throw new FreshRSS_EntriesGetter_Exception ('Bad type in Entry->listByType: [' . $type . ']!'); } diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 7ebe68d2b..79d8cff90 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -198,6 +198,19 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); } + public function listCategoryNames() { + $sql = 'SELECT f.id, c.name as c_name FROM `' . $this->prefix . 'feed` f ' + . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category'; + $stm = $this->bd->prepare ($sql); + $stm->execute (); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + $feedCategoryNames = array(); + foreach ($res as $line) { + $feedCategoryNames[$line['id']] = $line['c_name']; + } + return $feedCategoryNames; + } + public function listFeedsOrderUpdate ($cacheDuration = 1500) { $sql = 'SELECT id, name, url, lastUpdate, pathEntries, httpAuth, keep_history ' . 'FROM `' . $this->prefix . 'feed` ' diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index b3de9e39e..ff71d747c 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -54,6 +54,7 @@ class Minz_Configuration { private static $allow_anonymous = false; private static $allow_anonymous_refresh = false; private static $auth_type = 'none'; + private static $api_enabled = false; private static $db = array ( 'type' => 'mysql', @@ -131,6 +132,9 @@ class Minz_Configuration { public static function canLogIn() { return self::$auth_type === 'form' || self::$auth_type === 'persona'; } + public static function apiEnabled() { + return self::$api_enabled; + } public static function _allowAnonymous($allow = false) { self::$allow_anonymous = ((bool)$allow) && self::canLogIn(); @@ -151,6 +155,10 @@ class Minz_Configuration { self::_allowAnonymous(self::$allow_anonymous); } + public static function _enableApi($value = false) { + self::$api_enabled = (bool)$value; + } + /** * Initialise les variables de configuration * @exception Minz_FileNotExistException si le CONF_PATH_NAME n'existe pas @@ -179,6 +187,7 @@ class Minz_Configuration { 'allow_anonymous' => self::$allow_anonymous, 'allow_anonymous_refresh' => self::$allow_anonymous_refresh, 'auth_type' => self::$auth_type, + 'api_enabled' => self::$api_enabled, ), 'db' => self::$db, ); @@ -295,6 +304,12 @@ class Minz_Configuration { ($general['allow_anonymous_refresh'] !== 'no') ); } + if (isset ($general['api_enabled'])) { + self::$api_enabled = ( + ((bool)($general['api_enabled'])) && + ($general['api_enabled'] !== 'no') + ); + } // Base de données if (isset ($ini_array['db'])) { diff --git a/p/api/greader.php b/p/api/greader.php new file mode 100644 index 000000000..2969f5935 --- /dev/null +++ b/p/api/greader.php @@ -0,0 +1,552 @@ + date('c'), 'headers' => $ALL_HEADERS, '_SERVER' => $_SERVER, '_GET' => $_GET, '_POST' => $_POST, '_COOKIE' => $_COOKIE, 'INPUT' => $ORIGINAL_INPUT); + +if (PHP_INT_SIZE < 8) { //32-bit + function dec2hex($dec) { + return str_pad(gmp_strval(gmp_init($dec, 10), 16), 16, '0', STR_PAD_LEFT); + } + function hex2dec($hex) { + return gmp_strval(gmp_init($hex, 16), 10); + } +} else { //64-bit + function dec2hex($dec) { //http://code.google.com/p/google-reader-api/wiki/ItemId + return str_pad(dechex($dec), 16, '0', STR_PAD_LEFT); + } + function hex2dec($hex) { + return hexdec($hex); + } +} + +function headerVariable($headerName, $varName) { + global $ALL_HEADERS; + if (empty($ALL_HEADERS[$headerName])) { + return null; + } + parse_str($ALL_HEADERS[$headerName], $pairs); + //logMe('headerVariable(' . $headerName . ') => ' . print_r($pairs, true)); + return isset($pairs[$varName]) ? $pairs[$varName] : null; +} + +function multiplePosts($name) { //https://bugs.php.net/bug.php?id=51633 + global $ORIGINAL_INPUT; + $inputs = explode('&', $ORIGINAL_INPUT); + $result = array(); + $prefix = $name . '='; + $prefixLength = strlen($prefix); + foreach ($inputs as $input) { + if (strpos($input, $prefix) === 0) { + $result[] = urldecode(substr($input, $prefixLength)); + } + } + return $result; +} + +class MyPDO extends Minz_ModelPdo { + function prepare($sql) { + return $this->bd->prepare(str_replace('%_', $this->prefix, $sql)); + } +} + +function logMe($text) { + file_put_contents(LOG_PATH . '/api.log', $text, FILE_APPEND); +} + +function badRequest() { + logMe("badRequest()\n"); + header('HTTP/1.1 400 Bad Request'); + header('Content-Type: text/plain; charset=UTF-8'); + die('Bad Request!'); +} + +function unauthorized() { + logMe("unauthorized()\n"); + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/plain; charset=UTF-8'); + header('Google-Bad-Token: true'); + die('Unauthorized!'); +} + +function notImplemented() { + logMe("notImplemented()\n"); + header('HTTP/1.1 501 Not Implemented'); + header('Content-Type: text/plain; charset=UTF-8'); + die('Not Implemented!'); +} + +function serviceUnavailable() { + logMe("serviceUnavailable()\n"); + header('HTTP/1.1 503 Service Unavailable'); + header('Content-Type: text/plain; charset=UTF-8'); + die('Service Unavailable!'); +} + +function checkCompatibility() { + logMe("checkCompatibility()\n"); + header('Content-Type: text/plain; charset=UTF-8'); + $ok = true; + $ok &= function_exists('getallheaders'); + echo $ok ? 'PASS' : 'FAIL'; + exit(); +} + +function authorizationToUser() { + $auth = headerVariable('Authorization', 'GoogleLogin_auth'); //Input is 'GoogleLogin auth', but PHP replaces spaces by '_' http://php.net/language.variables.external + //logMe('authorizationToUser, auth => ' . $auth . "\n"); + list($userName) = explode('/', $auth); + return $userName; +} + +function clientLogin($email, $pass) { //http://web.archive.org/web/20130604091042/http://undoc.in/clientLogin.html + logMe('clientLogin(' . $email . ")\n"); + if ($pass !== TEMP_PASSWORD) { + unauthorized(); + } + header('Content-Type: text/plain; charset=UTF-8'); + $auth = $email . '/' . '0123456789'; + echo 'SID=', $auth, "\n", + 'Auth=', $auth, "\n"; + exit(); +} + +function token($user) { +//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 + logMe('token('. $user . ")\n"); + $token = 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ01234'; //Must have 57 characters... + echo $token, "\n"; + exit(); +} + +function checkToken($user, $token) { +//http://code.google.com/p/google-reader-api/wiki/ActionToken + logMe('checkToken(' . $token . ")\n"); + if ($token === 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ01234') { + return true; + } + unauthorized(); +} + +function tagList() { + logMe("tagList()\n"); + header('Content-Type: application/json; charset=UTF-8'); + + $pdo = new MyPDO(); + $stm = $pdo->prepare('SELECT c.name FROM `%_category` c'); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); + + $tags = array( + array('id' => 'user/-/state/com.google/starred'), + //array('id' => 'user/-/state/com.google/broadcast', 'sortid' => '2'), + ); + + foreach ($res as $cName) { + $tags[] = array( + 'id' => 'user/-/label/' . $cName, + //'sortid' => $cName, + ); + } + + echo json_encode(array('tags' => $tags)), "\n"; + exit(); +} + +function subscriptionList() { + logMe("subscriptionList()\n"); + header('Content-Type: application/json; charset=UTF-8'); + + $pdo = new MyPDO(); + $stm = $pdo->prepare('SELECT f.id, f.name, f.url, f.website, c.id as c_id, c.name as c_name FROM `%_feed` f + INNER JOIN `%_category` c ON c.id = f.category'); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + $subscriptions = array(); + + foreach ($res as $line) { + $subscriptions[] = array( + 'id' => 'feed/' . $line['id'], + 'title' => $line['name'], + 'categories' => array( + array( + 'id' => 'user/-/label/' . $line['c_name'], + 'label' => $line['c_name'], + ), + ), + //'sortid' => $line['name'], + //'firstitemmsec' => 0, + 'url' => $line['url'], + 'htmlUrl' => $line['website'], + //'iconUrl' => '', + ); + } + + echo json_encode(array('subscriptions' => $subscriptions)), "\n"; + exit(); +} + +function unreadCount() { + logMe("unreadCount()\n"); + header('Content-Type: application/json; charset=UTF-8'); + + $pdo = new MyPDO(); + $stm = $pdo->prepare('SELECT f.id, f.lastUpdate, f.cache_nbUnreads FROM `%_feed` f'); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + $unreadcounts = array(); + $totalUnreads = 0; + $totalLastUpdate = 0; + foreach ($res as $line) { + $nbUnreads = $line['cache_nbUnreads']; + $totalUnreads += $nbUnreads; + $lastUpdate = $line['lastUpdate']; + if ($totalLastUpdate < $lastUpdate) { + $totalLastUpdate = $lastUpdate; + } + $unreadcounts[] = array( + 'id' => 'feed/' . $line['id'], + 'count' => $nbUnreads, + 'newestItemTimestampUsec' => $lastUpdate . '000000', + ); + } + + $unreadcounts[] = array( + 'id' => 'user/-/state/com.google/reading-list', + 'count' => $totalUnreads, + 'newestItemTimestampUsec' => $totalLastUpdate . '000000', + ); + + echo json_encode(array( + 'max' => $totalUnreads, + 'unreadcounts' => $unreadcounts, + )), "\n"; + exit(); +} + +function streamContents($path, $include_target, $start_time, $count, $order, $exclude_target) +{//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(' . $include_target . ")\n"); + header('Content-Type: application/json; charset=UTF-8'); + + $feedDAO = new FreshRSS_FeedDAO(); + $feedCategoryNames = $feedDAO->listCategoryNames(); + + switch ($path) { + case 'reading-list': + $type = 'A'; + break; + case 'starred': + $type = 's'; + break; + case 'label': + $type = 'c'; + break; + default: + $type = 'A'; + break; + } + + switch ($exclude_target) { + case 'user/-/state/com.google/read': + $state = 'not_read'; + break; + default: + $state = 'all'; + break; + } + + $entryDAO = new FreshRSS_EntryDAO(); + $entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, '', '', $start_time); + + $items = array(); + foreach ($entries as $entry) { + $f_id = $entry->feed(); + $c_name = isset($feedCategoryNames[$f_id]) ? $feedCategoryNames[$f_id] : '_'; + $item = array( + 'id' => /*'tag:google.com,2005:reader/item/' .*/ dec2hex($entry->id()), //64-bit hexa http://code.google.com/p/google-reader-api/wiki/ItemId + 'crawlTimeMsec' => substr($entry->id(), 0, -3), + 'published' => $entry->date(true), + 'title' => $entry->title(), + 'summary' => array('content' => $entry->content()), + 'alternate' => array( + array('href' => $entry->link()), + ), + 'categories' => array( + 'user/-/state/com.google/reading-list', + 'user/-/label/' . $c_name, + ), + 'origin' => array( + 'streamId' => 'feed/' . $f_id, + //'title' => $line['f_name'], + //'htmlUrl' => $line['f_website'], + ), + ); + if ($entry->author() != '') { + $item['author'] = $entry->author(); + } + if ($entry->isRead()) { + $item['categories'][] = 'user/-/state/com.google/read'; + } + if ($entry->isFavorite()) { + $item['categories'][] = 'user/-/state/com.google/starred'; + } + $items[] = $item; + } + + echo json_encode(array( + 'id' => 'user/-/state/com.google/reading-list', + 'updated' => time(), + 'items' => $items, + )), "\n"; + exit(); +} + +function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target) +{//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 . ")\n"); + + $type = 'A'; + $id = ''; + if ($streamId === 'user/-/state/com.google/reading-list') { + $type = 'A'; + } elseif ('user/-/state/com.google/starred') { + $type = 's'; + } elseif (strpos($streamId, 'feed/') === 0) { + $type = 'f'; + $id = basename($streamId); + } elseif (strpos($streamId, 'user/-/label/') === 0) { + $type = 'C'; + $c_name = basename($streamId); + notImplemented(); //TODO + } + + switch ($exclude_target) { + case 'user/-/state/com.google/read': + $state = 'not_read'; + break; + default: + $state = 'all'; + break; + } + + $entryDAO = new FreshRSS_EntryDAO(); + $entries = $entryDAO->listWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, '', '', $start_time); + + $itemRefs = array(); + foreach ($entries as $entry) { + $f_id = $entry->feed(); + $itemRefs[] = array( + 'id' => $entry->id(), //64-bit decimal + //'timestampUsec' => $entry->dateAdded(true), + /*'directStreamIds' => array( + 'feed/' . $entry->feed() + ),*/ + ); + } + + echo json_encode(array( + 'itemRefs' => $itemRefs, + )), "\n"; + exit(); +} + +function editTag($e_ids, $a, $r) { + logMe("editTag()\n"); + $entryDAO = new FreshRSS_EntryDAO(); + + foreach ($e_ids as $e_id) { //TODO: User WHERE...IN + $e_id = hex2dec(basename($e_id)); //Strip prefix 'tag:google.com,2005:reader/item/' + switch ($a) { + case 'user/-/state/com.google/read': + $entryDAO->markRead($e_id, true); + break; + case 'user/-/state/com.google/starred': + $entryDAO->markFavorite($e_id, true); + break; + /*case 'user/-/state/com.google/tracking-kept-unread': + break; + case 'user/-/state/com.google/like': + break; + case 'user/-/state/com.google/broadcast': + break;*/ + } + switch ($r) { + case 'user/-/state/com.google/read': + $entryDAO->markRead($e_id, false); + break; + case 'user/-/state/com.google/starred': + $entryDAO->markFavorite($e_id, false); + break; + } + } + + echo 'OK'; + exit(); +} + +function markAllAsRead($streamId, $olderThanId) { + logMe('markAllAsRead(' . $streamId . ")\n"); + $entryDAO = new FreshRSS_EntryDAO(); + if (strpos($streamId, 'feed/') === 0) { + $f_id = basename($streamId); + $entryDAO->markReadFeed($f_id, $olderThanId); + } elseif (strpos($streamId, 'user/-/label/') === 0) { + $c_name = basename($streamId); + $entryDAO->markReadCatName($c_name, $olderThanId); + } elseif ($streamId === 'user/-/state/com.google/reading-list') { + $entryDAO->markReadEntries($olderThanId, false, -1); + } + + echo 'OK'; + exit(); +} + +logMe('----------------------------------------------------------------'."\n"); +logMe(print_r($debugInfo, true)); + +$pathInfo = empty($_SERVER['PATH_INFO']) ? '/Error' : $_SERVER['PATH_INFO']; +$pathInfos = explode('/', $pathInfo); + +logMe('pathInfos => ' . print_r($pathInfos, true)); + +Minz_Configuration::init(); + +if (!Minz_Configuration::apiEnabled()) { + serviceUnavailable(); +} + +Minz_Session::init('FreshRSS'); + +$user = authorizationToUser(); +$conf = null; + +logMe('User => ' . $user . "\n"); + +if ($user != null) { + try { + $conf = new FreshRSS_Configuration($user); + } catch (Exception $e) { + logMe($e->getMessage()); + $user = null; + badRequest(); + } +} + +Minz_Session::_param('currentUser', $user); + +if (count($pathInfos)<3) badRequest(); +elseif ($pathInfos[1] === 'accounts') { + if (($pathInfos[2] === 'ClientLogin') && isset($_REQUEST['Email']) && isset($_REQUEST['Passwd'])) + clientLogin($_REQUEST['Email'], $_REQUEST['Passwd']); +} +elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfos[3]) && $pathInfos[3] === '0' && isset($pathInfos[4])) { + if ($user == null) { + unauthorized(); + } + $timestamp = isset($_GET['ck']) ? intval($_GET['ck']) : 0; //ck=[unix timestamp] : Use the current Unix time here, helps Google with caching. + switch ($pathInfos[4]) { + case 'stream': + $exclude_target = isset($_GET['xt']) ? $_GET['xt'] : ''; //xt=[exclude target] : Used to exclude certain items from the feed. For example, using xt=user/-/state/com.google/read will exclude items that the current user has marked as read, or xt=feed/[feedurl] will exclude items from a particular feed (obviously not useful in this request, but xt appears in other listing requests). + $count = isset($_GET['n']) ? intval($_GET['n']) : 20; //n=[integer] : The maximum number of results to return. + $order = isset($_GET['r']) ? $_GET['r'] : 'd'; //r=[d|n|o] : Sort order of item results. d or n gives items in descending date order, o in ascending order. + $start_time = isset($_GET['ot']) ? intval($_GET['ot']) : 0; //ot=[unix timestamp] : The time from which you want to retrieve items. Only items that have been crawled by Google Reader after this time will be returned. + if (isset($pathInfos[5]) && $pathInfos[5] === 'contents' && isset($pathInfos[6]) && isset($pathInfos[7])) { + if ($pathInfos[6] === 'feed') { + $include_target = $pathInfos[7]; + StreamContents($pathInfos[6], $include_target, $start_time, $count, $order, $exclude_target); + } elseif ($pathInfos[6] === 'user' && isset($pathInfos[8]) && isset($pathInfos[9])) { + if ($pathInfos[8] === 'state') { + if ($pathInfos[9] === 'com.google' && isset($pathInfos[10])) { + if ($pathInfos[10] === 'reading-list' || $pathInfos[10] === 'starred') { + $include_target = ''; + streamContents($pathInfos[10], $include_target, $start_time, $count, $order, $exclude_target); + } + } + } elseif ($pathInfos[8] === 'label') { + $include_target = $pathInfos[9]; + streamContents($pathInfos[8], $include_target, $start_time, $count, $order, $exclude_target); + } + } + } elseif ($pathInfos[5] === 'items') { + if ($pathInfos[6] === 'ids' && isset($_GET['s'])) { + $streamId = $_GET['s']; //StreamId for which to fetch the item IDs. The parameter may be repeated to fetch the item IDs from multiple streams at once (more efficient from a backend perspective than multiple requests). + streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target); + } + } + break; + case 'tag': + if (isset($pathInfos[5]) && $pathInfos[5] === 'list') { + $output = isset($_GET['output']) ? $_GET['output'] : ''; + if ($output !== 'json') notImplemented(); + tagList($_GET['output']); + } + break; + case 'subscription': + if (isset($pathInfos[5]) && $pathInfos[5] === 'list') { + $output = isset($_GET['output']) ? $_GET['output'] : ''; + if ($output !== 'json') notImplemented(); + subscriptionList($_GET['output']); + } + break; + case 'unread-count': + $output = isset($_GET['output']) ? $_GET['output'] : ''; + if ($output !== 'json') notImplemented(); + $all = isset($_GET['all']) ? $_GET['all'] : ''; + unreadCount($all); + break; + case 'edit-tag': //http://blog.martindoms.com/2010/01/20/using-the-google-reader-api-part-3/ + $token = isset($_POST['T']) ? trim($_POST['T']) : ''; + checkToken($user, $token); + $a = isset($_POST['a']) ? $_POST['a'] : ''; //Add: user/-/state/com.google/read user/-/state/com.google/starred + $r = isset($_POST['r']) ? $_POST['r'] : ''; //Remove: user/-/state/com.google/read user/-/state/com.google/starred + $e_ids = multiplePosts('i'); //item IDs + editTag($e_ids, $a, $r); + break; + case 'mark-all-as-read': + $token = isset($_POST['T']) ? trim($_POST['T']) : ''; + checkToken($user, $token); + $streamId = $_POST['s']; //StreamId + $ts = isset($_POST['ts']) ? $_POST['ts'] : '0'; //Older than timestamp in nanoseconds + if (!ctype_digit($ts)) { + $ts = '0'; + } + markAllAsRead($streamId, $ts); + break; + case 'token': + Token($user); + break; + } +} elseif ($pathInfos[1] === 'check' && $pathInfos[2] === 'compatibility') { + checkCompatibility(); +} + +badRequest(); diff --git a/p/api/index.html b/p/api/index.html new file mode 100644 index 000000000..123d0d001 --- /dev/null +++ b/p/api/index.html @@ -0,0 +1,17 @@ + + + + +FreshRSS API + + + + + +

FreshRSS API

+ + + + -- cgit v1.2.3 From f44683b5671b323ba96f0c4cd47ba9458e934679 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 28 Feb 2014 20:22:50 +0100 Subject: API streamContents for categories and feeds https://github.com/marienfressinaud/FreshRSS/issues/13 --- app/Controllers/configureController.php | 2 +- app/Models/CategoryDAO.php | 6 +++--- app/Models/EntryDAO.php | 4 ++-- app/Models/FeedDAO.php | 6 +++--- lib/lib_opml.php | 2 +- p/api/greader.php | 22 ++++++++++++++++------ 6 files changed, 26 insertions(+), 16 deletions(-) (limited to 'app/Models/EntryDAO.php') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index f831f25d0..41a7920f0 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -44,7 +44,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { 'name' => $cat->name (), ); - if ($catDAO->searchByName ($newCat) == false) { + if ($catDAO->searchByName ($newCat) == null) { $catDAO->addCategory ($values); } } diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 5355228a5..f3c02e3e4 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -64,7 +64,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { if (isset ($cat[0])) { return $cat[0]; } else { - return false; + return null; } } public function searchByName ($name) { @@ -80,7 +80,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { if (isset ($cat[0])) { return $cat[0]; } else { - return false; + return null; } } @@ -120,7 +120,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { public function checkDefault () { $def_cat = $this->searchById (1); - if ($def_cat === false) { + if ($def_cat == null) { $cat = new FreshRSS_Category (Minz_Translate::t ('default_category')); $cat->_id (1); diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index b25ae444b..3fc7a05ef 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -307,7 +307,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); $entries = self::daoToEntry ($res); - return isset ($entries[0]) ? $entries[0] : false; + return isset ($entries[0]) ? $entries[0] : null; } public function searchById ($id) { @@ -320,7 +320,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); $entries = self::daoToEntry ($res); - return isset ($entries[0]) ? $entries[0] : false; + return isset ($entries[0]) ? $entries[0] : null; } public function listWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 79d8cff90..fb4a847a0 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -170,7 +170,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if (isset ($feed[$id])) { return $feed[$id]; } else { - return false; + return null; } } public function searchByUrl ($url) { @@ -186,7 +186,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if (isset ($feed)) { return $feed; } else { - return false; + return null; } } @@ -198,7 +198,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); } - public function listCategoryNames() { + public function arrayCategoryNames() { $sql = 'SELECT f.id, c.name as c_name FROM `' . $this->prefix . 'feed` f ' . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category'; $stm = $this->bd->prepare ($sql); diff --git a/lib/lib_opml.php b/lib/lib_opml.php index 9feb12ae0..05e54d85e 100644 --- a/lib/lib_opml.php +++ b/lib/lib_opml.php @@ -58,7 +58,7 @@ function opml_import ($xml) { $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); $catDAO = new FreshRSS_CategoryDAO (); $cat = $catDAO->searchByName ($title); - if ($cat === false) { + if ($cat == null) { $cat = new FreshRSS_Category ($title); $values = array ( 'name' => $cat->name () diff --git a/p/api/greader.php b/p/api/greader.php index 291bcdf1f..e99e1c0c8 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -266,7 +266,7 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex header('Content-Type: application/json; charset=UTF-8'); $feedDAO = new FreshRSS_FeedDAO(); - $feedCategoryNames = $feedDAO->listCategoryNames(); + $arrayFeedCategoryNames = $feedDAO->arrayCategoryNames(); switch ($path) { case 'reading-list': @@ -275,8 +275,14 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex case 'starred': $type = 's'; break; + case 'feed': + $type = 'f'; + break; case 'label': $type = 'c'; + $categoryDAO = new FreshRSS_CategoryDAO(); + $cat = $categoryDAO->searchByName($include_target); + $include_target = $cat == null ? -1 : $cat->id(); break; default: $type = 'A'; @@ -298,7 +304,7 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex $items = array(); foreach ($entries as $entry) { $f_id = $entry->feed(); - $c_name = isset($feedCategoryNames[$f_id]) ? $feedCategoryNames[$f_id] : '_'; + $c_name = isset($arrayFeedCategoryNames[$f_id]) ? $arrayFeedCategoryNames[$f_id] : '_'; $item = array( 'id' => /*'tag:google.com,2005:reader/item/' .*/ dec2hex($entry->id()), //64-bit hexa http://code.google.com/p/google-reader-api/wiki/ItemId 'crawlTimeMsec' => substr($entry->id(), 0, -3), @@ -352,9 +358,11 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude $type = 'f'; $id = basename($streamId); } elseif (strpos($streamId, 'user/-/label/') === 0) { - $type = 'C'; + $type = 'c'; $c_name = basename($streamId); - notImplemented(); //TODO + $categoryDAO = new FreshRSS_CategoryDAO(); + $cat = $categoryDAO->searchByName($c_name); + $id = $cat == null ? -1 : $cat->id(); } switch ($exclude_target) { @@ -441,7 +449,7 @@ function markAllAsRead($streamId, $olderThanId) { logMe('----------------------------------------------------------------'."\n"); logMe(print_r($debugInfo, true)); -$pathInfo = empty($_SERVER['PATH_INFO']) ? '/Error' : $_SERVER['PATH_INFO']; +$pathInfo = empty($_SERVER['PATH_INFO']) ? '/Error' : urldecode($_SERVER['PATH_INFO']); $pathInfos = explode('/', $pathInfo); logMe('pathInfos => ' . print_r($pathInfos, true)); @@ -471,7 +479,9 @@ if ($user != null) { Minz_Session::_param('currentUser', $user); -if (count($pathInfos)<3) badRequest(); +if (count($pathInfos) < 3) { + badRequest(); +} elseif ($pathInfos[1] === 'accounts') { if (($pathInfos[2] === 'ClientLogin') && isset($_REQUEST['Email']) && isset($_REQUEST['Passwd'])) clientLogin($_REQUEST['Email'], $_REQUEST['Passwd']); -- cgit v1.2.3 From 71f7ce1be5833b54b0f4e1f37e6557425c364725 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 1 Mar 2014 15:47:15 +0100 Subject: API: SQL optimisation https://github.com/marienfressinaud/FreshRSS/issues/13 --- app/Controllers/entryController.php | 4 +++- app/Models/EntryDAO.php | 43 ++++++++++++++++++++++++++++--------- app/Models/FeedDAO.php | 6 +++--- p/api/greader.php | 11 +++------- 4 files changed, 42 insertions(+), 22 deletions(-) (limited to 'app/Models/EntryDAO.php') diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index 1756c91e5..ca7122a7c 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -137,11 +137,13 @@ class FreshRSS_entry_Controller extends Minz_ActionController { if ($nb > 0) { $nbTotal += $nb; Minz_Log::record($nb . ' old entries cleaned in feed [' . $feed->url() . ']', Minz_Log::DEBUG); - $feedDAO->updateLastUpdate($feed->id()); + //$feedDAO->updateLastUpdate($feed->id()); } } } + $feedDAO->updateCachedValues(); + invalidateHttpCache(); $notif = array( diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 3fc7a05ef..e90b9a7fe 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -35,11 +35,15 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } } - public function markFavorite ($id, $is_favorite = true) { + public function markFavorite($ids, $is_favorite = true) { + if (!is_array($ids)) { + $ids = array($ids); + } $sql = 'UPDATE `' . $this->prefix . 'entry` e ' . 'SET e.is_favorite = ? ' - . 'WHERE e.id=?'; - $values = array ($is_favorite ? 1 : 0, $id); + . 'WHERE e.id IN (' . str_repeat('?,', count($ids) - 1). '?)'; + $values = array ($is_favorite ? 1 : 0); + $values = array_merge($values, $ids); $stm = $this->bd->prepare ($sql); if ($stm && $stm->execute ($values)) { return $stm->rowCount(); @@ -49,6 +53,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return false; } } + public function markRead ($id, $is_read = true) { $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' . 'SET e.is_read = ?,' @@ -64,6 +69,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return false; } } + public function markReadEntries ($idMax = 0, $onlyFavorites = false, $priorityMin = 0) { if ($idMax == 0) { $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' @@ -323,7 +329,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return isset ($entries[0]) ? $entries[0] : null; } - public function listWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { + private function sqlListWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { $where = ''; $joinFeed = false; $values = array(); @@ -432,14 +438,22 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } } + return array($values, + 'SELECT e1.id FROM `' . $this->prefix . 'entry` e1 ' + . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e1.id_feed = f.id ' : '') + . 'WHERE ' . $where + . $search + . 'ORDER BY e1.id ' . $order + . ($limit > 0 ? ' LIMIT ' . $limit : '')); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ + } + + public function listWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { + list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $keepHistoryDefault); + $sql = 'SELECT e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags ' . 'FROM `' . $this->prefix . 'entry` e ' - . 'INNER JOIN (SELECT e1.id FROM `' . $this->prefix . 'entry` e1 ' - . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e1.id_feed = f.id ' : '') - . 'WHERE ' . $where - . $search - . 'ORDER BY e1.id ' . $order - . ($limit > 0 ? ' LIMIT ' . $limit : '') //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ + . 'INNER JOIN (' + . $sql . ') e2 ON e2.id = e.id ' . 'ORDER BY e.id ' . $order; @@ -449,6 +463,15 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return self::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC)); } + public function listIdsWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { + list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $keepHistoryDefault); + + $stm = $this->bd->prepare($sql); + $stm->execute($values); + + return $stm->fetchAll(PDO::FETCH_COLUMN, 0); + } + public function listLastGuidsByFeed($id, $n) { $sql = 'SELECT guid FROM `' . $this->prefix . 'entry` WHERE id_feed=? ORDER BY id DESC LIMIT ' . intval($n); $stm = $this->bd->prepare ($sql); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index fb4a847a0..f9e1ad9e8 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -242,6 +242,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $res[0]['count']; } + public function countNotRead ($id) { $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND is_read=0'; $stm = $this->bd->prepare ($sql); @@ -251,6 +252,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $res[0]['count']; } + public function updateCachedValues () { //For one single feed, call updateLastUpdate($id) $sql = 'UPDATE `' . $this->prefix . 'feed` f ' . 'INNER JOIN (' @@ -263,9 +265,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { . 'SET f.cache_nbEntries=x.nbEntries, f.cache_nbUnreads=x.nbUnreads'; $stm = $this->bd->prepare ($sql); - $values = array ($feed_id); - - if ($stm && $stm->execute ($values)) { + if ($stm && $stm->execute()) { return $stm->rowCount(); } else { $info = $stm->errorInfo(); diff --git a/p/api/greader.php b/p/api/greader.php index 035a031dd..d1846fdaf 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -403,17 +403,12 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude } $entryDAO = new FreshRSS_EntryDAO(); - $entries = $entryDAO->listWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, '', '', $start_time); + $ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, '', '', $start_time); $itemRefs = array(); - foreach ($entries as $entry) { - $f_id = $entry->feed(); + foreach ($ids as $id) { $itemRefs[] = array( - 'id' => $entry->id(), //64-bit decimal - //'timestampUsec' => $entry->dateAdded(true), - /*'directStreamIds' => array( - 'feed/' . $entry->feed() - ),*/ + 'id' => $id, //64-bit decimal ); } -- cgit v1.2.3 From 00774f5a0bf2eacbb1825ccbf07e3fbc7b114b4d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 2 Mar 2014 11:54:52 +0100 Subject: API : SQL optimisation WHERE ... IN, and better compatibility EasyRSS https://github.com/marienfressinaud/FreshRSS/issues/13 --- app/Models/EntryDAO.php | 73 +++++++++++++++++++++++++++++++------- app/Models/FeedDAO.php | 9 +++-- p/api/greader.php | 94 ++++++++++++++++++++++++++++--------------------- 3 files changed, 120 insertions(+), 56 deletions(-) (limited to 'app/Models/EntryDAO.php') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index e90b9a7fe..cc52ea120 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -54,19 +54,66 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } } - public function markRead ($id, $is_read = true) { - $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' - . 'SET e.is_read = ?,' - . 'f.cache_nbUnreads=f.cache_nbUnreads' . ($is_read ? '-' : '+') . '1 ' - . 'WHERE e.id=?'; - $values = array ($is_read ? 1 : 0, $id); - $stm = $this->bd->prepare ($sql); - if ($stm && $stm->execute ($values)) { - return $stm->rowCount(); + public function markRead($ids, $is_read = true) { + if (is_array($ids)) { + if (count($ids) < 6) { //Speed heuristics + $affected = 0; + foreach ($ids as $id) { + $affected += $this->markRead($id, $is_read); + } + return $affected; + } + + $this->bd->beginTransaction(); + $sql = 'UPDATE `' . $this->prefix . 'entry` e ' + . 'SET e.is_read = ? ' + . 'WHERE e.id IN (' . str_repeat('?,', count($ids) - 1). '?)'; + $values = array($is_read ? 1 : 0); + $values = array_merge($values, $ids); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute($values))) { + $info = $stm->errorInfo(); + Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack(); + return false; + } + $affected = $stm->rowCount(); + + if ($affected > 0) { + $sql = 'UPDATE `' . $this->prefix . 'feed` f ' + . 'INNER JOIN (' + . 'SELECT e.id_feed, ' + . 'COUNT(CASE WHEN e.is_read = 0 THEN 1 END) AS nbUnreads, ' + . 'COUNT(e.id) AS nbEntries ' + . 'FROM `' . $this->prefix . 'entry` e ' + . 'GROUP BY e.id_feed' + . ') x ON x.id_feed=f.id ' + . 'SET f.cache_nbEntries=x.nbEntries, f.cache_nbUnreads=x.nbUnreads'; + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute())) { + $info = $stm->errorInfo(); + Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack(); + return false; + } + } + + $this->bd->commit(); + return $affected; } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); - return false; + $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' + . 'SET e.is_read = ?,' + . 'f.cache_nbUnreads=f.cache_nbUnreads' . ($is_read ? '-' : '+') . '1 ' + . 'WHERE e.id=?'; + $values = array($is_read ? 1 : 0, $ids); + $stm = $this->bd->prepare($sql); + if ($stm && $stm->execute($values)) { + return $stm->rowCount(); + } else { + $info = $stm->errorInfo(); + Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + return false; + } } } @@ -463,7 +510,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return self::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC)); } - public function listIdsWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { + public function listIdsWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { //For API list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $keepHistoryDefault); $stm = $this->bd->prepare($sql); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index f9e1ad9e8..ca25c3aeb 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -198,15 +198,18 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); } - public function arrayCategoryNames() { - $sql = 'SELECT f.id, c.name as c_name FROM `' . $this->prefix . 'feed` f ' + public function arrayFeedCategoryNames() { //For API + $sql = 'SELECT f.id, f.name, c.name as c_name FROM `' . $this->prefix . 'feed` f ' . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category'; $stm = $this->bd->prepare ($sql); $stm->execute (); $res = $stm->fetchAll(PDO::FETCH_ASSOC); $feedCategoryNames = array(); foreach ($res as $line) { - $feedCategoryNames[$line['id']] = $line['c_name']; + $feedCategoryNames[$line['id']] = array( + 'name' => $line['name'], + 'c_name' => $line['c_name'], + ); } return $feedCategoryNames; } diff --git a/p/api/greader.php b/p/api/greader.php index d1846fdaf..f26217279 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -294,7 +294,7 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex header('Content-Type: application/json; charset=UTF-8'); $feedDAO = new FreshRSS_FeedDAO(); - $arrayFeedCategoryNames = $feedDAO->arrayCategoryNames(); + $arrayFeedCategoryNames = $feedDAO->arrayFeedCategoryNames(); switch ($path) { case 'reading-list': @@ -332,10 +332,17 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex $items = array(); foreach ($entries as $entry) { $f_id = $entry->feed(); - $c_name = isset($arrayFeedCategoryNames[$f_id]) ? $arrayFeedCategoryNames[$f_id] : '_'; + if (isset($arrayFeedCategoryNames[$f_id])) { + $c_name = $arrayFeedCategoryNames[$f_id]['c_name']; + $f_name = $arrayFeedCategoryNames[$f_id]['name']; + } else { + $c_name = '_'; + $f_name = '_'; + } $item = array( 'id' => /*'tag:google.com,2005:reader/item/' .*/ dec2hex($entry->id()), //64-bit hexa http://code.google.com/p/google-reader-api/wiki/ItemId 'crawlTimeMsec' => substr($entry->id(), 0, -3), + 'timestampUsec' => $entry->id(), //EasyRSS 'published' => $entry->date(true), 'title' => $entry->title(), 'summary' => array('content' => $entry->content()), @@ -348,7 +355,7 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex ), 'origin' => array( 'streamId' => 'feed/' . $f_id, - //'title' => $line['f_name'], + 'title' => $f_name, //EasyRSS //'htmlUrl' => $line['f_website'], ), ); @@ -420,32 +427,34 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude function editTag($e_ids, $a, $r) { logMe("editTag()\n"); + + foreach ($e_ids as $i => $e_id) { + $e_ids[$i] = hex2dec(basename($e_id)); //Strip prefix 'tag:google.com,2005:reader/item/' + } + $entryDAO = new FreshRSS_EntryDAO(); - foreach ($e_ids as $e_id) { //TODO: User WHERE...IN - $e_id = hex2dec(basename($e_id)); //Strip prefix 'tag:google.com,2005:reader/item/' - switch ($a) { - case 'user/-/state/com.google/read': - $entryDAO->markRead($e_id, true); - break; - case 'user/-/state/com.google/starred': - $entryDAO->markFavorite($e_id, true); - break; - /*case 'user/-/state/com.google/tracking-kept-unread': - break; - case 'user/-/state/com.google/like': - break; - case 'user/-/state/com.google/broadcast': - break;*/ - } - switch ($r) { - case 'user/-/state/com.google/read': - $entryDAO->markRead($e_id, false); - break; - case 'user/-/state/com.google/starred': - $entryDAO->markFavorite($e_id, false); - break; - } + switch ($a) { + case 'user/-/state/com.google/read': + $entryDAO->markRead($e_ids, true); + break; + case 'user/-/state/com.google/starred': + $entryDAO->markFavorite($e_ids, true); + break; + /*case 'user/-/state/com.google/tracking-kept-unread': + break; + case 'user/-/state/com.google/like': + break; + case 'user/-/state/com.google/broadcast': + break;*/ + } + switch ($r) { + case 'user/-/state/com.google/read': + $entryDAO->markRead($e_ids, false); + break; + case 'user/-/state/com.google/starred': + $entryDAO->markFavorite($e_ids, false); + break; } echo 'OK'; @@ -511,22 +520,27 @@ elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfo $count = isset($_GET['n']) ? intval($_GET['n']) : 20; //n=[integer] : The maximum number of results to return. $order = isset($_GET['r']) ? $_GET['r'] : 'd'; //r=[d|n|o] : Sort order of item results. d or n gives items in descending date order, o in ascending order. $start_time = isset($_GET['ot']) ? intval($_GET['ot']) : 0; //ot=[unix timestamp] : The time from which you want to retrieve items. Only items that have been crawled by Google Reader after this time will be returned. - if (isset($pathInfos[5]) && $pathInfos[5] === 'contents' && isset($pathInfos[6]) && isset($pathInfos[7])) { - if ($pathInfos[6] === 'feed') { - $include_target = $pathInfos[7]; - StreamContents($pathInfos[6], $include_target, $start_time, $count, $order, $exclude_target); - } elseif ($pathInfos[6] === 'user' && isset($pathInfos[8]) && isset($pathInfos[9])) { - if ($pathInfos[8] === 'state') { - if ($pathInfos[9] === 'com.google' && isset($pathInfos[10])) { - if ($pathInfos[10] === 'reading-list' || $pathInfos[10] === 'starred') { - $include_target = ''; - streamContents($pathInfos[10], $include_target, $start_time, $count, $order, $exclude_target); + if (isset($pathInfos[5]) && $pathInfos[5] === 'contents' && isset($pathInfos[6])) { + if (isset($pathInfos[7])) { + if ($pathInfos[6] === 'feed') { + $include_target = $pathInfos[7]; + StreamContents($pathInfos[6], $include_target, $start_time, $count, $order, $exclude_target); + } elseif ($pathInfos[6] === 'user' && isset($pathInfos[8]) && isset($pathInfos[9])) { + if ($pathInfos[8] === 'state') { + if ($pathInfos[9] === 'com.google' && isset($pathInfos[10])) { + if ($pathInfos[10] === 'reading-list' || $pathInfos[10] === 'starred') { + $include_target = ''; + streamContents($pathInfos[10], $include_target, $start_time, $count, $order, $exclude_target); + } } + } elseif ($pathInfos[8] === 'label') { + $include_target = $pathInfos[9]; + streamContents($pathInfos[8], $include_target, $start_time, $count, $order, $exclude_target); } - } elseif ($pathInfos[8] === 'label') { - $include_target = $pathInfos[9]; - streamContents($pathInfos[8], $include_target, $start_time, $count, $order, $exclude_target); } + } else { //EasyRSS + $include_target = ''; + streamContents('reading-list', $include_target, $start_time, $count, $order, $exclude_target); } } elseif ($pathInfos[5] === 'items') { if ($pathInfos[6] === 'ids' && isset($_GET['s'])) { -- cgit v1.2.3 From fc6769c1b10314b50be4a3d970c5c4917be6305c Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 3 Mar 2014 21:25:14 +0100 Subject: API: Add continuation mode https://github.com/marienfressinaud/FreshRSS/issues/443 Needed for e.g. EasyRSS --- app/Controllers/indexController.php | 2 +- app/Models/EntryDAO.php | 22 +++++++++++++--------- p/api/greader.php | 30 ++++++++++++++++++++++-------- 3 files changed, 36 insertions(+), 18 deletions(-) (limited to 'app/Models/EntryDAO.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 0905e591a..2f263dff0 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -126,7 +126,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $keepHistoryDefault = $this->view->conf->keep_history_default; try { - $entries = $entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, $keepHistoryDefault); + $entries = $entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, true, $keepHistoryDefault); // Si on a récupéré aucun article "non lus" // on essaye de récupérer tous les articles diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index cc52ea120..e4cf128ea 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -376,7 +376,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return isset ($entries[0]) ? $entries[0] : null; } - private function sqlListWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { + private function sqlListWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { $where = ''; $joinFeed = false; $values = array(); @@ -429,11 +429,15 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $where .= 'AND e1.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; } if (($date_min > 0) && ($type !== 's')) { - $where .= 'AND (e1.id >= ' . $date_min . '000000 OR e1.is_read = 0 OR e1.is_favorite = 1 OR (f.keep_history <> 0'; - if (intval($keepHistoryDefault) === 0) { - $where .= ' AND f.keep_history <> -2'; //default + $where .= 'AND (e1.id >= ' . $date_min . '000000'; + if ($showOlderUnreadsorFavorites) { //Lax date constraint + $where .= ' OR e1.is_read = 0 OR e1.is_favorite = 1 OR (f.keep_history <> 0'; + if (intval($keepHistoryDefault) === 0) { + $where .= ' AND f.keep_history <> -2'; //default + } + $where .= ')'; } - $where .= ')) '; + $where .= ') '; $joinFeed = true; } $search = ''; @@ -494,8 +498,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { . ($limit > 0 ? ' LIMIT ' . $limit : '')); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ } - public function listWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { - list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $keepHistoryDefault); + public function listWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { + list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $showOlderUnreadsorFavorites, $keepHistoryDefault); $sql = 'SELECT e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags ' . 'FROM `' . $this->prefix . 'entry` e ' @@ -510,8 +514,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return self::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC)); } - public function listIdsWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { //For API - list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $keepHistoryDefault); + public function listIdsWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { //For API + list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $showOlderUnreadsorFavorites, $keepHistoryDefault); $stm = $this->bd->prepare($sql); $stm->execute($values); diff --git a/p/api/greader.php b/p/api/greader.php index f26217279..b1ed34d27 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -288,7 +288,7 @@ function unreadCount() { exit(); } -function streamContents($path, $include_target, $start_time, $count, $order, $exclude_target) +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(' . $include_target . ")\n"); header('Content-Type: application/json; charset=UTF-8'); @@ -326,8 +326,12 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex break; } + if (!empty($continuation)) { + $count++; //Shift by one element + } + $entryDAO = new FreshRSS_EntryDAO(); - $entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, '', '', $start_time); + $entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, '', $start_time); $items = array(); foreach ($entries as $entry) { @@ -371,11 +375,20 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex $items[] = $item; } - echo json_encode(array( + if (!empty($continuation)) { + array_shift($items); //Discard first element that was already sent in the previous response + } + + $response = array( 'id' => 'user/-/state/com.google/reading-list', 'updated' => time(), 'items' => $items, - )), "\n"; + ); + if ((count($entries) >= $count) && (!empty($entry))) { + $response['continuation'] = $entry->id(); + } + + echo json_encode($response), "\n"; exit(); } @@ -520,27 +533,28 @@ elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfo $count = isset($_GET['n']) ? intval($_GET['n']) : 20; //n=[integer] : The maximum number of results to return. $order = isset($_GET['r']) ? $_GET['r'] : 'd'; //r=[d|n|o] : Sort order of item results. d or n gives items in descending date order, o in ascending order. $start_time = isset($_GET['ot']) ? intval($_GET['ot']) : 0; //ot=[unix timestamp] : The time from which you want to retrieve items. Only items that have been crawled by Google Reader after this time will be returned. + $continuation = isset($_GET['c']) ? $_GET['c'] : ''; //Continuation token. If a StreamContents response does not represent all items in a timestamp range, it will have a continuation attribute. The same request can be re-issued with the value of that attribute put in this parameter to get more items if (isset($pathInfos[5]) && $pathInfos[5] === 'contents' && isset($pathInfos[6])) { if (isset($pathInfos[7])) { if ($pathInfos[6] === 'feed') { $include_target = $pathInfos[7]; - StreamContents($pathInfos[6], $include_target, $start_time, $count, $order, $exclude_target); + StreamContents($pathInfos[6], $include_target, $start_time, $count, $order, $exclude_target, $continuation); } elseif ($pathInfos[6] === 'user' && isset($pathInfos[8]) && isset($pathInfos[9])) { if ($pathInfos[8] === 'state') { if ($pathInfos[9] === 'com.google' && isset($pathInfos[10])) { if ($pathInfos[10] === 'reading-list' || $pathInfos[10] === 'starred') { $include_target = ''; - streamContents($pathInfos[10], $include_target, $start_time, $count, $order, $exclude_target); + streamContents($pathInfos[10], $include_target, $start_time, $count, $order, $exclude_target, $continuation); } } } elseif ($pathInfos[8] === 'label') { $include_target = $pathInfos[9]; - streamContents($pathInfos[8], $include_target, $start_time, $count, $order, $exclude_target); + streamContents($pathInfos[8], $include_target, $start_time, $count, $order, $exclude_target, $continuation); } } } else { //EasyRSS $include_target = ''; - streamContents('reading-list', $include_target, $start_time, $count, $order, $exclude_target); + streamContents('reading-list', $include_target, $start_time, $count, $order, $exclude_target, $continuation); } } elseif ($pathInfos[5] === 'items') { if ($pathInfos[6] === 'ids' && isset($_GET['s'])) { -- cgit v1.2.3 From 779afe9c4ee00f8099a423e1fceee40197a90cfa Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 30 Mar 2014 14:28:13 +0200 Subject: Import of articles is implemented! - Remove massiveImportAction and addCategories from FeedController - Fix typo for some methods (camelCase) - addCategoryObject and addFeedObject return id if corresponding object already exists in DB - introduce addEntryObject. Return -1 if Entry already exist (in order to keep quite good performances) - Complete importArticles method Need some more tests + better performance --- app/Controllers/feedController.php | 88 ---------------------------- app/Controllers/importExportController.php | 92 +++++++++++++++++++++--------- app/Models/CategoryDAO.php | 5 +- app/Models/EntryDAO.php | 30 ++++++++++ app/Models/FeedDAO.php | 5 +- 5 files changed, 100 insertions(+), 120 deletions(-) (limited to 'app/Models/EntryDAO.php') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 43435d99d..95c9c1e9c 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -327,82 +327,6 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - public function massiveImportAction () { - # TODO: this function has moved to importExportController.php - # I keep it for the moment but should be deleted in a near future. - @set_time_limit(300); - - $this->catDAO = new FreshRSS_CategoryDAO (); - $this->catDAO->checkDefault (); - - $entryDAO = new FreshRSS_EntryDAO (); - $feedDAO = new FreshRSS_FeedDAO (); - - $categories = Minz_Request::param ('categories', array (), true); - $feeds = Minz_Request::param ('feeds', array (), true); - - // on ajoute les catégories en masse dans une fonction à part - $this->addCategories ($categories); - - // on calcule la date des articles les plus anciens qu'on accepte - $nb_month_old = $this->view->conf->old_entries; - $date_min = time () - (3600 * 24 * 30 * $nb_month_old); - - // la variable $error permet de savoir si une erreur est survenue - // Le but est de ne pas arrêter l'import même en cas d'erreur - // L'utilisateur sera mis au courant s'il y a eu des erreurs, mais - // ne connaîtra pas les détails. Ceux-ci seront toutefois logguées - $error = false; - $i = 0; - foreach ($feeds as $feed) { - try { - $values = array ( - 'id' => $feed->id (), - 'url' => $feed->url (), - 'category' => $feed->category (), - 'name' => $feed->name (), - 'website' => $feed->website (), - 'description' => $feed->description (), - 'lastUpdate' => 0, - 'httpAuth' => $feed->httpAuth () - ); - - // ajout du flux que s'il n'est pas déjà en BDD - if (!$feedDAO->searchByUrl ($values['url'])) { - $id = $feedDAO->addFeed ($values); - if ($id) { - $feed->_id ($id); - $feed->faviconPrepare(); - } else { - $error = true; - } - } - } catch (FreshRSS_Feed_Exception $e) { - $error = true; - Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); - } - } - - if ($error) { - $res = Minz_Translate::t ('feeds_imported_with_errors'); - } else { - $res = Minz_Translate::t ('feeds_imported'); - } - - $notif = array ( - 'type' => 'good', - 'content' => $res - ); - Minz_Session::_param ('notification', $notif); - Minz_Session::_param ('actualize_feeds', true); - - // et on redirige vers la page d'accueil - Minz_Request::forward (array ( - 'c' => 'index', - 'a' => 'index' - ), true); - } - public function deleteAction () { if (Minz_Request::isPost ()) { $type = Minz_Request::param ('type', 'feed'); @@ -446,16 +370,4 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } } - - private function addCategories ($categories) { - foreach ($categories as $cat) { - if (!$this->catDAO->searchByName ($cat->name ())) { - $values = array ( - 'id' => $cat->id (), - 'name' => $cat->name (), - ); - $catDAO->addCategory ($values); - } - } - } } diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index b6b4d0fed..3b6bf2c5c 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -31,7 +31,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { @set_time_limit(300); $file = $_FILES['file']; - $type_file = $this->guess_file_type($file['name']); + $type_file = $this->guessFileType($file['name']); $list_files = array( 'opml' => array(), @@ -46,7 +46,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $zip = zip_open($file['tmp_name']); while (($zipfile = zip_read($zip)) !== false) { - $type_zipfile = $this->guess_file_type(zip_entry_name($zipfile)); + $type_zipfile = $this->guessFileType(zip_entry_name($zipfile)); if ($type_file !== 'unknown') { $list_files[$type_zipfile][] = zip_entry_read( @@ -67,13 +67,13 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // And finally all other files. $error = false; foreach ($list_files['opml'] as $opml_file) { - $error = $this->import_opml($opml_file); + $error = $this->importOpml($opml_file); } foreach ($list_files['json_starred'] as $article_file) { - $error = $this->import_articles($article_file, true); + $error = $this->importArticles($article_file, true); } foreach ($list_files['json_feed'] as $article_file) { - $error = $this->import_articles($article_file); + $error = $this->importArticles($article_file); } // And finally, we get import status and redirect to the home page @@ -107,7 +107,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { )); } - private function guess_file_type($filename) { + private function guessFileType($filename) { // A *very* basic guess file type function. Only based on filename // That's could be improved but should be enough, at least for a first // implementation. @@ -128,7 +128,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } } - private function import_opml($opml_file) { + private function importOpml($opml_file) { $opml_array = array(); try { $opml_array = libopml_parse_string($opml_file); @@ -164,7 +164,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { private function addFeedOpml($feed_elt, $parent_cat) { if (is_null($parent_cat)) { // This feed has no parent category so we get the default one - $parent_cat = $catDAO->getDefault()->name(); + $parent_cat = $this->catDAO->getDefault()->name(); } $cat = $this->catDAO->searchByName($parent_cat); @@ -199,7 +199,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $id = $this->feedDAO->addFeedObject($feed); $error = ($id === false); } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::record($e->getMessage(), Minz_Log::WARNING); + Minz_Log::warning($e->getMessage()); $error = true; } @@ -226,28 +226,23 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return $error; } - private function import_articles($article_file, $starred = false) { + private function importArticles($article_file, $starred = false) { $article_object = json_decode($article_file, true); if (is_null($article_object)) { Minz_Log::warning('Try to import a non-JSON file'); return true; } + $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0; + $google_compliant = (strpos($article_object['id'], 'com.google') !== false); + $error = false; foreach ($article_object['items'] as $item) { - $key = $google_compliant ? 'htmlUrl' : 'feedUrl'; - $feed = $this->feedDAO->searchByUrl($item['origin'][$key]); + $feed = $this->addFeedArticles($item['origin'], $google_compliant); if (is_null($feed)) { - $feed = new FreshRSS_Feed($item['origin'][$key]); - $feed->_name ($item['origin']['title']); - $feed->_website ($item['origin']['htmlUrl']); - - $error = $this->addFeed($feed); // TODO - - if ($error) { - continue; - } + $error = true; + continue; } $author = isset($item['author']) ? $item['author'] : ''; @@ -258,14 +253,55 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return strpos($var, '/state/com.google') === false; }); } + $entry = new FreshRSS_Entry( $feed->id(), $item['id'], $item['title'], $author, $item[$key_content]['content'], $item['alternate'][0]['href'], - $item['published'], false, $starred, $tags + $item['published'], $is_read, $starred ); + $entry->_tags($tags); - Minz_Log::debug(print_r($entry, true)); // TODO + $id = $this->entryDAO->addEntryObject( + $entry, $this->view->conf, $feed->keepHistory() + ); + + if (!$error && ($id === false)) { + $error = true; + } } + + return $error; + } + + private function addFeedArticles($origin, $google_compliant) { + $default_cat = $this->catDAO->getDefault(); + + $return = null; + $key = $google_compliant ? 'htmlUrl' : 'feedUrl'; + $url = $origin[$key]; + $name = $origin['title']; + $website = $origin['htmlUrl']; + $error = false; + try { + // Create a Feed object and add it in DB + $feed = new FreshRSS_Feed($url); + $feed->_category($default_cat->id()); + $feed->_name($name); + $feed->_website($website); + + // addFeedObject checks if feed is already in DB so nothing else to + // check here + $id = $this->feedDAO->addFeedObject($feed); + + if ($id !== false) { + $feed->_id($id); + $return = $feed; + } + } catch (FreshRSS_Feed_Exception $e) { + Minz_Log::warning($e->getMessage()); + } + + return $return; } public function exportAction() { @@ -283,16 +319,16 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // Stuff with content if ($export_opml) { - $zip->addFromString('feeds.opml', $this->generate_opml()); + $zip->addFromString('feeds.opml', $this->generateOpml()); } if ($export_starred) { - $zip->addFromString('starred.json', $this->generate_articles('starred')); + $zip->addFromString('starred.json', $this->generateArticles('starred')); } foreach ($export_feeds as $feed_id) { $feed = $this->feedDAO->searchById($feed_id); $zip->addFromString( 'feed_' . $feed->category() . '_' . $feed->id() . '.json', - $this->generate_articles('feed', $feed) + $this->generateArticles('feed', $feed) ); } @@ -306,7 +342,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } } - private function generate_opml() { + private function generateOpml() { $list = array(); foreach ($this->catDAO->listCategories() as $key => $cat) { $list[$key]['name'] = $cat->name(); @@ -317,7 +353,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return $this->view->helperToString('export/opml'); } - private function generate_articles($type, $feed = NULL) { + private function generateArticles($type, $feed = NULL) { $this->view->categories = $this->catDAO->listCategories(); if ($type == 'starred') { diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 8be732b98..6a9b839b9 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -19,7 +19,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { } public function addCategoryObject($category) { - if (!$this->searchByName($category->name())) { + $cat = $this->searchByName($category->name()); + if (!$cat) { // Category does not exist yet in DB so we add it before continue $values = array( 'name' => $category->name(), @@ -27,7 +28,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { return $this->addCategory($values); } - return false; + return $cat->id(); } public function updateCategory ($id, $valuesTmp) { diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index e4cf128ea..6d00967fc 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -35,6 +35,36 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } } + public function addEntryObject($entry, $conf, $feedHistory) { + $existingGuids = array_fill_keys( + $this->listLastGuidsByFeed($entry->feed(), 20), 1 + ); + + $nb_month_old = max($conf->old_entries, 1); + $date_min = time() - (3600 * 24 * 30 * $nb_month_old); + + $eDate = $entry->date(true); + + if ($feedHistory == -2) { + $feedHistory = $conf->keep_history_default; + } + + if (!isset($existingGuids[$entry->guid()]) && + ($feedHistory != 0 || $eDate >= $date_min)) { + $values = $entry->toArray(); + + $useDeclaredDate = empty($existingGuids); + $values['id'] = ($useDeclaredDate || $eDate < $date_min) ? + min(time(), $eDate) . uSecString() : + uTimeString(); + + return $this->addEntry($values); + } + + // We don't return Entry object to avoid a research in DB + return -1; + } + public function markFavorite($ids, $is_favorite = true) { if (!is_array($ids)) { $ids = array($ids); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index eac21df7e..b65ff4af0 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -29,7 +29,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { // should not be aware about feed class // Add feed only if we don't find it in DB - if (!$this->searchByUrl($feed->url())) { + $feed_search = $this->searchByUrl($feed->url()); + if (!$feed_search) { $values = array( 'id' => $feed->id(), 'url' => $feed->url(), @@ -50,7 +51,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $id; } - return false; + return $feed_search->id(); } public function updateFeed ($id, $valuesTmp) { -- cgit v1.2.3 From 3e665bcf9aefe40499b5d16e901d0c7427e367af Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Sat, 12 Apr 2014 11:51:44 -0400 Subject: Delete favorite button I extract drop-down menu actions to make them as button action in the page header. I removed the favorite button on the category list because it is a duplicate from the button action. Now button action act as filters and you can combine them. It is a test to see if we can keep it like that. There is still work to do to extract other actions from the drop-down list. I did not want to change everything if we don't keep it. See #376 and #277 --- app/Controllers/indexController.php | 7 +-- app/Models/Configuration.php | 9 +++- app/Models/EntryDAO.php | 32 ++++++----- app/i18n/en.php | 4 +- app/i18n/fr.php | 2 +- app/layout/aside_flux.phtml | 9 ---- app/layout/nav_menu.phtml | 102 +++++++++++++++++++++++------------- app/views/configure/reading.phtml | 2 +- p/themes/Dark/freshrss.css | 5 ++ p/themes/Dark/global.css | 6 --- p/themes/Flat/freshrss.css | 5 ++ p/themes/Flat/global.css | 6 --- p/themes/Origine/freshrss.css | 5 ++ p/themes/Origine/global.css | 6 --- 14 files changed, 111 insertions(+), 89 deletions(-) (limited to 'app/Models/EntryDAO.php') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 9da1e5022..243d887ac 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -91,14 +91,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { $nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page); $first = Minz_Request::param ('next', ''); - if ($state === 'not_read') { //Any unread article in this category at all? + if ($state === FreshRSS_Configuration::STATE_NOT_READ) { //Any unread article in this category at all? switch ($getType) { case 'a': $hasUnread = $this->view->nb_not_read > 0; break; - case 's': - $hasUnread = $this->view->nb_favorites['unread'] > 0; - break; case 'c': $hasUnread = (!isset($this->view->cat_aside[$getId])) || ($this->view->cat_aside[$getId]->nbNotRead() > 0); break; @@ -128,7 +125,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { // Si on a récupéré aucun article "non lus" // on essaye de récupérer tous les articles - if ($state === 'not_read' && empty($entries) && ($state_param === null)) { + if ($state === FreshRSS_Configuration::STATE_NOT_READ && empty($entries) && ($state_param === null)) { Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); $this->view->state = 'all'; $entries = $entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault); diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 80031369e..f9ea47be6 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -1,6 +1,11 @@ '', //CRYPT_BLOWFISH 'posts_per_page' => 20, 'view_mode' => 'normal', - 'default_view' => 'not_read', + 'default_view' => self::STATE_NOT_READ, 'auto_load_more' => true, 'display_posts' => false, 'onread_jump_next' => true, @@ -131,7 +136,7 @@ class FreshRSS_Configuration { } } public function _default_view ($value) { - $this->data['default_view'] = $value === 'all' ? 'all' : 'not_read'; + $this->data['default_view'] = $value === 'all' ? 'all' : self::STATE_NOT_READ; } public function _display_posts ($value) { $this->data['display_posts'] = ((bool)$value) && $value !== 'no'; diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 6d00967fc..2f5a9e1f6 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -415,9 +415,6 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $where .= 'f.priority > 0 '; $joinFeed = true; break; - case 's': - $where .= 'e1.is_favorite = 1 '; - break; case 'c': $where .= 'f.category = ? '; $values[] = intval($id); @@ -433,21 +430,28 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { default: throw new FreshRSS_EntriesGetter_Exception ('Bad type in Entry->listByType: [' . $type . ']!'); } - switch ($state) { - case 'all': - break; - case 'not_read': + + if ($state & FreshRSS_Configuration::STATE_NOT_READ) { + if (!($state & FreshRSS_Configuration::STATE_READ)) { $where .= 'AND e1.is_read = 0 '; - break; - case 'read': + } + } + if ($state & FreshRSS_Configuration::STATE_READ) { + if (!($state & FreshRSS_Configuration::STATE_NOT_READ)) { $where .= 'AND e1.is_read = 1 '; - break; - case 'favorite': + } + } + if ($state & FreshRSS_Configuration::STATE_NOT_FAVORITE) { + if (!($state & FreshRSS_Configuration::STATE_FAVORITE)) { + $where .= 'AND e1.is_favorite = 0 '; + } + } + if ($state & FreshRSS_Configuration::STATE_FAVORITE) { + if (!($state & FreshRSS_Configuration::STATE_NOT_FAVORITE)) { $where .= 'AND e1.is_favorite = 1 '; - break; - default: - throw new FreshRSS_EntriesGetter_Exception ('Bad state in Entry->listByType: [' . $state . ']!'); + } } + switch ($order) { case 'DESC': case 'ASC': diff --git a/app/i18n/en.php b/app/i18n/en.php index d6096bbe8..01f31eca7 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -27,7 +27,6 @@ return array ( 'subscription_management' => 'Subscriptions management', 'main_stream' => 'Main stream', 'all_feeds' => 'All feeds', - 'favorite_feeds' => 'Favourites (%d)', 'not_read' => '%d unread', 'not_reads' => '%d unread', @@ -51,7 +50,8 @@ return array ( 'show_all_articles' => 'Show all articles', 'show_not_reads' => 'Show only unread', 'show_read' => 'Show only read', - 'show_favorite' => 'Show favorites', + 'show_favorite' => 'Show only favorites', + 'show_not_favorite' => 'Show all but favorites', 'older_first' => 'Oldest first', 'newer_first' => 'Newer first', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index d89cbb098..299d4d242 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -27,7 +27,6 @@ return array ( 'subscription_management' => 'Gestion des abonnements', 'main_stream' => 'Flux principal', 'all_feeds' => 'Tous les flux', - 'favorite_feeds' => 'Favoris (%d)', 'not_read' => '%d non lu', 'not_reads' => '%d non lus', @@ -52,6 +51,7 @@ return array ( 'show_not_reads' => 'Afficher les non lus', 'show_read' => 'Afficher les lus', 'show_favorite' => 'Afficher les favoris', + 'show_not_favorite' => 'Afficher tout sauf les favoris', 'older_first' => 'Plus anciens en premier', 'newer_first' => 'Plus récents en premier', diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index 817dae676..0542e6b66 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -28,15 +28,6 @@ -
  • - -
  • - cat_aside as $cat) { $feeds = $cat->feeds (); diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index ffdc95b24..129647607 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -11,6 +11,70 @@ loginOk) { ?> + url; + if ($this->state & FreshRSS_Configuration::STATE_READ) { + $url_state['params']['state'] = $this->state - FreshRSS_Configuration::STATE_READ; + $checked = 'true'; + } else { + $url_state['params']['state'] = $this->state + FreshRSS_Configuration::STATE_READ; + $checked = 'false'; + } + ?> + + + + state & FreshRSS_Configuration::STATE_NOT_READ) { + $url_state['params']['state'] = $this->state - FreshRSS_Configuration::STATE_NOT_READ; + $checked = 'true'; + } else { + $url_state['params']['state'] = $this->state + FreshRSS_Configuration::STATE_NOT_READ; + $checked = 'false'; + } + ?> + + + + state & FreshRSS_Configuration::STATE_FAVORITE) { + $url_state['params']['state'] = $this->state - FreshRSS_Configuration::STATE_FAVORITE; + $checked = 'true'; + } else { + $url_state['params']['state'] = $this->state + FreshRSS_Configuration::STATE_FAVORITE; + $checked = 'false'; + } + ?> + + + + state & FreshRSS_Configuration::STATE_NOT_FAVORITE) { + $url_state['params']['state'] = $this->state - FreshRSS_Configuration::STATE_NOT_FAVORITE; + $checked = 'true'; + } else { + $url_state['params']['state'] = $this->state + FreshRSS_Configuration::STATE_NOT_FAVORITE; + $checked = 'false'; + } + ?> + + + -
  • +
  • today; @@ -132,42 +196,6 @@
  • - url; - $url_state['params']['state'] = 'all'; - ?> - - - - - - - - -
  • -
  • url; diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml index c32c59305..b778a4d22 100644 --- a/app/views/configure/reading.phtml +++ b/app/views/configure/reading.phtml @@ -36,7 +36,7 @@ diff --git a/p/themes/Dark/freshrss.css b/p/themes/Dark/freshrss.css index be8d6b86a..d7da91bb8 100644 --- a/p/themes/Dark/freshrss.css +++ b/p/themes/Dark/freshrss.css @@ -88,6 +88,11 @@ .nav_menu .search { display:none; } + .nav_menu .btn[aria-checked="true"]:before{ + content: '✓'; + font-size: 2.5rem; + position: absolute; + } .favicon { height: 16px; diff --git a/p/themes/Dark/global.css b/p/themes/Dark/global.css index e296f2188..0f1baa216 100644 --- a/p/themes/Dark/global.css +++ b/p/themes/Dark/global.css @@ -416,12 +416,6 @@ input, select, textarea { background: #26303F; color: #888; } - .dropdown-menu > .item[aria-checked="true"] > a:before { - content: '✓ '; - font-weight: bold; - margin: 0 0 0 -1.2em; - padding: 0 0.2em 0 0; - } .dropdown-menu > .item:hover > a { color: #888; text-decoration: none; diff --git a/p/themes/Flat/freshrss.css b/p/themes/Flat/freshrss.css index 1f297309b..f1737d23e 100644 --- a/p/themes/Flat/freshrss.css +++ b/p/themes/Flat/freshrss.css @@ -87,6 +87,11 @@ body { .nav_menu .search { display:none; } + .nav_menu .btn[aria-checked="true"]:before{ + content: '✓'; + font-size: 2.5rem; + position: absolute; + } .favicon { height: 16px; diff --git a/p/themes/Flat/global.css b/p/themes/Flat/global.css index 4044dd781..68e9fa359 100644 --- a/p/themes/Flat/global.css +++ b/p/themes/Flat/global.css @@ -412,12 +412,6 @@ input, select, textarea { background: #2980b9; color: #fff; } - .dropdown-menu > .item[aria-checked="true"] > a:before { - content: '✓ '; - font-weight: bold; - margin: 0 0 0 -1.2em; - padding: 0 0.2em 0 0; - } .dropdown-menu > .item:hover > a { color: #fff; text-decoration: none; diff --git a/p/themes/Origine/freshrss.css b/p/themes/Origine/freshrss.css index 662e085d6..d33ee4f54 100644 --- a/p/themes/Origine/freshrss.css +++ b/p/themes/Origine/freshrss.css @@ -89,6 +89,11 @@ .nav_menu .search { display:none; } + .nav_menu .btn[aria-checked="true"]:before{ + content: '✓'; + font-size: 2.5rem; + position: absolute; + } .favicon { height: 16px; diff --git a/p/themes/Origine/global.css b/p/themes/Origine/global.css index 5792c9e4d..7fb3a8f95 100644 --- a/p/themes/Origine/global.css +++ b/p/themes/Origine/global.css @@ -428,12 +428,6 @@ input, select, textarea { background: #0062BE; color: #fff; } - .dropdown-menu > .item[aria-checked="true"] > a:before { - content: '✓ '; - font-weight: bold; - margin: 0 0 0 -1.2em; - padding: 0 0.2em 0 0; - } .dropdown-menu > .item:hover > a { color: #fff; text-decoration: none; -- cgit v1.2.3 From 86066b1659e33eb5fdfbcae5fb7f0bd93604d442 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Sun, 13 Apr 2014 07:28:41 -0400 Subject: Add a new status for 'ALL' I made the conversion in every file I can think of. It should not have any reference to the string 'all' for the state context --- app/Controllers/importExportController.php | 4 ++-- app/Controllers/indexController.php | 8 ++++---- app/Models/Configuration.php | 3 ++- app/Models/EntryDAO.php | 9 ++++++--- app/views/configure/reading.phtml | 2 +- p/api/greader.php | 8 ++++---- 6 files changed, 19 insertions(+), 15 deletions(-) (limited to 'app/Models/EntryDAO.php') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index a9b103a34..b8253b7bd 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -370,7 +370,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->view->type = 'starred'; $unread_fav = $this->entryDAO->countUnreadReadFavorites(); $this->view->entries = $this->entryDAO->listWhere( - 's', '', 'all', 'ASC', + 's', '', FreshRSS_Configuration::STATE_ALL, 'ASC', $unread_fav['all'] ); } elseif ($type == 'feed' && !is_null($feed)) { @@ -379,7 +379,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { ); $this->view->type = 'feed/' . $feed->id(); $this->view->entries = $this->entryDAO->listWhere( - 'f', $feed->id(), 'all', 'ASC', + 'f', $feed->id(), FreshRSS_Configuration::STATE_ALL, 'ASC', $this->view->conf->posts_per_page ); $this->view->feed = $feed; diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 243d887ac..48471b480 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -85,7 +85,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $state_param = Minz_Request::param ('state', null); $filter = Minz_Request::param ('search', ''); if (!empty($filter)) { - $state = 'all'; //Search always in read and unread articles + $state = FreshRSS_Configuration::STATE_ALL; //Search always in read and unread articles } $this->view->order = $order = Minz_Request::param ('order', $this->view->conf->sort_order); $nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page); @@ -108,7 +108,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { break; } if (!$hasUnread && ($state_param === null)) { - $this->view->state = $state = 'all'; + $this->view->state = $state = FreshRSS_Configuration::STATE_ALL; } } @@ -127,8 +127,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { // on essaye de récupérer tous les articles if ($state === FreshRSS_Configuration::STATE_NOT_READ && empty($entries) && ($state_param === null)) { Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); - $this->view->state = 'all'; - $entries = $entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault); + $this->view->state = FreshRSS_Configuration::STATE_ALL; + $entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb, $first, $filter, $date_min, true, $keepHistoryDefault); } if (count($entries) <= $nb) { diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index f9ea47be6..e693542e0 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -1,6 +1,7 @@ data['default_view'] = $value === 'all' ? 'all' : self::STATE_NOT_READ; + $this->data['default_view'] = $value === self::STATE_ALL ? self::STATE_ALL : self::STATE_NOT_READ; } public function _display_posts ($value) { $this->data['display_posts'] = ((bool)$value) && $value !== 'no'; diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 2f5a9e1f6..b9cbfd584 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -406,7 +406,10 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return isset ($entries[0]) ? $entries[0] : null; } - private function sqlListWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { + private function sqlListWhere($type = 'a', $id = '', $state = null , $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { + if (!$state) { + $state = FreshRSS_Configuration::STATE_ALL; + } $where = ''; $joinFeed = false; $values = array(); @@ -532,7 +535,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { . ($limit > 0 ? ' LIMIT ' . $limit : '')); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ } - public function listWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { + public function listWhere($type = 'a', $id = '', $state = null, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $showOlderUnreadsorFavorites, $keepHistoryDefault); $sql = 'SELECT e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags ' @@ -548,7 +551,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return self::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC)); } - public function listIdsWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { //For API + public function listIdsWhere($type = 'a', $id = '', $state = null, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { //For API list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $showOlderUnreadsorFavorites, $keepHistoryDefault); $stm = $this->bd->prepare($sql); diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml index b778a4d22..b277397b1 100644 --- a/app/views/configure/reading.phtml +++ b/app/views/configure/reading.phtml @@ -32,7 +32,7 @@