From db120b4ed41584c7e33bfe63015f948e6230f1ab Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 23 Feb 2014 12:16:23 +0100 Subject: Test si crypt() fonctionne pour PHP 5.3.3 https://github.com/marienfressinaud/FreshRSS/issues/432 --- lib/lib_rss.php | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'lib') diff --git a/lib/lib_rss.php b/lib/lib_rss.php index a13d9e951..8c6fb5543 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -233,3 +233,14 @@ function listUsers() { function httpAuthUser() { return isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'] : ''; } + +function cryptAvailable() { + if (version_compare(PHP_VERSION, '5.3.3', '>=')) { + try { + $hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG'; + return $hash === @crypt('password', $hash); + } catch (Exception $e) { + } + } + return false; +} -- 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 'lib') 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 'lib') 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 11b807e4c3ee63172fecae9953d92a8b937a768a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 2 Mar 2014 00:08:45 +0100 Subject: Bug usernames with uppercase https://github.com/marienfressinaud/FreshRSS/issues/439 --- lib/lib_rss.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 8c6fb5543..83edbf015 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -219,7 +219,7 @@ function invalidateHttpCache() { } function usernameFromPath($userPath) { - if (preg_match('%/([a-z0-9]{1,16})_user\.php$%', $userPath, $matches)) { + if (preg_match('%/([A-Za-z0-9]{1,16})_user\.php$%', $userPath, $matches)) { return $matches[1]; } else { return ''; -- cgit v1.2.3 From b07f9157b18bffdb28833ed1363284571be3644e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 2 Mar 2014 13:35:09 +0100 Subject: New unsafe autologin mode https://github.com/marienfressinaud/FreshRSS/issues/440 --- app/Controllers/indexController.php | 26 ++++++++++++++++++++++++++ lib/Minz/Configuration.php | 14 ++++++++++++++ p/i/install.php | 3 +++ 3 files changed, 43 insertions(+) (limited to 'lib') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index c83c5b630..0905e591a 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -352,6 +352,32 @@ class FreshRSS_index_Controller extends Minz_ActionController { } $this->view->_useLayout(false); Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); + } elseif (Minz_Configuration::unsafeAutologinEnabled() && isset($_GET['u']) && isset($_GET['p'])) { + Minz_Session::_param('currentUser'); + Minz_Session::_param('mail'); + Minz_Session::_param('passwordHash'); + $username = ctype_alnum($_GET['u']) ? $_GET['u'] : ''; + $passwordPlain = $_GET['p']; + Minz_Request::_param('p'); //Discard plain-text password ASAP + $_GET['p'] = ''; + if (!function_exists('password_verify')) { + include_once(LIB_PATH . '/password_compat.php'); + } + try { + $conf = new FreshRSS_Configuration($username); + $s = $conf->passwordHash; + $ok = password_verify($passwordPlain, $s); + unset($passwordPlain); + if ($ok) { + Minz_Session::_param('currentUser', $username); + Minz_Session::_param('passwordHash', $s); + } else { + Minz_Log::record('Unsafe password mismatch for user ' . $username, Minz_Log::WARNING); + } + } catch (Minz_Exception $me) { + Minz_Log::record('Unsafe login failure: ' . $me->getMessage(), Minz_Log::WARNING); + } + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } elseif (!Minz_Configuration::canLogIn()) { Minz_Error::error ( 403, diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index ff71d747c..324aae881 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -55,6 +55,7 @@ class Minz_Configuration { private static $allow_anonymous_refresh = false; private static $auth_type = 'none'; private static $api_enabled = false; + private static $unsafe_autologin_enabled = false; private static $db = array ( 'type' => 'mysql', @@ -135,6 +136,9 @@ class Minz_Configuration { public static function apiEnabled() { return self::$api_enabled; } + public static function unsafeAutologinEnabled() { + return self::$unsafe_autologin_enabled; + } public static function _allowAnonymous($allow = false) { self::$allow_anonymous = ((bool)$allow) && self::canLogIn(); @@ -158,6 +162,9 @@ class Minz_Configuration { public static function _enableApi($value = false) { self::$api_enabled = (bool)$value; } + public static function _enableAutologin($value = false) { + self::$unsafe_autologin_enabled = (bool)$value; + } /** * Initialise les variables de configuration @@ -188,6 +195,7 @@ class Minz_Configuration { 'allow_anonymous_refresh' => self::$allow_anonymous_refresh, 'auth_type' => self::$auth_type, 'api_enabled' => self::$api_enabled, + 'unsafe_autologin_enabled' => self::$unsafe_autologin_enabled, ), 'db' => self::$db, ); @@ -310,6 +318,12 @@ class Minz_Configuration { ($general['api_enabled'] !== 'no') ); } + if (isset ($general['unsafe_autologin_enabled'])) { + self::$unsafe_autologin_enabled = ( + ((bool)($general['unsafe_autologin_enabled'])) && + ($general['unsafe_autologin_enabled'] !== 'no') + ); + } // Base de données if (isset ($ini_array['db'])) { diff --git a/p/i/install.php b/p/i/install.php index 720813323..a7563d5ee 100644 --- a/p/i/install.php +++ b/p/i/install.php @@ -235,6 +235,9 @@ function saveStep3 () { 'default_user' => $_SESSION['default_user'], 'auth_type' => $_SESSION['auth_type'], 'allow_anonymous' => isset($_SESSION['allow_anonymous']) ? $_SESSION['allow_anonymous'] : false, + 'allow_anonymous_refresh' => false, + 'unsafe_autologin_enabled' => false, + 'api_enabled' => false, ), 'db' => array( 'type' => $_SESSION['bd_type'], -- cgit v1.2.3 From 996c387f50f9b65f271b3cd13b9d63165236b6d2 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 16 Mar 2014 19:43:17 +0100 Subject: Add some helpers to Minz_Log class Add following methods: - Minz_Log::debug($msg) - Minz_Log::notice($msg) - Minz_Log::warning($msg) - Minz_Log::error($msg) --- lib/Minz/Log.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'lib') diff --git a/lib/Minz/Log.php b/lib/Minz/Log.php index e710aad4a..d3eaec2ae 100644 --- a/lib/Minz/Log.php +++ b/lib/Minz/Log.php @@ -80,4 +80,21 @@ class Minz_Log { self::record($msg_get, Minz_Log::DEBUG, $file_name); self::record($msg_post, Minz_Log::DEBUG, $file_name); } + + /** + * Some helpers to Minz_Log::record() method + * Parameters are the same of those of the record() method. + */ + public static function debug($msg, $file_name = null) { + self::record($msg, Minz_Log::DEBUG, $file_name); + } + public static function notice($msg, $file_name = null) { + self::record($msg, Minz_Log::NOTICE, $file_name); + } + public static function warning($msg, $file_name = null) { + self::record($msg, Minz_Log::WARNING, $file_name); + } + public static function error($msg, $file_name = null) { + self::record($msg, Minz_Log::ERROR, $file_name); + } } -- cgit v1.2.3 From 72ae58d45534b4c8b49ea0ac33a9a9ec9df4bdb1 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 22 Mar 2014 13:20:49 +0100 Subject: Minz: remove Minz_Cache https://github.com/marienfressinaud/FreshRSS/issues/163#issuecomment-37990319 https://github.com/marienfressinaud/FreshRSS/issues/303 --- lib/Minz/Cache.php | 116 --------------------------------------------- lib/Minz/Configuration.php | 22 --------- lib/Minz/Dispatcher.php | 57 ++++++++-------------- 3 files changed, 21 insertions(+), 174 deletions(-) delete mode 100644 lib/Minz/Cache.php (limited to 'lib') diff --git a/lib/Minz/Cache.php b/lib/Minz/Cache.php deleted file mode 100644 index fcb627eb2..000000000 --- a/lib/Minz/Cache.php +++ /dev/null @@ -1,116 +0,0 @@ - -*/ - -/** - * La classe Cache permet de gérer facilement les pages en cache - */ -class Minz_Cache { - /** - * $expire timestamp auquel expire le cache de $url - */ - private $expire = 0; - - /** - * $file est le nom du fichier de cache - */ - private $file = ''; - - /** - * $enabled permet de déterminer si le cache est activé - */ - private static $enabled = true; - - /** - * Constructeur - */ - public function __construct () { - $this->_fileName (); - $this->_expire (); - } - - /** - * Setteurs - */ - public function _fileName () { - $file = md5 (Minz_Request::getURI ()); - - $this->file = CACHE_PATH . '/'.$file; - } - - public function _expire () { - if ($this->exist ()) { - $this->expire = filemtime ($this->file) - + Minz_Configuration::delayCache (); - } - } - - /** - * Permet de savoir si le cache est activé - * @return true si activé, false sinon - */ - public static function isEnabled () { - return Minz_Configuration::cacheEnabled () && self::$enabled; - } - - /** - * Active / désactive le cache - */ - public static function switchOn () { - self::$enabled = true; - } - public static function switchOff () { - self::$enabled = false; - } - - /** - * Détermine si le cache de $url a expiré ou non - * @return true si il a expiré, false sinon - */ - public function expired () { - return time () > $this->expire; - } - - /** - * Affiche le contenu du cache - * @print le code html du cache - */ - public function render () { - if ($this->exist ()) { - include ($this->file); - } - } - - /** - * Enregistre $html en cache - * @param $html le html à mettre en cache - */ - public function cache ($html) { - file_put_contents ($this->file, $html); - } - - /** - * Permet de savoir si le cache existe - * @return true si il existe, false sinon - */ - public function exist () { - return file_exists ($this->file); - } - - /** - * Nettoie le cache en supprimant tous les fichiers - */ - public static function clean () { - $files = opendir (CACHE_PATH); - - while ($fic = readdir ($files)) { - if ($fic != '.' && $fic != '..') { - unlink (CACHE_PATH.'/'.$fic); - } - } - - closedir ($files); - } -} diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 324aae881..d905f6ddd 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -34,8 +34,6 @@ class Minz_Configuration { * $base_url le chemin de base pour accéder à l'application * $title le nom de l'application * $language la langue par défaut de l'application - * $cacheEnabled permet de savoir si le cache doit être activé - * $delayCache la limite de cache * $db paramètres pour la base de données (tableau) * - host le serveur de la base * - user nom d'utilisateur @@ -48,8 +46,6 @@ class Minz_Configuration { private static $use_url_rewriting = false; private static $title = ''; private static $language = 'en'; - private static $cache_enabled = false; - private static $delay_cache = 3600; private static $default_user = ''; private static $allow_anonymous = false; private static $allow_anonymous_refresh = false; @@ -103,12 +99,6 @@ class Minz_Configuration { public static function language () { return self::$language; } - public static function cacheEnabled () { - return self::$cache_enabled; - } - public static function delayCache () { - return self::$delay_cache; - } public static function dataBase () { return self::$db; } @@ -282,18 +272,6 @@ class Minz_Configuration { if (isset ($general['language'])) { self::$language = $general['language']; } - if (isset ($general['cache_enabled'])) { - self::$cache_enabled = $general['cache_enabled']; - if (CACHE_PATH === false && self::$cache_enabled) { - throw new FileNotExistException ( - 'CACHE_PATH', - Minz_Exception::ERROR - ); - } - } - if (isset ($general['delay_cache'])) { - self::$delay_cache = inval($general['delay_cache']); - } if (isset ($general['default_user'])) { self::$default_user = $general['default_user']; } diff --git a/lib/Minz/Dispatcher.php b/lib/Minz/Dispatcher.php index 71dfe8ac6..819f4cd5c 100644 --- a/lib/Minz/Dispatcher.php +++ b/lib/Minz/Dispatcher.php @@ -41,7 +41,6 @@ class Minz_Dispatcher { * @exception Minz_Exception */ public function run ($ob = true) { - $cache = new Minz_Cache(); // Le ob_start est dupliqué : sans ça il y a un bug sous Firefox // ici on l'appelle avec 'ob_gzhandler', après sans. // Vraisemblablement la compression fonctionne mais c'est sale @@ -50,45 +49,31 @@ class Minz_Dispatcher { ob_start ('ob_gzhandler'); } - if (Minz_Cache::isEnabled () && !$cache->expired ()) { - if ($ob) { - ob_start (); - } - $cache->render (); - if ($ob) { - $text = ob_get_clean(); - } - } else { - $text = ''; //TODO: Clean this code - while (Minz_Request::$reseted) { - Minz_Request::$reseted = false; + $text = ''; //TODO: Clean this code + while (Minz_Request::$reseted) { + Minz_Request::$reseted = false; - try { - $this->createController ('FreshRSS_' . Minz_Request::controllerName () . '_Controller'); - $this->controller->init (); - $this->controller->firstAction (); - $this->launchAction ( - Minz_Request::actionName () - . 'Action' - ); - $this->controller->lastAction (); + try { + $this->createController ('FreshRSS_' . Minz_Request::controllerName () . '_Controller'); + $this->controller->init (); + $this->controller->firstAction (); + $this->launchAction ( + Minz_Request::actionName () + . 'Action' + ); + $this->controller->lastAction (); - if (!Minz_Request::$reseted) { - if ($ob) { - ob_start (); - } - $this->controller->view ()->build (); - if ($ob) { - $text = ob_get_clean(); - } + if (!Minz_Request::$reseted) { + if ($ob) { + ob_start (); + } + $this->controller->view ()->build (); + if ($ob) { + $text = ob_get_clean(); } - } catch (Minz_Exception $e) { - throw $e; } - } - - if (Minz_Cache::isEnabled ()) { - $cache->cache ($text); + } catch (Minz_Exception $e) { + throw $e; } } -- cgit v1.2.3 From 5081ffaf39699398f83be97e47b72444e5bcd5d1 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 22 Mar 2014 17:56:07 +0100 Subject: Minz: remove one layer of ob_ (experimental) https://github.com/marienfressinaud/FreshRSS/issues/303#issuecomment-38351311 https://github.com/marienfressinaud/FreshRSS/issues/163 * Remove Minz_Response (not needed anymore) * Move Minz_Request::reseted to Minz_Dispatcher::reset() --- app/Controllers/importExportController.php | 14 ++---- app/actualize_script.php | 1 - lib/Minz/Dispatcher.php | 73 +++++++++++++----------------- lib/Minz/Error.php | 32 +++++++++---- lib/Minz/FrontController.php | 16 +------ lib/Minz/Request.php | 5 +- lib/Minz/Response.php | 60 ------------------------ lib/Minz/View.php | 10 ++++ lib/lib_rss.php | 2 +- 9 files changed, 73 insertions(+), 140 deletions(-) delete mode 100644 lib/Minz/Response.php (limited to 'lib') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 458814676..f697f4c9e 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -65,7 +65,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $export_all = Minz_Request::param('export_all', false); // code from https://stackoverflow.com/questions/1061710/php-zip-files-on-the-fly - $file = tempnam("tmp", "zip"); + $file = tempnam('tmp', 'zip'); $zip = new ZipArchive(); $zip->open($file, ZipArchive::OVERWRITE); @@ -101,17 +101,11 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } $this->view->categories = $list; - - // TODO: add a parameter to renderHelper in order to get a variable - ob_start(); - $this->view->renderHelper('export/opml'); - return ob_get_clean(); + return $this->view->helperToString('export/opml'); } private function generate_articles($type) { - // TODO: same here + we should get articles according to $type - ob_start(); - $this->view->renderHelper('export/articles'); - return ob_get_clean(); + // TODO: we should get articles according to $type + return $this->view->helperToString('export/articles'); } } diff --git a/app/actualize_script.php b/app/actualize_script.php index 8d81e0189..4c306b8da 100755 --- a/app/actualize_script.php +++ b/app/actualize_script.php @@ -28,7 +28,6 @@ foreach ($users as $myUser) { $_SERVER['HTTP_HOST'] = ''; $freshRSS = new FreshRSS(); - $freshRSS->_useOb(false); Minz_Configuration::_authType('none'); diff --git a/lib/Minz/Dispatcher.php b/lib/Minz/Dispatcher.php index 819f4cd5c..ca1fd1f5c 100644 --- a/lib/Minz/Dispatcher.php +++ b/lib/Minz/Dispatcher.php @@ -14,6 +14,7 @@ class Minz_Dispatcher { /* singleton */ private static $instance = null; + private static $needsReset; private $router; private $controller; @@ -40,44 +41,36 @@ class Minz_Dispatcher { * Remplit le body de Response à partir de la Vue * @exception Minz_Exception */ - public function run ($ob = true) { - // Le ob_start est dupliqué : sans ça il y a un bug sous Firefox - // ici on l'appelle avec 'ob_gzhandler', après sans. - // Vraisemblablement la compression fonctionne mais c'est sale - // J'ignore les effets de bord :( - if ($ob) { - ob_start ('ob_gzhandler'); - } - - $text = ''; //TODO: Clean this code - while (Minz_Request::$reseted) { - Minz_Request::$reseted = false; + public function run () { + do { + self::$needsReset = false; try { $this->createController ('FreshRSS_' . Minz_Request::controllerName () . '_Controller'); $this->controller->init (); $this->controller->firstAction (); - $this->launchAction ( - Minz_Request::actionName () - . 'Action' - ); + if (!self::$needsReset) { + $this->launchAction ( + Minz_Request::actionName () + . 'Action' + ); + } $this->controller->lastAction (); - if (!Minz_Request::$reseted) { - if ($ob) { - ob_start (); - } - $this->controller->view ()->build (); - if ($ob) { - $text = ob_get_clean(); - } + if (!self::$needsReset) { + echo $this->controller->view ()->build (); } } catch (Minz_Exception $e) { throw $e; } - } + } while (self::$needsReset); + } - Minz_Response::setBody ($text); + /** + * Informe le contrôleur qu'il doit recommancer car la requête a été modifiée + */ + public static function reset() { + self::$needsReset = true; } /** @@ -114,21 +107,19 @@ class Minz_Dispatcher { * le controller */ private function launchAction ($action_name) { - if (!Minz_Request::$reseted) { - if (!is_callable (array ( - $this->controller, - $action_name - ))) { - throw new Minz_ActionException ( - get_class ($this->controller), - $action_name, - Minz_Exception::ERROR - ); - } - call_user_func (array ( - $this->controller, - $action_name - )); + if (!is_callable (array ( + $this->controller, + $action_name + ))) { + throw new Minz_ActionException ( + get_class ($this->controller), + $action_name, + Minz_Exception::ERROR + ); } + call_user_func (array ( + $this->controller, + $action_name + )); } } diff --git a/lib/Minz/Error.php b/lib/Minz/Error.php index 337ab6c0a..c8222a430 100644 --- a/lib/Minz/Error.php +++ b/lib/Minz/Error.php @@ -23,13 +23,32 @@ class Minz_Error { $logs = self::processLogs ($logs); $error_filename = APP_PATH . '/Controllers/errorController.php'; + switch ($code) { + case 200 : + header('HTTP/1.1 200 OK'); + break; + case 403 : + header('HTTP/1.1 403 Forbidden'); + break; + case 404 : + header('HTTP/1.1 404 Not Found'); + break; + case 500 : + header('HTTP/1.1 500 Internal Server Error'); + break; + case 503 : + header('HTTP/1.1 503 Service Unavailable'); + break; + default : + header('HTTP/1.1 500 Internal Server Error'); + } + if (file_exists ($error_filename)) { $params = array ( 'code' => $code, 'logs' => $logs ); - Minz_Response::setHeader ($code); if ($redirect) { Minz_Request::forward (array ( 'c' => 'error' @@ -41,19 +60,16 @@ class Minz_Error { ), false); } } else { - $text = '

An error occured

'."\n"; + echo '

An error occured

' . "\n"; if (!empty ($logs)) { - $text .= '
    '."\n"; + echo '
      ' . "\n"; foreach ($logs as $log) { - $text .= '
    • ' . $log . '
    • '."\n"; + echo '
    • ' . $log . '
    • ' . "\n"; } - $text .= '
    '."\n"; + echo '
' . "\n"; } - Minz_Response::setHeader ($code); - Minz_Response::setBody ($text); - Minz_Response::send (); exit (); } } diff --git a/lib/Minz/FrontController.php b/lib/Minz/FrontController.php index 80eda8877..3e50db1cf 100644 --- a/lib/Minz/FrontController.php +++ b/lib/Minz/FrontController.php @@ -26,8 +26,6 @@ class Minz_FrontController { protected $dispatcher; protected $router; - private $useOb = true; - /** * Constructeur * Initialise le router et le dispatcher @@ -63,8 +61,7 @@ class Minz_FrontController { */ public function run () { try { - $this->dispatcher->run ($this->useOb); - Minz_Response::send (); + $this->dispatcher->run(); } catch (Minz_Exception $e) { try { Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); @@ -96,15 +93,4 @@ class Minz_FrontController { } exit ('### Application problem ###
'."\n".$txt); } - - public function useOb() { - return $this->useOb; - } - - /** - * Use ob_start('ob_gzhandler') or not. - */ - public function _useOb($ob) { - return $this->useOb = (bool)$ob; - } } diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 282d47a77..7e3c59990 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -15,8 +15,6 @@ class Minz_Request { private static $default_controller_name = 'index'; private static $default_action_name = 'index'; - public static $reseted = true; - /** * Getteurs */ @@ -137,14 +135,13 @@ class Minz_Request { header ('Location: ' . Minz_Url::display ($url, 'php')); exit (); } else { - self::$reseted = true; - self::_controllerName ($url['c']); self::_actionName ($url['a']); self::_params (array_merge ( self::$params, $url['params'] )); + Minz_Dispatcher::reset(); } } diff --git a/lib/Minz/Response.php b/lib/Minz/Response.php deleted file mode 100644 index f8ea3d946..000000000 --- a/lib/Minz/Response.php +++ /dev/null @@ -1,60 +0,0 @@ - -*/ - -/** - * Response représente la requête http renvoyée à l'utilisateur - */ -class Minz_Response { - private static $header = 'HTTP/1.0 200 OK'; - private static $body = ''; - - /** - * Mets à jour le body de la Response - * @param $text le texte à incorporer dans le body - */ - public static function setBody ($text) { - self::$body = $text; - } - - /** - * Mets à jour le header de la Response - * @param $code le code HTTP, valeurs possibles - * - 200 (OK) - * - 403 (Forbidden) - * - 404 (Forbidden) - * - 500 (Forbidden) -> par défaut si $code erroné - * - 503 (Forbidden) - */ - public static function setHeader ($code) { - switch ($code) { - case 200 : - self::$header = 'HTTP/1.0 200 OK'; - break; - case 403 : - self::$header = 'HTTP/1.0 403 Forbidden'; - break; - case 404 : - self::$header = 'HTTP/1.0 404 Not Found'; - break; - case 500 : - self::$header = 'HTTP/1.0 500 Internal Server Error'; - break; - case 503 : - self::$header = 'HTTP/1.0 503 Service Unavailable'; - break; - default : - self::$header = 'HTTP/1.0 500 Internal Server Error'; - } - } - - /** - * Envoie la Response à l'utilisateur - */ - public static function send () { - header (self::$header); - echo self::$body; - } -} diff --git a/lib/Minz/View.php b/lib/Minz/View.php index e170bd406..00d9a1a6d 100644 --- a/lib/Minz/View.php +++ b/lib/Minz/View.php @@ -102,6 +102,16 @@ class Minz_View { } } + /** + * Retourne renderHelper() dans une chaîne + * @param $helper l'élément à traîter + */ + public function helperToString($helper) { + ob_start(); + renderHelper($helper); + return ob_get_clean(); + } + /** * Permet de choisir si on souhaite utiliser le layout * @param $use true si on souhaite utiliser le layout, false sinon diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 83edbf015..2077fe63f 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -27,7 +27,7 @@ function classAutoloader($class) { include(APP_PATH . '/Models/' . $components[1] . '.php'); return; case 3: //Controllers, Exceptions - include(APP_PATH . '/' . $components[2] . 's/' . $components[1] . $components[2] . '.php'); + @include(APP_PATH . '/' . $components[2] . 's/' . $components[1] . $components[2] . '.php'); return; } } elseif (strpos($class, 'Minz') === 0) { -- cgit v1.2.3 From c8aa451c768a3d4dfce3d19648f3c8420dedb74c Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 24 Mar 2014 20:55:18 +0100 Subject: Minz: remove url_rewriting As suggested https://github.com/marienfressinaud/FreshRSS/issues/163#issuecomment-38478669 At the same time, removes a bunch of (almost) dead code such as Minz_Router (the few remaining lines being moved to Minz_FrontController to avoid a class) Contributes to https://github.com/marienfressinaud/FreshRSS/issues/303 --- lib/Minz/ActionController.php | 6 +- lib/Minz/Configuration.php | 9 -- lib/Minz/Dispatcher.php | 16 +-- lib/Minz/FrontController.php | 43 ++++++-- lib/Minz/RouteNotFoundException.php | 16 --- lib/Minz/Router.php | 209 ------------------------------------ lib/Minz/Url.php | 13 +-- p/i/install.php | 1 - 8 files changed, 40 insertions(+), 273 deletions(-) delete mode 100644 lib/Minz/RouteNotFoundException.php delete mode 100644 lib/Minz/Router.php (limited to 'lib') diff --git a/lib/Minz/ActionController.php b/lib/Minz/ActionController.php index 409d9611f..b47c54554 100644 --- a/lib/Minz/ActionController.php +++ b/lib/Minz/ActionController.php @@ -8,16 +8,12 @@ * La classe ActionController représente le contrôleur de l'application */ class Minz_ActionController { - protected $router; protected $view; /** * Constructeur - * @param $controller nom du controller - * @param $action nom de l'action à lancer */ - public function __construct ($router) { - $this->router = $router; + public function __construct () { $this->view = new Minz_View (); $this->view->attributeParams (); } diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index d905f6ddd..16c8eb727 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -30,7 +30,6 @@ class Minz_Configuration { * définition des variables de configuration * $salt une chaîne de caractères aléatoires (obligatoire) * $environment gère le niveau d'affichage pour log et erreurs - * $use_url_rewriting indique si on utilise l'url_rewriting * $base_url le chemin de base pour accéder à l'application * $title le nom de l'application * $language la langue par défaut de l'application @@ -43,7 +42,6 @@ class Minz_Configuration { private static $salt = ''; private static $environment = Minz_Configuration::PRODUCTION; private static $base_url = ''; - private static $use_url_rewriting = false; private static $title = ''; private static $language = 'en'; private static $default_user = ''; @@ -90,9 +88,6 @@ class Minz_Configuration { public static function baseUrl () { return self::$base_url; } - public static function useUrlRewriting () { - return self::$use_url_rewriting; - } public static function title () { return self::$title; } @@ -176,7 +171,6 @@ class Minz_Configuration { $ini_array = array( 'general' => array( 'environment' => self::environment(true), - 'use_url_rewriting' => self::$use_url_rewriting, 'salt' => self::$salt, 'base_url' => self::$base_url, 'title' => self::$title, @@ -262,9 +256,6 @@ class Minz_Configuration { if (isset ($general['base_url'])) { self::$base_url = $general['base_url']; } - if (isset ($general['use_url_rewriting'])) { - self::$use_url_rewriting = $general['use_url_rewriting']; - } if (isset ($general['title'])) { self::$title = $general['title']; diff --git a/lib/Minz/Dispatcher.php b/lib/Minz/Dispatcher.php index ca1fd1f5c..f62a92911 100644 --- a/lib/Minz/Dispatcher.php +++ b/lib/Minz/Dispatcher.php @@ -16,26 +16,18 @@ class Minz_Dispatcher { private static $instance = null; private static $needsReset; - private $router; private $controller; /** * Récupère l'instance du Dispatcher */ - public static function getInstance ($router) { + public static function getInstance () { if (self::$instance === null) { - self::$instance = new Minz_Dispatcher ($router); + self::$instance = new Minz_Dispatcher (); } return self::$instance; } - /** - * Constructeur - */ - private function __construct ($router) { - $this->router = $router; - } - /** * Lance le controller indiqué dans Request * Remplit le body de Response à partir de la Vue @@ -58,7 +50,7 @@ class Minz_Dispatcher { $this->controller->lastAction (); if (!self::$needsReset) { - echo $this->controller->view ()->build (); + $this->controller->view ()->build (); } } catch (Minz_Exception $e) { throw $e; @@ -90,7 +82,7 @@ class Minz_Dispatcher { Minz_Exception::ERROR ); } - $this->controller = new $controller_name ($this->router); + $this->controller = new $controller_name (); if (! ($this->controller instanceof Minz_ActionController)) { throw new Minz_ControllerNotActionControllerException ( diff --git a/lib/Minz/FrontController.php b/lib/Minz/FrontController.php index 3e50db1cf..f13882801 100644 --- a/lib/Minz/FrontController.php +++ b/lib/Minz/FrontController.php @@ -24,11 +24,10 @@ */ class Minz_FrontController { protected $dispatcher; - protected $router; /** * Constructeur - * Initialise le router et le dispatcher + * Initialise le dispatcher, met à jour la Request */ public function __construct () { if (LOG_PATH === false) { @@ -40,24 +39,46 @@ class Minz_FrontController { Minz_Request::init (); - $this->router = new Minz_Router (); - $this->router->init (); - } catch (Minz_RouteNotFoundException $e) { - Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); - Minz_Error::error ( - 404, - array ('error' => array ($e->getMessage ())) + $url = $this->buildUrl(); + $url['params'] = array_merge ( + $url['params'], + Minz_Request::fetchPOST () ); + Minz_Request::forward ($url); } catch (Minz_Exception $e) { Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); $this->killApp ($e->getMessage ()); } - $this->dispatcher = Minz_Dispatcher::getInstance ($this->router); + $this->dispatcher = Minz_Dispatcher::getInstance(); + } + + /** + * Retourne un tableau représentant l'url passée par la barre d'adresses + * @return tableau représentant l'url + */ + private function buildUrl() { + $url = array (); + + $url['c'] = Minz_Request::fetchGET ( + 'c', + Minz_Request::defaultControllerName () + ); + $url['a'] = Minz_Request::fetchGET ( + 'a', + Minz_Request::defaultActionName () + ); + $url['params'] = Minz_Request::fetchGET (); + + // post-traitement + unset ($url['params']['c']); + unset ($url['params']['a']); + + return $url; } /** - * Démarre l'application (lance le dispatcher et renvoie la réponse + * Démarre l'application (lance le dispatcher et renvoie la réponse) */ public function run () { try { diff --git a/lib/Minz/RouteNotFoundException.php b/lib/Minz/RouteNotFoundException.php deleted file mode 100644 index dc4f6fbad..000000000 --- a/lib/Minz/RouteNotFoundException.php +++ /dev/null @@ -1,16 +0,0 @@ -route = $route; - - $message = 'Route `' . $route . '` not found'; - - parent::__construct ($message, $code); - } - - public function route () { - return $this->route; - } -} diff --git a/lib/Minz/Router.php b/lib/Minz/Router.php deleted file mode 100644 index 1ccd72597..000000000 --- a/lib/Minz/Router.php +++ /dev/null @@ -1,209 +0,0 @@ - -*/ - -/** - * La classe Router gère le routage de l'application - * Les routes sont définies dans APP_PATH.'/configuration/routes.php' - */ -class Minz_Router { - const ROUTES_PATH_NAME = '/configuration/routes.php'; - - private $routes = array (); - - /** - * Constructeur - * @exception FileNotExistException si ROUTES_PATH_NAME n'existe pas - * et que l'on utilise l'url rewriting - */ - public function __construct () { - if (Minz_Configuration::useUrlRewriting ()) { - if (file_exists (APP_PATH . self::ROUTES_PATH_NAME)) { - $routes = include ( - APP_PATH . self::ROUTES_PATH_NAME - ); - - if (!is_array ($routes)) { - $routes = array (); - } - - $this->routes = array_map ( - array ('Url', 'checkUrl'), - $routes - ); - } else { - throw new Minz_FileNotExistException ( - self::ROUTES_PATH_NAME, - Minz_Exception::ERROR - ); - } - } - } - - /** - * Initialise le Router en déterminant le couple Controller / Action - * Mets à jour la Request - * @exception RouteNotFoundException si l'uri n'est pas présente dans - * > la table de routage - */ - public function init () { - $url = array (); - - if (Minz_Configuration::useUrlRewriting ()) { - try { - $url = $this->buildWithRewriting (); - } catch (Minz_RouteNotFoundException $e) { - throw $e; - } - } else { - $url = $this->buildWithoutRewriting (); - } - - $url['params'] = array_merge ( - $url['params'], - Minz_Request::fetchPOST () - ); - - Minz_Request::forward ($url); - } - - /** - * Retourne un tableau représentant l'url passée par la barre d'adresses - * Ne se base PAS sur la table de routage - * @return tableau représentant l'url - */ - public function buildWithoutRewriting () { - $url = array (); - - $url['c'] = Minz_Request::fetchGET ( - 'c', - Minz_Request::defaultControllerName () - ); - $url['a'] = Minz_Request::fetchGET ( - 'a', - Minz_Request::defaultActionName () - ); - $url['params'] = Minz_Request::fetchGET (); - - // post-traitement - unset ($url['params']['c']); - unset ($url['params']['a']); - - return $url; - } - - /** - * Retourne un tableau représentant l'url passée par la barre d'adresses - * Se base sur la table de routage - * @return tableau représentant l'url - * @exception RouteNotFoundException si l'uri n'est pas présente dans - * > la table de routage - */ - public function buildWithRewriting () { - $url = array (); - $uri = Minz_Request::getURI (); - $find = false; - - foreach ($this->routes as $route) { - $regex = '*^' . $route['route'] . '$*'; - if (preg_match ($regex, $uri, $matches)) { - $url['c'] = $route['controller']; - $url['a'] = $route['action']; - $url['params'] = $this->getParams ( - $route['params'], - $matches - ); - $find = true; - break; - } - } - - if (!$find && $uri != '/') { - throw new Minz_RouteNotFoundException ( - $uri, - Minz_Exception::ERROR - ); - } - - // post-traitement - $url = Minz_Url::checkUrl ($url); - - return $url; - } - - /** - * Retourne l'uri d'une url en se basant sur la table de routage - * @param l'url sous forme de tableau - * @return l'uri formatée (string) selon une route trouvée - */ - public function printUriRewrited ($url) { - $route = $this->searchRoute ($url); - - if ($route !== false) { - return $this->replaceParams ($route, $url['params']); - } - - return ''; - } - - /** - * Recherche la route correspondante à une url - * @param l'url sous forme de tableau - * @return la route telle que spécifiée dans la table de routage, - * false si pas trouvée - */ - public function searchRoute ($url) { - foreach ($this->routes as $route) { - if ($route['controller'] == $url['c'] - && $route['action'] == $url['a']) { - // calcule la différence des tableaux de params - $params = array_flip ($route['params']); - $difference_params = array_diff_key ( - $params, - $url['params'] - ); - - // vérifie que pas de différence - // et le cas où $params est vide et pas $url['params'] - if (empty ($difference_params) - && (!empty ($params) || empty ($url['params']))) { - return $route; - } - } - } - - return false; - } - - /** - * Récupère un tableau dont - * - les clés sont définies dans $params_route - * - les valeurs sont situées dans $matches - * Le tableau $matches est décalé de +1 par rapport à $params_route - */ - private function getParams($params_route, $matches) { - $params = array (); - - for ($i = 0; $i < count ($params_route); $i++) { - $param = $params_route[$i]; - $params[$param] = $matches[$i + 1]; - } - - return $params; - } - - /** - * Remplace les éléments de la route par les valeurs contenues dans $params - */ - private function replaceParams ($route, $params_replace) { - $uri = $route['route']; - $params = array(); - foreach($route['params'] as $param) { - $uri = preg_replace('#\((.+)\)#U', $params_replace[$param], $uri, 1); - } - - return stripslashes($uri); - } -} diff --git a/lib/Minz/Url.php b/lib/Minz/Url.php index 17f1ddece..af48f00f5 100644 --- a/lib/Minz/Url.php +++ b/lib/Minz/Url.php @@ -5,8 +5,7 @@ */ class Minz_Url { /** - * Affiche une Url formatée selon que l'on utilise l'url_rewriting ou non - * si oui, on cherche dans la table de routage la correspondance pour formater + * Affiche une Url formatée * @param $url l'url à formater définie comme un tableau : * $url['c'] = controller * $url['a'] = action @@ -39,13 +38,7 @@ class Minz_Url { } if ($isArray) { - $router = new Minz_Router (); - - if (Minz_Configuration::useUrlRewriting ()) { - $url_string .= $router->printUriRewrited ($url); - } else { - $url_string .= self::printUri ($url, $encodage); - } + $url_string .= self::printUri ($url, $encodage); } else { $url_string .= $url; } @@ -54,7 +47,7 @@ class Minz_Url { } /** - * Construit l'URI d'une URL sans url rewriting + * Construit l'URI d'une URL * @param l'url sous forme de tableau * @param $encodage pour indiquer comment encoder les & (& ou & pour html) * @return l'uri sous la forme ?key=value&key2=value2 diff --git a/p/i/install.php b/p/i/install.php index a7563d5ee..f97f95d37 100644 --- a/p/i/install.php +++ b/p/i/install.php @@ -228,7 +228,6 @@ function saveStep3 () { $ini_array = array( 'general' => array( 'environment' => empty($_SESSION['environment']) ? 'production' : $_SESSION['environment'], - 'use_url_rewriting' => false, 'salt' => $_SESSION['salt'], 'base_url' => '', 'title' => $_SESSION['title'], -- cgit v1.2.3 From 9d87f2f0aa8306c3e07a79d1a100b4d41ea8bc72 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 27 Mar 2014 20:36:51 +0100 Subject: Export is fully implemented - Export list of feeds (OPML) - Export list of favourites (JSON) - Export list of articles per feed (JSON) --- app/Controllers/importExportController.php | 47 +++++++++++++++++----- app/i18n/en.php | 6 ++- app/i18n/fr.php | 6 ++- app/layout/aside_feed.phtml | 2 +- app/views/helpers/export/articles.phtml | 64 +++++++++++++++++------------- app/views/importExport/index.phtml | 10 ++--- lib/Minz/View.php | 2 +- 7 files changed, 90 insertions(+), 47 deletions(-) (limited to 'lib') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index f697f4c9e..040cf3f7b 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -13,16 +13,16 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } public function indexAction() { - $catDAO = new FreshRSS_CategoryDAO (); - $this->view->categories = $catDAO->listCategories (); + $catDAO = new FreshRSS_CategoryDAO(); + $this->view->categories = $catDAO->listCategories(); - $feedDAO = new FreshRSS_FeedDAO (); - $this->view->feeds = $feedDAO->listFeeds (); + $feedDAO = new FreshRSS_FeedDAO(); + $this->view->feeds = $feedDAO->listFeeds(); // au niveau de la vue, permet de ne pas voir un flux sélectionné dans la liste $this->view->flux = false; - Minz_View::prependTitle (Minz_Translate::t ('import_export') . ' · '); + Minz_View::prependTitle(Minz_Translate::t('import_export') . ' · '); } public function importAction() { @@ -62,7 +62,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $export_opml = Minz_Request::param('export_opml', false); $export_starred = Minz_Request::param('export_starred', false); - $export_all = Minz_Request::param('export_all', false); + $export_feeds = Minz_Request::param('export_feeds', false); // code from https://stackoverflow.com/questions/1061710/php-zip-files-on-the-fly $file = tempnam('tmp', 'zip'); @@ -76,11 +76,16 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if ($export_starred) { $zip->addFromString('starred.json', $this->generate_articles('starred')); } - if ($export_all) { - $zip->addFromString('all.json', $this->generate_articles('all')); + $feedDAO = new FreshRSS_FeedDAO (); + foreach ($export_feeds as $feed_id) { + $feed = $feedDAO->searchById($feed_id); + $zip->addFromString( + 'feed_' . $feed->category() . '_' . $feed->id() . '.json', + $this->generate_articles('feed', $feed) + ); } - // Close and send to users + // Close and send to user $zip->close(); header('Content-Type: application/zip'); header('Content-Length: ' . filesize($file)); @@ -104,8 +109,28 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return $this->view->helperToString('export/opml'); } - private function generate_articles($type) { - // TODO: we should get articles according to $type + private function generate_articles($type, $feed = NULL) { + $entryDAO = new FreshRSS_EntryDAO(); + + $catDAO = new FreshRSS_CategoryDAO(); + $this->view->categories = $catDAO->listCategories(); + + if ($type == 'starred') { + $this->view->list_title = Minz_Translate::t("starred_list"); + $this->view->type = 'starred'; + $this->view->entries = $entryDAO->listWhere( + 's', '', 'all', 'ASC', + $entryDAO->countUnreadReadFavorites()['all'] + ); + } elseif ($type == 'feed' && !is_null($feed)) { + $this->view->list_title = Minz_Translate::t("feed_list", $feed->name()); + $this->view->type = 'feed/' . $feed->id(); + $this->view->entries = $entryDAO->listWhere( + 'f', $feed->id(), 'all', 'ASC', + $this->view->conf->posts_per_page + ); + $this->view->feed = $feed; + } return $this->view->helperToString('export/articles'); } } diff --git a/app/i18n/en.php b/app/i18n/en.php index e9bed39a7..1a0c80249 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -137,9 +137,13 @@ return array ( 'auto_share' => 'Share', 'auto_share_help' => 'If there is only one sharing mode, it is used. Else modes are accessible by their number.', - 'file_to_import' => 'File to import', + 'file_to_import' => 'File to import (OPML)', 'import' => 'Import', 'export' => 'Export', + 'export_opml' => 'Export list of feeds (OPML)', + 'export_starred' => 'Export your favourites', + 'starred_list' => 'List of favourite articles', + 'feed_list' => 'List of %s articles', 'or' => 'or', 'informations' => 'Information', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 24f3741c8..0cc835142 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -137,9 +137,13 @@ return array ( 'auto_share' => 'Partager', 'auto_share_help' => 'Si il n’y a qu’un mode de partage, celui ci est utilisé automatiquement. Sinon ils sont accessibles par leur numéro.', - 'file_to_import' => 'Fichier à importer', + 'file_to_import' => 'Fichier à importer (OPML)', 'import' => 'Importer', 'export' => 'Exporter', + 'export_opml' => 'Exporter la liste des flux (OPML)', + 'export_starred' => 'Exporter les favoris', + 'starred_list' => 'Liste des articles favoris', + 'feed_list' => 'Liste des articles de %s', 'or' => 'ou', 'informations' => 'Informations', diff --git a/app/layout/aside_feed.phtml b/app/layout/aside_feed.phtml index 899cafe02..ae16efd96 100644 --- a/app/layout/aside_feed.phtml +++ b/app/layout/aside_feed.phtml @@ -43,7 +43,7 @@ -
  • +
  • diff --git a/app/views/helpers/export/articles.phtml b/app/views/helpers/export/articles.phtml index b7df58caf..71ac22f44 100644 --- a/app/views/helpers/export/articles.phtml +++ b/app/views/helpers/export/articles.phtml @@ -1,30 +1,40 @@ { - "id": "user//state/org.freshrss/", - "title": "", - "author": "", - "items": [ - 1 ? ', ': ''; ?>{ - "id": "id(); ?>", - "categories": [], - "title": "title()); ?>", - "published": date(true); ?>, - "updated": date(true); ?>, - "content": "content()); ?>", - "origin": { - - "streamId": "", - "title": "", - "htmlUrl": "", - "feedUrl": "" - } + + $articles = array( + 'id' => 'user/' . str_replace('/', '', $username) . '/state/org.freshrss/' . $this->type, + 'title' => $this->list_title, + 'author' => $username, + 'items' => array() + ); + + foreach ($this->entries as $entry) { + if (!isset($this->feed)) { + $feed = FreshRSS_CategoryDAO::findFeed($this->categories, $entry->feed ()); + } else { + $feed = $this->feed; } - - ] -} \ No newline at end of file + + $articles['items'][] = array( + 'id' => $entry->id(), + 'categories' => array_values($entry->tags()), + 'title' => $entry->title(), + 'published' => $entry->date(true), + 'updated' => $entry->date(true), + 'content' => $entry->content(), + 'origin' => array( + 'streamId' => $feed->id(), + 'title' => $feed->name(), + 'htmlUrl' => $feed->website(), + 'feedUrl' => $feed->url() + ) + ); + } + + $options = 0; + if (version_compare(PHP_VERSION, '5.4.0') >= 0) { + $options = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; + } + + echo json_encode($articles, $options); +?> diff --git a/app/views/importExport/index.phtml b/app/views/importExport/index.phtml index d8c76543e..b82e0d26b 100644 --- a/app/views/importExport/index.phtml +++ b/app/views/importExport/index.phtml @@ -33,11 +33,11 @@ - + diff --git a/lib/Minz/View.php b/lib/Minz/View.php index 00d9a1a6d..f034ab10b 100644 --- a/lib/Minz/View.php +++ b/lib/Minz/View.php @@ -108,7 +108,7 @@ class Minz_View { */ public function helperToString($helper) { ob_start(); - renderHelper($helper); + $this->renderHelper($helper); return ob_get_clean(); } -- cgit v1.2.3 From 9ea3819402746d8425d4a608f2d5f3c0f5bc29fb Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 29 Mar 2014 20:18:57 +0100 Subject: Better OPML import / export - use a new OPML library (https://github.com/marienfressinaud/lib_opml) - import has been completely rewritten (far better!) - introduce addFeedObject and addCategoryObject (in DAO for the moment). Permit to add easily feeds and categories (check if they already exist in DB) - introduce html_chars_utf8 (wrap htmlspecialchars for UTF-8) --- app/Controllers/importExportController.php | 124 ++++++++----- app/Exceptions/OpmlException.php | 6 - app/Models/CategoryDAO.php | 12 ++ app/Models/FeedDAO.php | 29 +++ app/views/helpers/export/opml.phtml | 43 +++-- lib/lib_opml.php | 277 ++++++++++++++++++++--------- lib/lib_rss.php | 4 + 7 files changed, 345 insertions(+), 150 deletions(-) delete mode 100644 app/Exceptions/OpmlException.php (limited to 'lib') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index cbadeb6ca..b6b4d0fed 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -129,71 +129,101 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } private function import_opml($opml_file) { - $categories = array(); - $feeds = array(); + $opml_array = array(); try { - list($categories, $feeds) = opml_import($opml_file); - } catch (FreshRSS_Opml_Exception $e) { + $opml_array = libopml_parse_string($opml_file); + } catch (LibOPML_Exception $e) { Minz_Log::warning($e->getMessage()); return true; } $this->catDAO->checkDefault(); - // 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); + return $this->addOpmlElements($opml_array['body']); + } - // 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 + private function addOpmlElements($opml_elements, $parent_cat = null) { $error = false; - 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() - ); + foreach ($opml_elements as $elt) { + $res = false; + if (isset($elt['xmlUrl'])) { + $res = $this->addFeedOpml($elt, $parent_cat); + } else { + $res = $this->addCategoryOpml($elt, $parent_cat); + } - // ajout du flux que s'il n'est pas déjà en BDD - if (!$this->feedDAO->searchByUrl($values['url'])) { - $id = $this->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) { + // oops: there is at least one error! + $error = $res; } } return $error; } - private function addCategories($categories) { - foreach ($categories as $cat) { - if (!$this->catDAO->searchByName($cat->name())) { - $values = array( - 'id' => $cat->id(), - 'name' => $cat->name(), - ); - $this->catDAO->addCategory($values); + 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(); + } + + $cat = $this->catDAO->searchByName($parent_cat); + + if (!$cat) { + return true; + } + + // We get different useful information + $url = html_chars_utf8($feed_elt['xmlUrl']); + $name = html_chars_utf8($feed_elt['text']); + $website = ''; + if (isset($feed_elt['htmlUrl'])) { + $website = html_chars_utf8($feed_elt['htmlUrl']); + } + $description = ''; + if (isset($feed_elt['description'])) { + $description = html_chars_utf8($feed_elt['description']); + } + + $error = false; + try { + // Create a Feed object and add it in DB + $feed = new FreshRSS_Feed($url); + $feed->_category($cat->id()); + $feed->_name($name); + $feed->_website($website); + $feed->_description($description); + + // addFeedObject checks if feed is already in DB so nothing else to + // check here + $id = $this->feedDAO->addFeedObject($feed); + $error = ($id === false); + } catch (FreshRSS_Feed_Exception $e) { + Minz_Log::record($e->getMessage(), Minz_Log::WARNING); + $error = true; + } + + return $error; + } + + private function addCategoryOpml($cat_elt, $parent_cat) { + // Create a new Category object + $cat = new FreshRSS_Category(html_chars_utf8($cat_elt['text'])); + + $id = $this->catDAO->addCategoryObject($cat); + $error = ($id === false); + + if (isset($cat_elt['@outlines'])) { + // Our cat_elt contains more categories or more feeds, so we + // add them recursively. + // Note: FreshRSS does not support yet category arborescence + $res = $this->addOpmlElements($cat_elt['@outlines'], $cat->name()); + if (!$error && $res) { + $error = true; } } + + return $error; } private function import_articles($article_file, $starred = false) { diff --git a/app/Exceptions/OpmlException.php b/app/Exceptions/OpmlException.php deleted file mode 100644 index e0ea3e493..000000000 --- a/app/Exceptions/OpmlException.php +++ /dev/null @@ -1,6 +0,0 @@ -searchByName($category->name())) { + // Category does not exist yet in DB so we add it before continue + $values = array( + 'name' => $category->name(), + ); + return $this->addCategory($values); + } + + return false; + } + public function updateCategory ($id, $valuesTmp) { $sql = 'UPDATE `' . $this->prefix . 'category` SET name=? WHERE id=?'; $stm = $this->bd->prepare ($sql); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index ca25c3aeb..eac21df7e 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -24,6 +24,35 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } } + public function addFeedObject($feed) { + // TODO: not sure if we should write this method in DAO since DAO + // should not be aware about feed class + + // Add feed only if we don't find it in DB + if (!$this->searchByUrl($feed->url())) { + $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() + ); + + $id = $this->addFeed($values); + if ($id) { + $feed->_id($id); + $feed->faviconPrepare(); + } + + return $id; + } + + return false; + } + public function updateFeed ($id, $valuesTmp) { $set = ''; foreach ($valuesTmp as $key => $v) { diff --git a/app/views/helpers/export/opml.phtml b/app/views/helpers/export/opml.phtml index 2e66e5054..adbac904d 100644 --- a/app/views/helpers/export/opml.phtml +++ b/app/views/helpers/export/opml.phtml @@ -1,15 +1,30 @@ '; -?> - - - - <?php echo Minz_Configuration::title (); ?> OPML Feed - - - -categories); ?> - - + +$opml_array = array( + 'head' => array( + 'title' => Minz_Configuration::title(), + 'dateCreated' => date('D, d M Y H:i:s') + ), + 'body' => array() +); + +foreach ($this->categories as $key => $cat) { + $opml_array['body'][$key] = array( + 'text' => $cat['name'], + '@outlines' => array() + ); + + foreach ($cat['feeds'] as $feed) { + $opml_array['body'][$key]['@outlines'][] = array( + 'text' => $feed->name(), + 'type' => 'rss', + 'xmlUrl' => $feed->url(), + 'htmlUrl' => $feed->website(), + 'description' => htmlspecialchars( + $feed->description(), ENT_COMPAT, 'UTF-8' + ) + ); + } +} + +echo libopml_render($opml_array); diff --git a/lib/lib_opml.php b/lib/lib_opml.php index 05e54d85e..16a9921ea 100644 --- a/lib/lib_opml.php +++ b/lib/lib_opml.php @@ -1,23 +1,86 @@ ' . "\n"; - - foreach ($cat['feeds'] as $feed) { - $txt .= "\t" . '' . "\n"; +/* * + * lib_opml is a free library to manage OPML format in PHP. + * It takes in consideration only version 2.0 (http://dev.opml.org/spec2.html). + * Basically it means "text" attribute for outline elements is required. + * + * lib_opml requires SimpleXML (http://php.net/manual/en/book.simplexml.php) + * + * Usages: + * > include('lib_opml.php'); + * > $filename = 'my_opml_file.xml'; + * > $opml_array = libopml_parse_file($filename); + * > print_r($opml_array); + * + * > $opml_string = [...]; + * > $opml_array = libopml_parse_string($opml_string); + * > print_r($opml_array); + * + * > $opml_array = [...]; + * > $opml_string = libopml_render($opml_array); + * > $opml_object = libopml_render($opml_array, true); + * > echo $opml_string; + * > print_r($opml_object); + * + * If parsing fails for any reason (e.g. not an XML string, does not match with + * the specifications), a LibOPML_Exception is raised. + * + * Author: Marien Fressinaud + * Url: https://github.com/marienfressinaud/lib_opml + * Version: 0.1 + * Date: 2014-03-29 + * License: public domain + * + * */ + +class LibOPML_Exception extends Exception {} + + +// These elements are optional +define('HEAD_ELEMENTS', serialize(array( + 'title', 'dateCreated', 'dateModified', 'ownerName', 'ownerEmail', + 'ownerId', 'docs', 'expansionState', 'vertScrollState', 'windowTop', + 'windowLeft', 'windowBottom', 'windowRight' +))); + + +function libopml_parse_outline($outline_xml) { + $outline = array(); + + // An outline may contain any kind of attributes but "text" attribute is + // required ! + $text_is_present = false; + foreach ($outline_xml->attributes() as $key => $value) { + $outline[$key] = (string)$value; + + if ($key === 'text') { + $text_is_present = true; } + } - $txt .= '' . "\n"; + if (!$text_is_present) { + throw new LibOPML_Exception( + 'Outline does not contain any text attribute' + ); } - return $txt; + foreach ($outline_xml->children() as $key => $value) { + // An outline may contain any number of outline children + if ($key === 'outline') { + $outline['@outlines'][] = libopml_parse_outline($value); + } else { + throw new LibOPML_Exception( + 'Body can contain only outline elements' + ); + } + } + + return $outline; } -function opml_import ($xml) { - $xml = html_only_entity_decode($xml); //!\ Assume UTF-8 +function libopml_parse_string($xml) { $dom = new DOMDocument(); $dom->recover = true; $dom->strictErrorChecking = false; @@ -27,94 +90,142 @@ function opml_import ($xml) { $opml = simplexml_import_dom($dom); if (!$opml) { - throw new FreshRSS_Opml_Exception (); + throw new LibOPML_Exception(); } - $catDAO = new FreshRSS_CategoryDAO(); - $catDAO->checkDefault(); - $defCat = $catDAO->getDefault(); + $array = array( + 'version' => (string)$opml['version'], + 'head' => array(), + 'body' => array() + ); + + // First, we get all "head" elements. Head is required but its sub-elements + // are optional. + foreach ($opml->head->children() as $key => $value) { + if (in_array($key, unserialize(HEAD_ELEMENTS), true)) { + $array['head'][$key] = (string)$value; + } else { + throw new LibOPML_Exception( + $key . 'is not part of OPML format' + ); + } + } - $categories = array (); - $feeds = array (); + // Then, we get body oulines. Body must contain at least one outline + // element. + $at_least_one_outline = false; + foreach ($opml->body->children() as $key => $value) { + if ($key === 'outline') { + $at_least_one_outline = true; + $array['body'][] = libopml_parse_outline($value); + } else { + throw new LibOPML_Exception( + 'Body can contain only outline elements' + ); + } + } + + if (!$at_least_one_outline) { + throw new LibOPML_Exception( + 'Body must contain at least one outline element' + ); + } - foreach ($opml->body->outline as $outline) { - if (!isset ($outline['xmlUrl'])) { - // Catégorie - $title = ''; + return $array; +} - if (isset ($outline['text'])) { - $title = (string) $outline['text']; - } elseif (isset ($outline['title'])) { - $title = (string) $outline['title']; - } - if ($title) { - // Permet d'éviter les soucis au niveau des id : - // ceux-ci sont générés en fonction de la date, - // un flux pourrait être dans une catégorie X avec l'id Y - // alors qu'il existe déjà la catégorie X mais avec l'id Z - // Y ne sera pas ajouté et le flux non plus vu que l'id - // de sa catégorie n'exisera pas - $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); - $catDAO = new FreshRSS_CategoryDAO (); - $cat = $catDAO->searchByName ($title); - if ($cat == null) { - $cat = new FreshRSS_Category ($title); - $values = array ( - 'name' => $cat->name () - ); - $cat->_id ($catDAO->addCategory ($values)); - } - - $feeds = array_merge ($feeds, getFeedsOutline ($outline, $cat->id ())); +function libopml_parse_file($filename) { + $file_content = file_get_contents($filename); + + if ($file_content === false) { + throw new LibOPML_Exception( + $filename . ' cannot be found' + ); + } + + return libopml_parse_string($file_content); +} + + +function libopml_render_outline($parent_elt, $outline) { + // Outline MUST be an array! + if (!is_array($outline)) { + throw new LibOPML_Exception( + 'Outline element must be defined as array' + ); + } + + $outline_elt = $parent_elt->addChild('outline'); + $text_is_present = false; + foreach ($outline as $key => $value) { + // Only outlines can be an array and so we consider children are also + // outline elements. + if ($key === '@outlines' && is_array($value)) { + foreach ($value as $outline_child) { + libopml_render_outline($outline_elt, $outline_child); } + } elseif (is_array($value)) { + throw new LibOPML_Exception( + 'Type of outline elements cannot be array: ' . $key + ); } else { - // Flux rss sans catégorie, on récupère l'ajoute dans la catégorie par défaut - $feeds[] = getFeed ($outline, $defCat->id()); + // Detect text attribute is present, that's good :) + if ($key === 'text') { + $text_is_present = true; + } + + $outline_elt->addAttribute($key, $value); } } - return array ($categories, $feeds); + if (!$text_is_present) { + throw new LibOPML_Exception( + 'You must define at least a text element for all outlines' + ); + } } -/** - * import all feeds of a given outline tag - */ -function getFeedsOutline ($outline, $cat_id) { - $feeds = array (); - foreach ($outline->children () as $child) { - if (isset ($child['xmlUrl'])) { - $feeds[] = getFeed ($child, $cat_id); - } else { - $feeds = array_merge( - $feeds, - getFeedsOutline ($child, $cat_id) - ); +function libopml_render($array, $as_xml_object = false) { + $opml = new SimpleXMLElement(''); + + // Create head element. $array['head'] is optional but head element will + // exist in the final XML object. + $head = $opml->addChild('head'); + if (isset($array['head'])) { + foreach ($array['head'] as $key => $value) { + if (in_array($key, unserialize(HEAD_ELEMENTS), true)) { + $head->addChild($key, $value); + } } } - return $feeds; -} + // Check body is set and contains at least one element + if (!isset($array['body'])) { + throw new LibOPML_Exception( + '$array must contain a body element' + ); + } + if (count($array['body']) <= 0) { + throw new LibOPML_Exception( + 'Body element must contain at least one element (array)' + ); + } -function getFeed ($outline, $cat_id) { - $url = (string) $outline['xmlUrl']; - $url = htmlspecialchars($url, ENT_COMPAT, 'UTF-8'); - $title = ''; - if (isset ($outline['text'])) { - $title = (string) $outline['text']; - } elseif (isset ($outline['title'])) { - $title = (string) $outline['title']; - } - $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); - $feed = new FreshRSS_Feed ($url); - $feed->_category ($cat_id); - $feed->_name ($title); - if (isset($outline['htmlUrl'])) { - $feed->_website(htmlspecialchars((string)$outline['htmlUrl'], ENT_COMPAT, 'UTF-8')); - } - if (isset($outline['description'])) { - $feed->_description(sanitizeHTML((string)$outline['description'])); - } - return $feed; + // Create outline elements + $body = $opml->addChild('body'); + foreach ($array['body'] as $outline) { + libopml_render_outline($body, $outline); + } + + // And return the final result + if ($as_xml_object) { + return $opml; + } else { + $dom = dom_import_simplexml($opml)->ownerDocument; + $dom->formatOutput = true; + $dom->encoding = 'UTF-8'; + return $dom->saveXML(); + } } diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 2077fe63f..0f8161129 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -244,3 +244,7 @@ function cryptAvailable() { } return false; } + +function html_chars_utf8($str) { + return htmlspecialchars($str, ENT_COMPAT, 'UTF-8'); +} -- cgit v1.2.3 From 27b678203b2a9034312fcb5a0c3f923caa26901f Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 30 Mar 2014 15:42:32 +0200 Subject: Minz_Url separator is "?" instead of "/?" See https://github.com/marienfressinaud/FreshRSS/pull/426 --- lib/Minz/Url.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/Minz/Url.php b/lib/Minz/Url.php index af48f00f5..e9f9a69ba 100644 --- a/lib/Minz/Url.php +++ b/lib/Minz/Url.php @@ -54,7 +54,7 @@ class Minz_Url { */ private static function printUri ($url, $encodage) { $uri = ''; - $separator = '/?'; + $separator = '?'; if($encodage == 'html') { $and = '&'; -- cgit v1.2.3 From 17a603b333f305a5a9c8fdd36468575bc1cd8f74 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 17 May 2014 12:08:47 +0200 Subject: SimplePie patch accepted Merged into master branch of SimplePie https://github.com/simplepie/simplepie/pull/352 --- lib/SimplePie/SimplePie.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/SimplePie/SimplePie.php b/lib/SimplePie/SimplePie.php index d7aaeb0c5..685fe1cc0 100644 --- a/lib/SimplePie/SimplePie.php +++ b/lib/SimplePie/SimplePie.php @@ -1331,7 +1331,7 @@ class SimplePie // First check to see if input has been overridden. if ($this->input_encoding !== false) { - $encodings[] = strtoupper($this->input_encoding); //FreshRSS + $encodings[] = strtoupper($this->input_encoding); } $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); @@ -1355,7 +1355,7 @@ class SimplePie { if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) { - $encodings[] = strtoupper($charset[1]); //FreshRSS + $encodings[] = strtoupper($charset[1]); } else { -- cgit v1.2.3 From ecac55d3d36dbb0af1c8b5d190972adebee42434 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 1 Jun 2014 14:40:38 +0200 Subject: New search system, including date: and pubdate: and combination Now also accepts combination of #tag and intitle: and inurl: and author: and the new date: and pubdate: https://github.com/marienfressinaud/FreshRSS/issues/511 Each search prefix stop at the first space (we should add a possibility to have quotes for multiple words) So if you want two words in title, write "intitle:word1 intitle:word2" Examples of dates: date:2014 date:2014-02/2014-04 or date:201402/201404 date:P1W for the last week --- app/Models/EntryDAO.php | 70 +++++++++++++------------- lib/lib_date.php | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 34 deletions(-) create mode 100644 lib/lib_date.php (limited to 'lib') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 73893a88d..0d8c2ae13 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -478,48 +478,50 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } $search = ''; if ($filter !== '') { + require_once(LIB_PATH . '/lib_date.php'); $filter = trim($filter); $filter = addcslashes($filter, '\\%_'); - if (stripos($filter, 'intitle:') === 0) { - $filter = substr($filter, strlen('intitle:')); - $intitle = true; - } else { - $intitle = false; - } - if (stripos($filter, 'inurl:') === 0) { - $filter = substr($filter, strlen('inurl:')); - $inurl = true; - } else { - $inurl = false; - } - if (stripos($filter, 'author:') === 0) { - $filter = substr($filter, strlen('author:')); - $author = true; - } else { - $author = false; - } $terms = array_unique(explode(' ', $filter)); sort($terms); //Put #tags first foreach ($terms as $word) { $word = trim($word); - if (strlen($word) > 0) { - if ($intitle) { - $search .= 'AND e1.title LIKE ? '; - $values[] = '%' . $word .'%'; - } elseif ($inurl) { - $search .= 'AND CONCAT(e1.link, e1.guid) LIKE ? '; - $values[] = '%' . $word .'%'; - } elseif ($author) { - $search .= 'AND e1.author LIKE ? '; + if (stripos($word, 'intitle:') === 0) { + $word = substr($word, strlen('intitle:')); + $search .= 'AND e1.title LIKE ? '; + $values[] = '%' . $word .'%'; + } elseif (stripos($word, 'inurl:') === 0) { + $word = substr($word, strlen('inurl:')); + $search .= 'AND CONCAT(e1.link, e1.guid) LIKE ? '; + $values[] = '%' . $word .'%'; + } elseif (stripos($word, 'author:') === 0) { + $word = substr($word, strlen('author:')); + $search .= 'AND e1.author LIKE ? '; + $values[] = '%' . $word .'%'; + } elseif (stripos($word, 'date:') === 0) { + $word = substr($word, strlen('date:')); + list($minDate, $maxDate) = parseDateInterval($word); + if ($minDate) { + $search .= 'AND e1.id >= ' . $minDate . '000000 '; + } + if ($maxDate) { + $search .= 'AND e1.id <= ' . $maxDate . '000000 '; + } + } elseif (stripos($word, 'pubdate:') === 0) { + $word = substr($word, strlen('pubdate:')); + list($minDate, $maxDate) = parseDateInterval($word); + if ($minDate) { + $search .= 'AND e1.date >= ' . $minDate . ' '; + } + if ($maxDate) { + $search .= 'AND e1.date <= ' . $maxDate . ' '; + } + } else { + if ($word[0] === '#' && isset($word[1])) { + $search .= 'AND e1.tags LIKE ? '; $values[] = '%' . $word .'%'; } else { - if ($word[0] === '#' && isset($word[1])) { - $search .= 'AND e1.tags LIKE ? '; - $values[] = '%' . $word .'%'; - } else { - $search .= 'AND CONCAT(e1.title, UNCOMPRESS(e1.content_bin)) LIKE ? '; - $values[] = '%' . $word .'%'; - } + $search .= 'AND CONCAT(e1.title, UNCOMPRESS(e1.content_bin)) LIKE ? '; + $values[] = '%' . $word .'%'; } } } diff --git a/lib/lib_date.php b/lib/lib_date.php new file mode 100644 index 000000000..ab7975852 --- /dev/null +++ b/lib/lib_date.php @@ -0,0 +1,130 @@ +\t", + $dateIntervalArray[0] == null ? 'null' : @date('c', $dateIntervalArray[0]), '/', + $dateIntervalArray[1] == null ? 'null' : @date('c', $dateIntervalArray[1]), "\n"; +} +*/ + +function _dateFloor($isoDate) { + $x = explode('T', $isoDate, 2); + $t = isset($x[1]) ? str_pad($x[1], 6, '0') : '000000'; + return str_pad($x[0], 8, '01') . 'T' . $t; +} + +function _dateCeiling($isoDate) { + $x = explode('T', $isoDate, 2); + $t = isset($x[1]) && strlen($x[1]) > 1 ? str_pad($x[1], 6, '59') : '235959'; + switch (strlen($x[0])) { + case 4: + return $x[0] . '1231T' . $t; + case 6: + $d = @strtotime($x[0] . '01'); + return $x[0] . date('t', $d) . 'T' . $t; + default: + return $x[0] . 'T' . $t; + } +} + +function _noDelimit($isoDate) { + return $isoDate === null || $isoDate === '' ? null : + str_replace(array('-', ':'), '', $isoDate); //FIXME: Bug with negative time zone +} + +function _dateRelative($d1, $d2) { + if ($d2 === null) { + return $d1 !== null && $d1[0] !== 'P' ? $d1 : null; + } elseif ($d2 !== '' && $d2[0] != 'P' && $d1 !== null && $d1[0] !== 'P') { + $y2 = substr($d2, 0, 4); + if (strlen($y2) < 4 || !ctype_digit($y2)) { //Does not start by a year + $d2 = _noDelimit($d2); + return substr($d1, 0, -strlen($d2)) . $d2; //Add prefix from $d1 + } + } + return _noDelimit($d2); +} + +/** + * Parameter $dateInterval is a string containing an ISO 8601 time intervals. + * Returns an array with the minimum and maximum Unix timestamp of this interval, or null if open interval. + */ +function parseDateInterval($dateInterval) { + $dateInterval = trim($dateInterval); + $dateInterval = str_replace('--', '/', $dateInterval); + $dateInterval = strtoupper($dateInterval); + $min = null; + $max = null; + $x = explode('/', $dateInterval, 2); + $d1 = _noDelimit($x[0]); + $d2 = _dateRelative($d1, count($x) > 1 ? $x[1] : null); + if ($d1 !== null && $d1[0] !== 'P') { + $min = @strtotime(_dateFloor($d1)); + } + if ($d2 !== null) { + if ($d2[0] === 'P') { + try { + $di2 = new DateInterval($d2); + $dt1 = @date_create(); //new DateTime() would create an Exception if the default time zone is not defined + if ($min !== null && $min !== false) { + $dt1->setTimestamp($min); + } + $max = $dt1->add($di2)->getTimestamp() - 1; + } catch (Exception $e) { + $max = false; + } + } elseif ($d1 === null || $d1[0] !== 'P') { + $max = @strtotime(_dateCeiling($d2)); + } else { + $max = @strtotime($d2); + } + } + if ($d1 !== null && $d1[0] === 'P') { + try { + $di1 = new DateInterval($d1); + $dt2 = @date_create(); + if ($max !== null && $max !== false) { + $dt2->setTimestamp($max); + } + $min = $dt2->sub($di1)->getTimestamp() + 1; + } catch (Exception $e) { + $min = false; + } + } + return array($min, $max); +} -- cgit v1.2.3 From a1adac617a475ba17be0623c1bde34681852b734 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 1 Jun 2014 14:55:03 +0200 Subject: lib_date comment https://github.com/marienfressinaud/FreshRSS/issues/511 --- lib/lib_date.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/lib_date.php b/lib/lib_date.php index ab7975852..e8c8c9fac 100644 --- a/lib/lib_date.php +++ b/lib/lib_date.php @@ -82,7 +82,7 @@ function _dateRelative($d1, $d2) { /** * Parameter $dateInterval is a string containing an ISO 8601 time intervals. - * Returns an array with the minimum and maximum Unix timestamp of this interval, or null if open interval. + * Returns an array with the minimum and maximum Unix timestamp of this interval, or null if open interval, or false if error. */ function parseDateInterval($dateInterval) { $dateInterval = trim($dateInterval); -- cgit v1.2.3 From 1857d2121c5a606080fe48f3a05024d9457f47f8 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 1 Jun 2014 14:56:50 +0200 Subject: lib_date comment https://github.com/marienfressinaud/FreshRSS/issues/511 --- lib/lib_date.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/lib_date.php b/lib/lib_date.php index e8c8c9fac..9533711e3 100644 --- a/lib/lib_date.php +++ b/lib/lib_date.php @@ -81,8 +81,9 @@ function _dateRelative($d1, $d2) { } /** - * Parameter $dateInterval is a string containing an ISO 8601 time intervals. - * Returns an array with the minimum and maximum Unix timestamp of this interval, or null if open interval, or false if error. + * Parameter $dateInterval is a string containing an ISO 8601 time interval. + * Returns an array with the minimum and maximum Unix timestamp of this interval, + * or null if open interval, or false if error. */ function parseDateInterval($dateInterval) { $dateInterval = trim($dateInterval); -- cgit v1.2.3 From a7e833280954a537e12d5a3f4fa12a5b9e8412da Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 14 Jun 2014 08:58:33 +0200 Subject: Improve system of queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Coding style - More checks server side - Default query name is "Query n°X" - List of queries is moved into nav_menu, in a dropdown - Better system to remove fields in JS (to a.remove elements, give an attibute data-remove="id_to_remove") - Fix a bug in lib/Mine/Request.php (htmlspecialchars_utf8 can be applied on arrays now) - Few theme improvements - Add an element .no-mobile to apply to elements which should not appear on mobiles See https://github.com/marienfressinaud/FreshRSS/pull/498 --- app/Controllers/configureController.php | 66 +++++++++++++------- app/Models/Configuration.php | 8 ++- app/i18n/en.php | 49 ++++++++------- app/i18n/fr.php | 49 ++++++++------- app/layout/aside_flux.phtml | 17 ------ app/layout/nav_menu.phtml | 63 +++++++++++++------ app/views/configure/queries.phtml | 105 +++++++++++++++++++++++--------- app/views/configure/sharing.phtml | 14 ++--- lib/Minz/Request.php | 5 +- p/scripts/main.js | 21 ++++--- p/themes/Origine/origine.css | 6 +- p/themes/Origine/template.css | 13 +++- 12 files changed, 263 insertions(+), 153 deletions(-) (limited to 'lib') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 14cd65647..89130cae4 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -302,8 +302,14 @@ class FreshRSS_configure_Controller extends Minz_ActionController { public function queriesAction () { if (Minz_Request::isPost ()) { - $params = Minz_Request::params(); - $this->view->conf->_queries (isset($params['queries']) ? $params['queries'] : array()); + $queries = Minz_Request::param('queries', array()); + + foreach ($queries as $key => $query) { + if (!$query['name']) { + $query['name'] = Minz_Translate::t('query_number', $key + 1); + } + } + $this->view->conf->_queries($queries); $this->view->conf->save(); $notif = array ( @@ -316,25 +322,39 @@ class FreshRSS_configure_Controller extends Minz_ActionController { } else { $this->view->query_get = array(); foreach ($this->view->conf->queries as $key => $query) { - if (isset($query['get'])) { - switch ($query['get'][0]) { - case 'c': - $dao = new FreshRSS_CategoryDAO(); - $category = $dao->searchById(substr($query['get'], 2)); - $this->view->query_get[$key] = array( - 'type' => 'category', - 'name' => $category->name(), - ); - break; - case 'f': - $dao = new FreshRSS_FeedDAO(); - $feed = $dao->searchById(substr($query['get'], 2)); - $this->view->query_get[$key] = array( - 'type' => 'feed', - 'name' => $feed->name(), - ); - break; - } + if (!isset($query['get'])) { + continue; + } + + switch ($query['get'][0]) { + case 'c': + $dao = new FreshRSS_CategoryDAO(); + $category = $dao->searchById(substr($query['get'], 2)); + $this->view->query_get[$key] = array( + 'type' => 'category', + 'name' => $category->name(), + ); + break; + case 'f': + $dao = new FreshRSS_FeedDAO(); + $feed = $dao->searchById(substr($query['get'], 2)); + $this->view->query_get[$key] = array( + 'type' => 'feed', + 'name' => $feed->name(), + ); + break; + case 's': + $this->view->query_get[$key] = array( + 'type' => 'favorite', + 'name' => 'favorite', + ); + break; + case 'a': + $this->view->query_get[$key] = array( + 'type' => 'all', + 'name' => 'all', + ); + break; } } } @@ -345,12 +365,14 @@ class FreshRSS_configure_Controller extends Minz_ActionController { public function addQueryAction () { $queries = $this->view->conf->queries; $query = Minz_Request::params(); + $query['name'] = Minz_Translate::t('query_number', count($queries) + 1); unset($query['output']); unset($query['token']); $queries[] = $query; $this->view->conf->_queries($queries); $this->view->conf->save(); - + + // Minz_Request::forward(array('params' => $query), true); Minz_Request::forward(array('c' => 'configure', 'a' => 'queries'), true); } } diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index b0ce70000..ffd20deca 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -223,7 +223,13 @@ class FreshRSS_Configuration { public function _queries ($values) { $this->data['queries'] = array(); foreach ($values as $value) { - $this->data['queries'][] = array_filter($value); + $value = array_filter($value); + $params = $value; + unset($params['name']); + unset($params['url']); + $value['url'] = Minz_Url::display(array('params' => $params)); + + $this->data['queries'][] = $value; } } public function _theme($value) { diff --git a/app/i18n/en.php b/app/i18n/en.php index afcc4b7ec..223b74010 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -16,27 +16,34 @@ return array ( 'feeds' => 'Feeds', 'shortcuts' => 'Shortcuts', 'queries' => 'User queries', - 'query-search' => 'Search for "%s"', - 'query-order-asc' => 'Display oldest articles first', - 'query-order-desc' => 'Display newest articles first', - 'query-get-category' => 'Display "%s" category', - 'query-get-feed' => 'Display "%s" feed', - 'query-state-0' => 'Display all articles', - 'query-state-1' => 'Display read articles', - 'query-state-2' => 'Display unread articles', - 'query-state-3' => 'Display all articles', - 'query-state-4' => 'Display favorite articles', - 'query-state-5' => 'Display read favorite articles', - 'query-state-6' => 'Display unread favorite articles', - 'query-state-7' => 'Display favorite articles', - 'query-state-8' => 'Display not favorite articles', - 'query-state-9' => 'Display read not favorite articles', - 'query-state-10' => 'Display unread not favorite articles', - 'query-state-11' => 'Display not favorite articles', - 'query-state-12' => 'Display all articles', - 'query-state-13' => 'Display read articles', - 'query-state-14' => 'Display unread articles', - 'query-state-15' => 'Display all articles', + 'query_search' => 'Search for "%s"', + 'query_order_asc' => 'Display oldest articles first', + 'query_order_desc' => 'Display newest articles first', + 'query_get_category' => 'Display "%s" category', + 'query_get_feed' => 'Display "%s" feed', + 'query_get_all' => 'Display all articles', + 'query_get_favorite' => 'Display favorite articles', + 'query_state_0' => 'Display all articles', + 'query_state_1' => 'Display read articles', + 'query_state_2' => 'Display unread articles', + 'query_state_3' => 'Display all articles', + 'query_state_4' => 'Display favorite articles', + 'query_state_5' => 'Display read favorite articles', + 'query_state_6' => 'Display unread favorite articles', + 'query_state_7' => 'Display favorite articles', + 'query_state_8' => 'Display not favorite articles', + 'query_state_9' => 'Display read not favorite articles', + 'query_state_10' => 'Display unread not favorite articles', + 'query_state_11' => 'Display not favorite articles', + 'query_state_12' => 'Display all articles', + 'query_state_13' => 'Display read articles', + 'query_state_14' => 'Display unread articles', + 'query_state_15' => 'Display all articles', + 'query_number' => 'Query n°%d', + 'add_query' => 'Add a query', + 'no_query' => 'You have not create user query yet.', + 'query_filter' => 'Filter applied:', + 'no_query_filter' => 'No filter', 'about' => 'About', 'stats' => 'Statistics', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 9cd1725dd..e85e66723 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -16,27 +16,34 @@ return array ( 'feeds' => 'Flux', 'shortcuts' => 'Raccourcis', 'queries' => 'Filtres utilisateurs', - 'query-search' => 'Chercher "%s"', - 'query-order-asc' => 'Afficher les articles les plus anciens en premier', - 'query-order-desc' => 'Afficher les articles les plus récents en premier', - 'query-get-category' => 'Afficher la catégorie "%s"', - 'query-get-feed' => 'Afficher le flux "%s"', - 'query-state-0' => 'Afficher tous les articles', - 'query-state-1' => 'Afficher les articles lus', - 'query-state-2' => 'Afficher les articles non lus', - 'query-state-3' => 'Afficher tous les articles', - 'query-state-4' => 'Afficher les articles favoris', - 'query-state-5' => 'Afficher les articles lus et favoris', - 'query-state-6' => 'Afficher les articles non lus et favoris', - 'query-state-7' => 'Afficher les articles favoris', - 'query-state-8' => 'Afficher les articles non favoris', - 'query-state-9' => 'Afficher les articles lus et non favoris', - 'query-state-10' => 'Afficher les articles non lus et non favoris', - 'query-state-11' => 'Afficher les articles non favoris', - 'query-state-12' => 'Afficher tous les articles', - 'query-state-13' => 'Afficher les articles lus', - 'query-state-14' => 'Afficher les articles non lus', - 'query-state-15' => 'Afficher tous les articles', + 'query_search' => 'Recherche de "%s"', + 'query_order_asc' => 'Afficher les articles les plus anciens en premier', + 'query_order_desc' => 'Afficher les articles les plus récents en premier', + 'query_get_category' => 'Afficher la catégorie "%s"', + 'query_get_feed' => 'Afficher le flux "%s"', + 'query_get_all' => 'Afficher tous les articles', + 'query_get_favorite' => 'Afficher les articles favoris', + 'query_state_0' => 'Afficher tous les articles', + 'query_state_1' => 'Afficher les articles lus', + 'query_state_2' => 'Afficher les articles non lus', + 'query_state_3' => 'Afficher tous les articles', + 'query_state_4' => 'Afficher les articles favoris', + 'query_state_5' => 'Afficher les articles lus et favoris', + 'query_state_6' => 'Afficher les articles non lus et favoris', + 'query_state_7' => 'Afficher les articles favoris', + 'query_state_8' => 'Afficher les articles non favoris', + 'query_state_9' => 'Afficher les articles lus et non favoris', + 'query_state_10' => 'Afficher les articles non lus et non favoris', + 'query_state_11' => 'Afficher les articles non favoris', + 'query_state_12' => 'Afficher tous les articles', + 'query_state_13' => 'Afficher les articles lus', + 'query_state_14' => 'Afficher les articles non lus', + 'query_state_15' => 'Afficher tous les articles', + 'query_number' => 'Filtre n°%d', + 'add_query' => 'Créer un filtre', + 'no_query' => 'Vous n’avez pas encore créé de filtre.', + 'query_filter' => 'Filtres appliqués :', + 'no_query_filter' => 'Aucun filtre appliqué', 'about' => 'À propos', 'stats' => 'Statistiques', diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index 8f8d436e1..817dae676 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -36,23 +36,6 @@ - - conf->queries as $query_conf): - $count++; - $name = $count; - if (isset($query_conf['name'])) { - $name = $query_conf['name']; - unset($query_conf['name']); - } - $url_user_query = array('c' => 'index', 'a' => 'index', 'params' => $query_conf); ?> -
  • -
    - -
    -
  • - cat_aside as $cat) { diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index aadaadff9..a9cf02388 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -7,18 +7,20 @@ loginOk) { ?> - url; - if ($this->state & FreshRSS_Entry::STATE_READ) { - $url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_READ; - $checked = 'true'; - $class = 'active'; - } else { - $url_state['params']['state'] = $this->state | FreshRSS_Entry::STATE_READ; - $checked = 'false'; - $class = ''; - } - ?>
    + url; + + if ($this->state & FreshRSS_Entry::STATE_READ) { + $url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_READ; + $checked = 'true'; + $class = 'active'; + } else { + $url_state['params']['state'] = $this->state | FreshRSS_Entry::STATE_READ; + $checked = 'false'; + $class = ''; + } + ?> + state & FreshRSS_Entry::STATE_NOT_READ) { $url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_NOT_READ; @@ -44,6 +47,7 @@ title=""> + state & FreshRSS_Entry::STATE_FAVORITE) { $url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_FAVORITE; @@ -62,6 +66,7 @@ title=""> + state & FreshRSS_Entry::STATE_NOT_FAVORITE) { $url_state['params']['state'] = $this->state & ~FreshRSS_Entry::STATE_NOT_FAVORITE; @@ -80,6 +85,34 @@ title=""> + +
    " title=""> - - loginOk) { - $url_query = $this->url; - $url_query['c'] = 'configure'; - $url_query['a'] = 'addQuery'; - ?> - - loginOk || Minz_Configuration::allowAnonymousRefresh()) { ?> diff --git a/app/views/configure/queries.phtml b/app/views/configure/queries.phtml index f0c551742..d0aec687b 100644 --- a/app/views/configure/queries.phtml +++ b/app/views/configure/queries.phtml @@ -1,45 +1,90 @@ -partial ('aside_configure'); ?> +partial('aside_configure'); ?>
    - - -
    - - - conf->queries as $key => $query):?> -
    - -
    - "/> - "/> - "/> - "/> - "/> - + + + + + + conf->queries as $key => $query) { ?> +
    + + +
    + "/> + "/> + "/> + "/> + +
    + + + + + + + + + +
    + + + + +
    +
    +
    + +
    +
    +
      - -
    • - - -
    • - - -
    • - - -
    • query_get[$key]['type'], $this->query_get[$key]['name']); ?>
    • - + +
    • + + + +
    • + + + +
    • + + + +
    • query_get[$key]['type'], $this->query_get[$key]['name']); ?>
    • +
    +
    - - +
    + + + conf->queries) > 0) { ?>
    + +

    +
    \ No newline at end of file diff --git a/app/views/configure/sharing.phtml b/app/views/configure/sharing.phtml index a952bc3b4..02ce331da 100644 --- a/app/views/configure/sharing.phtml +++ b/app/views/configure/sharing.phtml @@ -4,35 +4,35 @@
    + data-simple='
    ' - data-advanced='
    + data-advanced='
    -
    +
    '> conf->sharing as $key => $sharing): ?> conf->shares[$sharing['type']]; ?> -
    +
    ' /> - +
    - +
    - +
    diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 7e3c59990..755784522 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -28,6 +28,9 @@ class Minz_Request { return self::$params; } static function htmlspecialchars_utf8 ($p) { + if (is_array($p)) { + return array_map('self::htmlspecialchars_utf8', $p); + } return htmlspecialchars($p, ENT_COMPAT, 'UTF-8'); } public static function param ($key, $default = false, $specialchars = false) { @@ -35,8 +38,6 @@ class Minz_Request { $p = self::$params[$key]; if(is_object($p) || $specialchars) { return $p; - } elseif(is_array($p)) { - return array_map('self::htmlspecialchars_utf8', $p); } else { return self::htmlspecialchars_utf8($p); } diff --git a/p/scripts/main.js b/p/scripts/main.js index 079ae0da4..5a3b25c3f 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -975,11 +975,6 @@ function init_print_action() { function init_share_observers() { shares = $('.form-group:not(".form-actions")').length; - $('.post').on('click', '.share.remove', function(e) { - e.preventDefault(); - $(this).parents('.form-group').remove(); - }); - $('.share.add').on('click', function(e) { var opt = $(this).siblings('select').find(':selected'); var row = $(this).parents('form').data(opt.data('form')); @@ -994,10 +989,16 @@ function init_share_observers() { }); } -function init_queries_observers() { - $('.post').on('click', '.query.remove', function(e) { - e.preventDefault(); - $(this).parents('.form-group').remove(); +function init_remove_observers() { + $('.post').on('click', 'a.remove', function(e) { + var remove_what = $(this).attr('data-remove'); + + if (remove_what !== undefined) { + var remove_obj = $('#' + remove_what); + remove_obj.remove(); + } + + return false; }); } @@ -1061,7 +1062,7 @@ function init_all() { window.setInterval(refreshUnreads, 120000); } else { init_share_observers(); - init_queries_observers(); + init_remove_observers(); init_feed_observers(); init_password_observers(); } diff --git a/p/themes/Origine/origine.css b/p/themes/Origine/origine.css index 1835e9ff9..cd40dc509 100644 --- a/p/themes/Origine/origine.css +++ b/p/themes/Origine/origine.css @@ -362,6 +362,10 @@ a.btn { padding: 0 25px; line-height: 2.5em; } +.dropdown-menu > .item > span { + padding: 0 25px; + line-height: 2em; +} .dropdown-menu > .item:hover { background: #0062BE; color: #fff; @@ -400,7 +404,7 @@ a.btn { font-size: 0.9em; } .alert-head { - font-size: 1.2em; + font-size: 1.15em; } .alert > a { color: inherit; diff --git a/p/themes/Origine/template.css b/p/themes/Origine/template.css index f68fdfca3..09ecaf685 100644 --- a/p/themes/Origine/template.css +++ b/p/themes/Origine/template.css @@ -180,7 +180,8 @@ a.btn { .dropdown-menu > .item { display: block; } -.dropdown-menu > .item > a { +.dropdown-menu > .item > a, +.dropdown-menu > .item > span { display: block; } .dropdown-menu > .item[aria-checked="true"] > a:before { @@ -220,10 +221,16 @@ a.btn { display: block; width: 90%; } +.group-controls .alert { + width: 100% +} .alert-head { margin: 0; font-weight: bold; } +.alert ul { + margin: 5px 20px; +} /*=== Icons */ .icon { @@ -587,7 +594,9 @@ a.btn { .aside .btn-important, .aside .feeds .dropdown, .flux_header .item.website span, - .item.date, .day .date { + .item.date, .day .date, + .dropdown-menu > .no-mobile, + .no-mobile { display: none; } .nav-login { -- cgit v1.2.3 From 09602acc5f754c3e814b4b7de9d9d4d283ed45ff Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 15 Jun 2014 17:49:23 +0200 Subject: Add two wrappers (_t() and _i()) - _t() is a wrapper for Minz_Translate::t() - Improve coding style of Translate.php - _i() is a wrapper for FreshRSS::icon() - queries.phtml shows how they work - It is a lot easier to read files with these functions :) --- app/Models/Themes.php | 4 ++++ app/views/configure/queries.phtml | 28 ++++++++++++++-------------- lib/Minz/Translate.php | 34 +++++++++++++++++++++------------- 3 files changed, 39 insertions(+), 27 deletions(-) (limited to 'lib') diff --git a/app/Models/Themes.php b/app/Models/Themes.php index 2e2c3f9cd..b4e8f4ccc 100644 --- a/app/Models/Themes.php +++ b/app/Models/Themes.php @@ -110,3 +110,7 @@ class FreshRSS_Themes extends Minz_Model { '' . $alts[$name] . ''; } } + +function _i($icon, $url_only = false) { + return FreshRSS_Themes::icon($icon, $url_only); +} diff --git a/app/views/configure/queries.phtml b/app/views/configure/queries.phtml index d0aec687b..2895f584c 100644 --- a/app/views/configure/queries.phtml +++ b/app/views/configure/queries.phtml @@ -1,15 +1,15 @@ partial('aside_configure'); ?>
    - + - + conf->queries as $key => $query) { ?>
    @@ -27,11 +27,11 @@ /> - + - +
    @@ -46,27 +46,27 @@
    -
    +
    -
    +
      -
    • +
    • -
    • +
    • -
    • +
    • -
    • query_get[$key]['type'], $this->query_get[$key]['name']); ?>
    • +
    • query_get[$key]['type'], $this->query_get[$key]['name']); ?>
    @@ -78,12 +78,12 @@ conf->queries) > 0) { ?>
    - - + +
    -

    +

    diff --git a/lib/Minz/Translate.php b/lib/Minz/Translate.php index e14f783f7..df48350e9 100644 --- a/lib/Minz/Translate.php +++ b/lib/Minz/Translate.php @@ -18,28 +18,28 @@ class Minz_Translate { * $translates est le tableau de correspondance * $key => $traduction */ - private static $translates = array (); + private static $translates = array(); /** * Inclus le fichier de langue qui va bien * l'enregistre dans $translates */ - public static function init () { - $l = Minz_Configuration::language (); - self::$language = Minz_Session::param ('language', $l); + public static function init() { + $l = Minz_Configuration::language(); + self::$language = Minz_Session::param('language', $l); $l_path = APP_PATH . '/i18n/' . self::$language . '.php'; - if (file_exists ($l_path)) { - self::$translates = include ($l_path); + if (file_exists($l_path)) { + self::$translates = include($l_path); } } /** * Alias de init */ - public static function reset () { - self::init (); + public static function reset() { + self::init(); } /** @@ -48,24 +48,32 @@ class Minz_Translate { * @return la valeur correspondante à la clé * > si non présente dans le tableau, on retourne la clé elle-même */ - public static function t ($key) { + public static function t($key) { $translate = $key; - if (isset (self::$translates[$key])) { + if (isset(self::$translates[$key])) { $translate = self::$translates[$key]; } - $args = func_get_args (); + $args = func_get_args(); unset($args[0]); - return vsprintf ($translate, $args); + return vsprintf($translate, $args); } /** * Retourne la langue utilisée actuellement * @return la langue */ - public static function language () { + public static function language() { return self::$language; } } + +function _t($key) { + $args = func_get_args(); + unset($args[0]); + array_unshift($args, $key); + + return call_user_func_array("Minz_Translate::t", $args); +} -- cgit v1.2.3 From 63c9c47a761b9925e6811ae630c3b77ef05b9fc8 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 1 Jul 2014 23:28:51 +0200 Subject: SimplePie HTTP 301 Moved Permanently Add support for HTTP 301 Moved Permanently in SimplePie FreshRSS will automatically update the address of a feed, only in this case. --- app/Controllers/feedController.php | 64 ++++++++++++++++++++------------------ app/Models/Feed.php | 41 ++++++++++++++---------- lib/SimplePie/SimplePie.php | 38 +++++++++++++++++----- lib/SimplePie/SimplePie/File.php | 12 +++++-- 4 files changed, 97 insertions(+), 58 deletions(-) (limited to 'lib') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index fce008399..5f5a40bc7 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -162,38 +162,39 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed', 'params' => $params), true); - } - - // GET request so we must ask confirmation to user - Minz_View::prependTitle(Minz_Translate::t('add_rss_feed') . ' · '); - $this->view->categories = $this->catDAO->listCategories(); - $this->view->feed = new FreshRSS_Feed($url); - try { - // We try to get some more information about the feed - $this->view->feed->load(true); - $this->view->load_ok = true; - } catch (Exception $e) { - $this->view->load_ok = false; - } + } else { - $feed = $feedDAO->searchByUrl($this->view->feed->url()); - if ($feed) { - // Already subscribe so we redirect to the feed configuration page - $notif = array( - 'type' => 'bad', - 'content' => Minz_Translate::t( - 'already_subscribed', $feed->name() - ) - ); - Minz_Session::_param('notification', $notif); + // GET request so we must ask confirmation to user + Minz_View::prependTitle(Minz_Translate::t('add_rss_feed') . ' · '); + $this->view->categories = $this->catDAO->listCategories(); + $this->view->feed = new FreshRSS_Feed($url); + try { + // We try to get some more information about the feed + $this->view->feed->load(true); + $this->view->load_ok = true; + } catch (Exception $e) { + $this->view->load_ok = false; + } - Minz_Request::forward(array( - 'c' => 'configure', - 'a' => 'feed', - 'params' => array( - 'id' => $feed->id() - ) - ), true); + $feed = $feedDAO->searchByUrl($this->view->feed->url()); + if ($feed) { + // Already subscribe so we redirect to the feed configuration page + $notif = array( + 'type' => 'bad', + 'content' => Minz_Translate::t( + 'already_subscribed', $feed->name() + ) + ); + Minz_Session::_param('notification', $notif); + + Minz_Request::forward(array( + 'c' => 'configure', + 'a' => 'feed', + 'params' => array( + 'id' => $feed->id() + ) + ), true); + } } } @@ -300,7 +301,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feedDAO->commit(); } $flux_update++; - if ($feed->url() !== $url) { //URL has changed (auto-discovery) + if (($feed->url() !== $url)) { //HTTP 301 Moved Permanently + Minz_Log::record('Feed ' . $url . ' moved permanently to ' . $feed->url(), Minz_Log::NOTICE); $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); } } catch (FreshRSS_Feed_Exception $e) { diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 757eacd59..14b52b143 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -17,6 +17,7 @@ class FreshRSS_Feed extends Minz_Model { private $error = false; private $keep_history = -2; private $hash = null; + private $lockPath = ''; public function __construct ($url, $validate=true) { if ($validate) { @@ -193,28 +194,35 @@ class FreshRSS_Feed extends Minz_Model { } $feed = customSimplePie(); $feed->set_feed_url ($url); + if (!$loadDetails) { //Only activates auto-discovery when adding a new feed + $feed->set_autodiscovery_level(SIMPLEPIE_LOCATOR_NONE); + } $mtime = $feed->init(); if ((!$mtime) || $feed->error()) { throw new FreshRSS_Feed_Exception ($feed->error() . ' [' . $url . ']'); } - // si on a utilisé l'auto-discover, notre url va avoir changé - $subscribe_url = $feed->subscribe_url (); - if ($subscribe_url !== null && $subscribe_url !== $this->url) { - if ($this->httpAuth != '') { - // on enlève les id si authentification HTTP - $subscribe_url = preg_replace ('#((.+)://)((.+)@)(.+)#', '${1}${5}', $subscribe_url); - } - $this->_url ($subscribe_url); - } - if ($loadDetails) { + // si on a utilisé l'auto-discover, notre url va avoir changé + $subscribe_url = $feed->subscribe_url(false); + $title = strtr(html_only_entity_decode($feed->get_title()), array('<' => '<', '>' => '>', '"' => '"')); //HTML to HTML-PRE //ENT_COMPAT except & $this->_name ($title == '' ? $this->url : $title); $this->_website(html_only_entity_decode($feed->get_link())); $this->_description(html_only_entity_decode($feed->get_description())); + } else { + //The case of HTTP 301 Moved Permanently + $subscribe_url = $feed->subscribe_url(true); + } + + if ($subscribe_url !== null && $subscribe_url !== $this->url) { + if ($this->httpAuth != '') { + // on enlève les id si authentification HTTP + $subscribe_url = preg_replace ('#((.+)://)((.+)@)(.+)#', '${1}${5}', $subscribe_url); + } + $this->_url ($subscribe_url); } if (($mtime === true) || ($mtime > $this->lastUpdate)) { @@ -288,20 +296,19 @@ class FreshRSS_Feed extends Minz_Model { } function lock() { - $lock = TMP_PATH . '/' . md5(Minz_Configuration::salt() . $this->url) . '.freshrss.lock'; - if (file_exists($lock) && ((time() - @filemtime($lock)) > 3600)) { - @unlink($lock); + $this->lockPath = TMP_PATH . '/' . md5(Minz_Configuration::salt() . $this->url) . '.freshrss.lock'; + if (file_exists($this->lockPath) && ((time() - @filemtime($this->lockPath)) > 3600)) { + @unlink($this->lockPath); } - if (($handle = @fopen($lock, 'x')) === false) { + if (($handle = @fopen($this->lockPath, 'x')) === false) { return false; } - //register_shutdown_function('unlink', $lock); + //register_shutdown_function('unlink', $this->lockPath); @fclose($handle); return true; } function unlock() { - $lock = TMP_PATH . '/' . md5(Minz_Configuration::salt() . $this->url) . '.freshrss.lock'; - @unlink($lock); + @unlink($this->lockPath); } } diff --git a/lib/SimplePie/SimplePie.php b/lib/SimplePie/SimplePie.php index 685fe1cc0..06c100f59 100644 --- a/lib/SimplePie/SimplePie.php +++ b/lib/SimplePie/SimplePie.php @@ -445,6 +445,13 @@ class SimplePie */ public $feed_url; + /** + * @var string Original feed URL, or new feed URL iff HTTP 301 Moved Permanently + * @see SimplePie::subscribe_url() + * @access private + */ + public $permanent_url = null; //FreshRSS + /** * @var object Instance of SimplePie_File to use as a feed * @see SimplePie::set_file() @@ -735,6 +742,7 @@ class SimplePie else { $this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1)); + $this->permanent_url = $this->feed_url; //FreshRSS } } @@ -749,6 +757,7 @@ class SimplePie if ($file instanceof SimplePie_File) { $this->feed_url = $file->url; + $this->permanent_url = $this->feed_url; //FreshRSS $this->file =& $file; return true; } @@ -1602,7 +1611,7 @@ class SimplePie } $this->raw_data = $file->body; - + $this->permanent_url = $file->permanent_url; //FreshRSS $headers = $file->headers; $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file)); $sniffed = $sniffer->get_type(); @@ -1788,26 +1797,39 @@ class SimplePie /** * Get the URL for the feed + * + * When the 'permanent' mode is enabled, returns the original feed URL, + * except in the case of an `HTTP 301 Moved Permanently` status response, + * in which case the location of the first redirection is returned. * - * May or may not be different from the URL passed to {@see set_feed_url()}, + * When the 'permanent' mode is disabled (default), + * may or may not be different from the URL passed to {@see set_feed_url()}, * depending on whether auto-discovery was used. * * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.) - * @todo If we have a perm redirect we should return the new URL - * @todo When we make the above change, let's support as well + * @todo Support * @todo Also, |atom:link|@rel=self + * @param bool $permanent Permanent mode to return only the original URL or the first redirection + * iff it is a 301 redirection * @return string|null */ - public function subscribe_url() + public function subscribe_url($permanent = false) { - if ($this->feed_url !== null) + if ($permanent) //FreshRSS { - return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); + if ($this->permanent_url !== null) + { + return $this->sanitize($this->permanent_url, SIMPLEPIE_CONSTRUCT_IRI); + } } else { - return null; + if ($this->feed_url !== null) + { + return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); + } } + return null; } /** diff --git a/lib/SimplePie/SimplePie/File.php b/lib/SimplePie/SimplePie/File.php index faf5dd1f1..b1bbe4420 100644 --- a/lib/SimplePie/SimplePie/File.php +++ b/lib/SimplePie/SimplePie/File.php @@ -64,6 +64,7 @@ class SimplePie_File var $redirects = 0; var $error; var $method = SIMPLEPIE_FILE_SOURCE_NONE; + var $permanent_url; //FreshRSS public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) { @@ -74,6 +75,7 @@ class SimplePie_File $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); } $this->url = $url; + $this->permanent_url = $url; //FreshRSS $this->useragent = $useragent; if (preg_match('/^http(s)?:\/\//i', $url)) { @@ -142,7 +144,10 @@ class SimplePie_File { $this->redirects++; $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); - return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + $previousStatusCode = $this->status_code; + $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + $this->permanent_url = ($previousStatusCode == 301) ? $location : $url; //FreshRSS + return; } } } @@ -224,7 +229,10 @@ class SimplePie_File { $this->redirects++; $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); - return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + $previousStatusCode = $this->status_code; + $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + $this->permanent_url = ($previousStatusCode == 301) ? $location : $url; //FreshRSS + return; } if (isset($this->headers['content-encoding'])) { -- cgit v1.2.3 From 32750182382ab0f49ef6fb9aa0ebc1f3d69493ae Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 3 Jul 2014 00:43:02 +0200 Subject: Cleaning some hash functions --- app/Models/Feed.php | 4 ++-- lib/lib_rss.php | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 14b52b143..ba142c8c8 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -105,7 +105,7 @@ class FreshRSS_Feed extends Minz_Model { $file = DATA_PATH . '/favicons/' . $this->hash() . '.txt'; if (!file_exists ($file)) { $t = $this->website; - if (empty($t)) { + if ($t == '') { $t = $this->url; } file_put_contents($file, $t); @@ -296,7 +296,7 @@ class FreshRSS_Feed extends Minz_Model { } function lock() { - $this->lockPath = TMP_PATH . '/' . md5(Minz_Configuration::salt() . $this->url) . '.freshrss.lock'; + $this->lockPath = TMP_PATH . '/' . $this->hash() . '.freshrss.lock'; if (file_exists($this->lockPath) && ((time() - @filemtime($this->lockPath)) > 3600)) { @unlink($this->lockPath); } diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 0f8161129..1b1e4b021 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -56,12 +56,6 @@ function checkUrl($url) { } } -// tiré de Shaarli de Seb Sauvage //Format RFC 4648 base64url -function small_hash ($txt) { - $t = rtrim (base64_encode (hash ('crc32', $txt, true)), '='); - return strtr ($t, '+/', '-_'); -} - function formatNumber($n, $precision = 0) { return str_replace(' ', ' ', //Espace insécable //TODO: remplacer par une espace _fine_ insécable number_format($n, $precision, '.', ' ')); //number_format does not seem to be Unicode-compatible -- cgit v1.2.3 From d6f414108667f32fe2b480adeb7ec9c218db2f4a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 3 Jul 2014 21:26:30 +0200 Subject: Preparation for SQLite https://github.com/marienfressinaud/FreshRSS/issues/100 --- app/Controllers/configureController.php | 2 +- app/Controllers/entryController.php | 6 +- app/Controllers/feedController.php | 4 +- app/Controllers/importExportController.php | 2 +- app/Controllers/indexController.php | 2 +- app/Controllers/usersController.php | 6 +- app/Models/CategoryDAO.php | 12 ++-- app/Models/Entry.php | 2 +- app/Models/EntryDAO.php | 101 +++++++++++++++++------------ app/Models/EntryDAO_SQLite.php | 42 ++++++++++++ app/Models/Factory.php | 13 ++++ app/Models/FeedDAO.php | 46 ++++++------- app/Models/UserDAO.php | 31 ++++++--- app/SQL/install.sql.mysql.php | 60 +++++++++++++++++ app/SQL/install.sql.sqlite.php | 57 ++++++++++++++++ app/sql.php | 58 ----------------- lib/Minz/Configuration.php | 80 ++++++++++++++--------- lib/Minz/ModelPdo.php | 52 +++++++++------ 18 files changed, 375 insertions(+), 201 deletions(-) create mode 100644 app/Models/EntryDAO_SQLite.php create mode 100644 app/Models/Factory.php create mode 100644 app/SQL/install.sql.mysql.php create mode 100644 app/SQL/install.sql.sqlite.php delete mode 100644 app/sql.php (limited to 'lib') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 89130cae4..cb8e6528f 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -291,7 +291,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_View::prependTitle(Minz_Translate::t('archiving_configuration') . ' · '); - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); $this->view->nb_total = $entryDAO->count(); $this->view->size_user = $entryDAO->size(); diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index bbcb990f5..c2d897cf3 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -43,7 +43,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $nextGet = Minz_Request::param ('nextGet', $get); $idMax = Minz_Request::param ('idMax', 0); - $entryDAO = new FreshRSS_EntryDAO (); + $entryDAO = FreshRSS_Factory::createEntryDao(); if ($id == false) { if (!$get) { $entryDAO->markReadEntries ($idMax); @@ -85,7 +85,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $id = Minz_Request::param ('id'); if ($id) { - $entryDAO = new FreshRSS_EntryDAO (); + $entryDAO = FreshRSS_Factory::createEntryDao(); $entryDAO->markFavorite ($id, (bool)(Minz_Request::param ('is_favorite', true))); } } @@ -97,7 +97,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { // La table des entrées a tendance à grossir énormément // Cette action permet d'optimiser cette table permettant de grapiller un peu de place // Cette fonctionnalité n'est à appeler qu'occasionnellement - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); $entryDAO->optimizeTable(); $feedDAO = new FreshRSS_FeedDAO(); diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 5f5a40bc7..149557a48 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -102,7 +102,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0; - $entryDAO = new FreshRSS_EntryDAO (); + $entryDAO = FreshRSS_Factory::createEntryDao(); $entries = array_reverse($feed->entries()); //We want chronological order and SimplePie uses reverse order // on calcule la date des articles les plus anciens qu'on accepte @@ -217,7 +217,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { @set_time_limit(300); $feedDAO = new FreshRSS_FeedDAO (); - $entryDAO = new FreshRSS_EntryDAO (); + $entryDAO = FreshRSS_Factory::createEntryDao(); Minz_Session::_param('actualize_feeds', false); $id = Minz_Request::param ('id'); diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 3cd791781..12154d9b6 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -12,7 +12,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { require_once(LIB_PATH . '/lib_opml.php'); $this->catDAO = new FreshRSS_CategoryDAO(); - $this->entryDAO = new FreshRSS_EntryDAO(); + $this->entryDAO = FreshRSS_Factory::createEntryDao(); $this->feedDAO = new FreshRSS_FeedDAO(); } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index c3f39fbe5..46c9518c9 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -45,7 +45,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } $catDAO = new FreshRSS_CategoryDAO(); - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); $this->view->cat_aside = $catDAO->listCategories (); $this->view->nb_favorites = $entryDAO->countUnreadReadFavorites (); diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index 38b8f829b..35fa3675f 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -99,7 +99,8 @@ class FreshRSS_users_Controller extends Minz_ActionController { public function createAction() { if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { - require_once(APP_PATH . '/sql.php'); + $db = Minz_Configuration::dataBase(); + require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php'); $new_user_language = Minz_Request::param('new_user_language', $this->view->conf->language); if (!in_array($new_user_language, $this->view->conf->availableLanguages())) { @@ -170,7 +171,8 @@ class FreshRSS_users_Controller extends Minz_ActionController { public function deleteAction() { if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { - require_once(APP_PATH . '/sql.php'); + $db = Minz_Configuration::dataBase(); + require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php'); $username = Minz_Request::param('username'); $ok = ctype_alnum($username); diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 6a9b839b9..3003dea0d 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -12,8 +12,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $this->bd->lastInsertId(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error addCategory: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -43,8 +43,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error updateCategory: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -58,8 +58,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error deleteCategory: ' . $info[2], Minz_Log::ERROR); return false; } } diff --git a/app/Models/Entry.php b/app/Models/Entry.php index fa9066d5b..a24a902d5 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -154,7 +154,7 @@ class FreshRSS_Entry extends Minz_Model { // Gestion du contenu // On cherche à récupérer les articles en entier... même si le flux ne le propose pas if ($pathEntries) { - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); $entry = $entryDAO->searchByGuid($this->feed, $this->guid); if($entry) { diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 4e24541dc..9ba0d2128 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -1,9 +1,18 @@ prefix . 'entry`(id, guid, title, author, content_bin, link, date, is_read, is_favorite, id_feed, tags) ' - . 'VALUES(?, ?, ?, ?, COMPRESS(?), ?, ?, ?, ?, ?, ?)'; + $sql = 'INSERT INTO `' . $this->prefix . 'entry`(id, guid, title, author, ' + . ($this->isCompressed() ? 'content_bin' : 'content') + . ', link, date, is_read, is_favorite, id_feed, tags) ' + . 'VALUES(?, ?, ?, ?, ' + . ($this->isCompressed() ? 'COMPRESS(?)' : '?') + . ', ?, ?, ?, ?, ?, ?)'; $stm = $this->bd->prepare ($sql); $values = array ( @@ -23,9 +32,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $this->bd->lastInsertId(); } else { - $info = $stm->errorInfo(); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); if ((int)($info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries - Minz_Log::record ('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] + Minz_Log::record('SQL error addEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] . ' while adding entry in feed ' . $valuesTmp['id_feed'] . ' with title: ' . $valuesTmp['title'], Minz_Log::ERROR); } /*else { Minz_Log::record ('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] @@ -78,14 +87,14 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markFavorite: ' . $info[2], Minz_Log::ERROR); return false; } } public function markRead($ids, $is_read = true) { - if (is_array($ids)) { + if (is_array($ids)) { //Many IDs at once if (count($ids) < 6) { //Speed heuristics $affected = 0; foreach ($ids as $id) { @@ -102,8 +111,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $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); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack(); return false; } @@ -121,8 +130,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { . '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); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack(); return false; } @@ -134,14 +143,14 @@ 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 = ?,' . 'f.cache_nbUnreads=f.cache_nbUnreads' . ($is_read ? '-' : '+') . '1 ' - . 'WHERE e.id=?'; - $values = array($is_read ? 1 : 0, $ids); + . 'WHERE e.id=? AND e.is_read<>?'; + $values = array($is_read ? 1 : 0, $ids, $is_read ? 1 : 0); $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); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -161,8 +170,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ()) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR); return false; } } else { @@ -179,8 +188,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array ($idMax); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -198,8 +207,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0)'; $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ())) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -220,8 +229,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR); return false; } } else { @@ -233,8 +242,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array ($id, $idMax); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -254,8 +263,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array ($id); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -278,8 +287,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCatName: ' . $info[2], Minz_Log::ERROR); return false; } } else { @@ -293,8 +302,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $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); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCatName: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack(); return false; } @@ -315,8 +324,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $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); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCatName: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack(); return false; } @@ -337,8 +346,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR); return false; } } else { @@ -350,8 +359,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array ($id, $idMax); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -364,8 +373,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $values = array ($id); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -378,7 +387,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { public function searchByGuid ($feed_id, $id) { // un guid est unique pour un flux donné - $sql = 'SELECT id, guid, title, author, UNCOMPRESS(content_bin) AS content, link, date, is_read, is_favorite, id_feed, tags ' + $sql = 'SELECT id, guid, title, author, ' + . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') + . ', link, date, is_read, is_favorite, id_feed, tags ' . 'FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid=?'; $stm = $this->bd->prepare ($sql); @@ -394,7 +405,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } public function searchById ($id) { - $sql = 'SELECT id, guid, title, author, UNCOMPRESS(content_bin) AS content, link, date, is_read, is_favorite, id_feed, tags ' + $sql = 'SELECT id, guid, title, author, ' + . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') + . ', link, date, is_read, is_favorite, id_feed, tags ' . 'FROM `' . $this->prefix . 'entry` WHERE id=?'; $stm = $this->bd->prepare ($sql); @@ -520,7 +533,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $search .= 'AND e1.tags LIKE ? '; $values[] = '%' . $word .'%'; } else { - $search .= 'AND CONCAT(e1.title, UNCOMPRESS(e1.content_bin)) LIKE ? '; + $search .= 'AND CONCAT(e1.title, ' . ($this->isCompressed() ? 'UNCOMPRESS(content_bin)' : 'content') . ') LIKE ? '; $values[] = '%' . $word .'%'; } } @@ -539,7 +552,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { public function listWhere($type = 'a', $id = '', $state = FreshRSS_Entry::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 ' + $sql = 'SELECT e.id, e.guid, e.title, e.author, ' + . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') + . ', e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags ' . 'FROM `' . $this->prefix . 'entry` e ' . 'INNER JOIN (' . $sql diff --git a/app/Models/EntryDAO_SQLite.php b/app/Models/EntryDAO_SQLite.php new file mode 100644 index 000000000..f148f3c63 --- /dev/null +++ b/app/Models/EntryDAO_SQLite.php @@ -0,0 +1,42 @@ +markRead($id, $is_read); + } + return $affected; + } + } else { + $this->bd->beginTransaction(); + $sql = 'UPDATE `' . $this->prefix . 'entry` e SET e.is_read = ? WHERE e.id=? AND e.is_read<>?'; + $values = array($is_read ? 1 : 0, $ids, $is_read ? 1 : 0); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute ($values))) { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } + $affected = $stm->rowCount(); + if ($affected > 0) { + $sql = 'UPDATE `' . $this->prefix . 'feed` f SET f.cache_nbUnreads=f.cache_nbUnreads' . ($is_read ? '-' : '+') . '1 ' + . 'WHERE f.id=(SELECT e.id_feed FROM `' . $this->prefix . 'entry` e WHERE e.id=?)'; + $values = array($ids); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute ($values))) { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } + } + $this->bd->commit(); + return $affected; + } + } +} diff --git a/app/Models/Factory.php b/app/Models/Factory.php new file mode 100644 index 000000000..bea89c114 --- /dev/null +++ b/app/Models/Factory.php @@ -0,0 +1,13 @@ +execute ($values)) { return $this->bd->lastInsertId(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error addFeed: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -76,8 +76,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error updateFeed: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -106,8 +106,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error updateLastUpdate: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -130,8 +130,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error changeCategory: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -155,8 +155,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error deleteFeed: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -181,8 +181,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ($values)) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error deleteFeedByCategory: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -301,8 +301,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute()) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error updateCachedValues: ' . $info[2], Minz_Log::ERROR); return false; } } @@ -313,11 +313,11 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $values = array($id); $this->bd->beginTransaction (); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); - $this->bd->rollBack (); - return false; - } + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack (); + return false; + } $affected = $stm->rowCount(); $sql = 'UPDATE `' . $this->prefix . 'feed` f ' @@ -325,8 +325,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $values = array ($id); $stm = $this->bd->prepare ($sql); if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR); $this->bd->rollBack (); return false; } @@ -350,8 +350,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($stm && $stm->execute ()) { return $stm->rowCount(); } else { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error cleanOldEntries: ' . $info[2], Minz_Log::ERROR); return false; } } diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index a25b57f89..dcf847a62 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -2,33 +2,44 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { public function createUser($username) { - require_once(APP_PATH . '/sql.php'); $db = Minz_Configuration::dataBase(); + require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php'); + + if (defined('SQL_CREATE_TABLES')) { + $sql = sprintf(SQL_CREATE_TABLES, $db['prefix'] . $username . '_', Minz_Translate::t('default_category')); + $stm = $c->prepare($sql); + $ok = $stm && $stm->execute(); + } else { + global $SQL_CREATE_TABLES; + if (is_array($SQL_CREATE_TABLES)) { + $ok = true; + foreach ($SQL_CREATE_TABLES as $instruction) { + $sql = sprintf($instruction, '', Minz_Translate::t('default_category')); + $stm = $c->prepare($sql); + $ok &= ($stm && $stm->execute()); + } + } + } - $sql = sprintf(SQL_CREATE_TABLES, $db['prefix'] . $username . '_'); - $stm = $this->bd->prepare($sql, array(PDO::ATTR_EMULATE_PREPARES => true)); - $values = array( - 'catName' => Minz_Translate::t('default_category'), - ); - if ($stm && $stm->execute($values)) { + if ($ok) { return true; } else { - $info = $stm->errorInfo(); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); return false; } } public function deleteUser($username) { - require_once(APP_PATH . '/sql.php'); $db = Minz_Configuration::dataBase(); + require_once(APP_PATH . '/SQL/sql.' . $db['type'] . '.php'); $sql = sprintf(SQL_DROP_TABLES, $db['prefix'] . $username . '_'); $stm = $this->bd->prepare($sql); if ($stm && $stm->execute()) { return true; } else { - $info = $stm->errorInfo(); + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); return false; } diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php new file mode 100644 index 000000000..d509cb4a8 --- /dev/null +++ b/app/SQL/install.sql.mysql.php @@ -0,0 +1,60 @@ +bd = self::$sharedBd; $this->prefix = self::$sharedPrefix; return; } - $db = Minz_Configuration::dataBase (); - $driver_options = null; + $db = Minz_Configuration::dataBase(); try { $type = $db['type']; - if($type == 'mysql') { - $string = $type - . ':host=' . $db['host'] + if ($type === 'mysql') { + $string = 'mysql:host=' . $db['host'] . ';dbname=' . $db['base'] . ';charset=utf8'; $driver_options = array( - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', + ); + $this->prefix = $db['prefix'] . Minz_Session::param('currentUser', '_') . '_'; + } elseif ($type === 'sqlite') { + $string = 'sqlite:' . DATA_PATH . '/' . Minz_Session::param('currentUser', '_') . '.sqlite'; + $driver_options = array( + //PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + $this->prefix = ''; + } else { + throw new Minz_PDOConnectionException( + 'Invalid database type!', + $db['user'], Minz_Exception::ERROR ); - } elseif($type == 'sqlite') { - $string = $type . ':/' . DATA_PATH . $db['base'] . '.sqlite'; //TODO: DEBUG UTF-8 http://www.siteduzero.com/forum/sujet/sqlite-connexion-utf-8-18797 } + self::$sharedDbType = $type; + self::$sharedPrefix = $this->prefix; - $this->bd = new FreshPDO ( + $this->bd = new FreshPDO( $string, $db['user'], $db['password'], $driver_options ); self::$sharedBd = $this->bd; - - $this->prefix = $db['prefix'] . Minz_Session::param('currentUser', '_') . '_'; - self::$sharedPrefix = $this->prefix; } catch (Exception $e) { - throw new Minz_PDOConnectionException ( + throw new Minz_PDOConnectionException( $string, $db['user'], Minz_Exception::ERROR ); @@ -81,15 +93,15 @@ class Minz_ModelPdo { } public function size($all = false) { - $db = Minz_Configuration::dataBase (); + $db = Minz_Configuration::dataBase(); $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = ?'; - $values = array ($db['base']); + $values = array($db['base']); if (!$all) { $sql .= ' AND table_name LIKE ?'; $values[] = $this->prefix . '%'; } - $stm = $this->bd->prepare ($sql); - $stm->execute ($values); + $stm = $this->bd->prepare($sql); + $stm->execute($values); $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); return $res[0]; } @@ -107,12 +119,12 @@ class FreshPDO extends PDO { } } - public function prepare ($statement, $driver_options = array()) { + public function prepare($statement, $driver_options = array()) { FreshPDO::check($statement); return parent::prepare($statement, $driver_options); } - public function exec ($statement) { + public function exec($statement) { FreshPDO::check($statement); return parent::exec($statement); } -- cgit v1.2.3 From 805c91da98c2f582e279f3c853fba9e43f572419 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 5 Jul 2014 01:52:41 +0200 Subject: Add support for SQLite https://github.com/marienfressinaud/FreshRSS/issues/100 Warning: MySQL has been changed too, so bugs may have been introduced --- CHANGELOG | 2 + README.md | 2 +- app/Models/CategoryDAO.php | 4 +- app/Models/EntryDAO.php | 526 +++++++++++++++++------------------------- app/Models/EntryDAOSQLite.php | 111 ++++++++- app/Models/FeedDAO.php | 218 ++++++++--------- app/Models/FeedDAOSQLite.php | 14 ++ lib/Minz/ModelPdo.php | 14 -- p/api/greader.php | 12 +- 9 files changed, 441 insertions(+), 462 deletions(-) (limited to 'lib') diff --git a/CHANGELOG b/CHANGELOG index 9325ee724..4ca0199e1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,8 @@ ## 2014-xx-xx FreshRSS 0.7.x +* SQL + * Add support for SQLite (beta) in addition to MySQL * SimplePie * Complies with HTTP "301 Moved Permanently" responses by automatically updating the URL of feeds that have changed address. diff --git a/README.md b/README.md index ce3c8ef32..fff08472b 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Privilégiez pour cela des demandes sur GitHub * PHP 5.2.1+ (PHP 5.3.7+ recommandé) * Requis : [PDO_MySQL](http://php.net/pdo-mysql), [cURL](http://php.net/curl), [LibXML](http://php.net/xml), [PCRE](http://php.net/pcre), [ctype](http://php.net/ctype) * Recommandés : [JSON](http://php.net/json), [zlib](http://php.net/zlib), [mbstring](http://php.net/mbstring), [iconv](http://php.net/iconv), [Zip](http://php.net/zip) -* MySQL 5.0.3+ (ou SQLite 3.7.4+ à venir) +* MySQL 5.0.3+ (recommandé) ou SQLite 3.7.4+ (en bêta) * Un navigateur Web récent tel Firefox 4+, Chrome, Opera, Safari, Internet Explorer 9+ * Fonctionne aussi sur mobile diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 3003dea0d..f11f87f47 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -102,7 +102,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { $sql = 'SELECT c.id AS c_id, c.name AS c_name, ' . ($details ? 'f.* ' : 'f.id, f.name, f.url, f.website, f.priority, f.error, f.cache_nbEntries, f.cache_nbUnreads ') . 'FROM `' . $this->prefix . 'category` c ' - . 'LEFT OUTER JOIN `' . $this->prefix . 'feed` f ON f.category = c.id ' + . 'LEFT OUTER JOIN `' . $this->prefix . 'feed` f ON f.category=c.id ' . 'GROUP BY f.id ' . 'ORDER BY c.name, f.name'; $stm = $this->bd->prepare ($sql); @@ -166,7 +166,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { } public function countNotRead ($id) { - $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE category=? AND e.is_read=0'; + $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE category=? AND e.is_read=0'; $stm = $this->bd->prepare ($sql); $values = array ($id); $stm->execute ($values); diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 9ba0d2128..1775af63c 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -6,16 +6,16 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return parent::$sharedDbType !== 'sqlite'; } - public function addEntry ($valuesTmp) { + public function addEntry($valuesTmp) { $sql = 'INSERT INTO `' . $this->prefix . 'entry`(id, guid, title, author, ' . ($this->isCompressed() ? 'content_bin' : 'content') . ', link, date, is_read, is_favorite, id_feed, tags) ' . 'VALUES(?, ?, ?, ?, ' . ($this->isCompressed() ? 'COMPRESS(?)' : '?') . ', ?, ?, ?, ?, ?, ?)'; - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); - $values = array ( + $values = array( $valuesTmp['id'], substr($valuesTmp['guid'], 0, 760), substr($valuesTmp['title'], 0, 255), @@ -29,7 +29,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { substr($valuesTmp['tags'], 0, 1023), ); - if ($stm && $stm->execute ($values)) { + if ($stm && $stm->execute($values)) { return $this->bd->lastInsertId(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); @@ -78,13 +78,13 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if (!is_array($ids)) { $ids = array($ids); } - $sql = 'UPDATE `' . $this->prefix . 'entry` e ' - . 'SET e.is_favorite = ? ' - . 'WHERE e.id IN (' . str_repeat('?,', count($ids) - 1). '?)'; - $values = array ($is_favorite ? 1 : 0); + $sql = 'UPDATE `' . $this->prefix . 'entry` ' + . 'SET is_favorite=? ' + . 'WHERE 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)) { + $stm = $this->bd->prepare($sql); + if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); @@ -93,8 +93,38 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } } + protected function updateCacheUnreads($catId = false, $feedId = false) { + $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 ' + . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0) ' + . 'WHERE 1'; + $values = array(); + if ($feedId !== false) { + $sql .= ' AND f.id=?'; + $values[] = $id; + } + if ($catId !== false) { + $sql .= ' AND f.category=?'; + $values[] = $catId; + } + $stm = $this->bd->prepare($sql); + if ($stm && $stm->execute($values)) { + return true; + } else { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error updateCacheUnreads: ' . $info[2], Minz_Log::ERROR); + return false; + } + } + public function markRead($ids, $is_read = true) { - if (is_array($ids)) { //Many IDs at once + if (is_array($ids)) { //Many IDs at once (used by API) if (count($ids) < 6) { //Speed heuristics $affected = 0; foreach ($ids as $id) { @@ -104,9 +134,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } $this->bd->beginTransaction(); - $sql = 'UPDATE `' . $this->prefix . 'entry` e ' - . 'SET e.is_read = ? ' - . 'WHERE e.id IN (' . str_repeat('?,', count($ids) - 1). '?)'; + $sql = 'UPDATE `' . $this->prefix . 'entry` ' + . 'SET is_read=? ' + . 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?)'; $values = array($is_read ? 1 : 0); $values = array_merge($values, $ids); $stm = $this->bd->prepare($sql); @@ -117,34 +147,18 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { 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 == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); - $this->bd->rollBack(); - return false; - } + if (($affected > 0) && (!$this->updateCacheUnreads(false, false))) { + $this->bd->rollBack(); + return false; } - $this->bd->commit(); return $affected; } else { - $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' - . 'SET e.is_read = ?,' + $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=? AND e.is_read<>?'; - $values = array($is_read ? 1 : 0, $ids, $is_read ? 1 : 0); + . 'WHERE e.id=? AND e.is_read=?'; + $values = array($is_read ? 1 : 0, $ids, $is_read ? 0 : 1); $stm = $this->bd->prepare($sql); if ($stm && $stm->execute($values)) { return $stm->rowCount(); @@ -156,267 +170,139 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } } - public function markReadEntries ($idMax = 0, $onlyFavorites = false, $priorityMin = 0) { + 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'; - 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 ()) { - return $stm->rowCount(); - } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadEntries: ' . $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 ' - . 'SET e.is_read = 1 ' - . '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); - if (!($stm && $stm->execute ($values))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadEntries: ' . $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 ' - . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0)'; - $stm = $this->bd->prepare ($sql); - if (!($stm && $stm->execute ())) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR); - $this->bd->rollBack (); - return false; - } - } - - $this->bd->commit (); - return $affected; + $idMax = time() . '000000'; + Minz_Log::record($nb . 'Calling markReadEntries(0) is deprecated!', Minz_Log::DEBUG); + } + $this->bd->beginTransaction(); + + $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 <= ?'; + 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); + if (!($stm && $stm->execute($values))) { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack(); + return false; + } + $affected = $stm->rowCount(); + if (($affected > 0) && (!$this->updateCacheUnreads(false, false))) { + $this->bd->rollBack(); + return false; } + $this->bd->commit(); + return $affected; } - public function markReadCat ($id, $idMax = 0) { + 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 ' - . 'SET e.is_read = 1, f.cache_nbUnreads=0 ' - . 'WHERE f.category = ? AND e.is_read = 0'; - $values = array ($id); - $stm = $this->bd->prepare ($sql); - if ($stm && $stm->execute ($values)) { - return $stm->rowCount(); - } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadCat: ' . $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 ' - . 'SET e.is_read = 1 ' - . 'WHERE f.category = ? AND e.is_read = 0 AND e.id <= ?'; - $values = array ($id, $idMax); - $stm = $this->bd->prepare ($sql); - if (!($stm && $stm->execute ($values))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadCat: ' . $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 ' - . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0) ' - . 'WHERE f.category = ?'; - $values = array ($id); - $stm = $this->bd->prepare ($sql); - if (!($stm && $stm->execute ($values))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR); - $this->bd->rollBack (); - return false; - } - } + $idMax = time() . '000000'; + Minz_Log::record($nb . 'Calling markReadCat(0) is deprecated!', Minz_Log::DEBUG); + } + $this->bd->beginTransaction(); - $this->bd->commit (); - return $affected; + $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id ' + . 'SET e.is_read=1 ' + . 'WHERE f.category=? AND e.is_read=0 AND e.id <= ?'; + $values = array($id, $idMax); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute($values))) { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack(); + return false; } + $affected = $stm->rowCount(); + if (($affected > 0) && (!$this->updateCacheUnreads($id, false))) { + $this->bd->rollBack(); + return false; + } + $this->bd->commit(); + return $affected; } - public function markReadCatName($name, $idMax = 0) { + 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 ' - . '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 == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadCatName: ' . $info[2], Minz_Log::ERROR); - return false; - } - } else { - $this->bd->beginTransaction(); + $idMax = time() . '000000'; + Minz_Log::record($nb . 'Calling markReadFeed(0) is deprecated!', Minz_Log::DEBUG); + } + $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); + $sql = 'UPDATE `' . $this->prefix . 'entry` ' + . 'SET is_read=1 ' + . 'WHERE id_feed=? AND is_read=0 AND id <= ?'; + $values = array($id, $idMax); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute($values))) { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack(); + return false; + } + $affected = $stm->rowCount(); + + if ($affected > 0) { + $sql = 'UPDATE `' . $this->prefix . 'feed` ' + . 'SET cache_nbUnreads=cache_nbUnreads-' . $affected + . ' WHERE id=?'; + $values = array($id); $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadCatName: ' . $info[2], Minz_Log::ERROR); + Minz_Log::record('SQL error markReadFeed: ' . $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 == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadCatName: ' . $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 ' - . 'SET e.is_read = 1, f.cache_nbUnreads=0 ' - . 'WHERE f.id=? AND e.is_read = 0'; - $values = array ($id); - $stm = $this->bd->prepare ($sql); - if ($stm && $stm->execute ($values)) { - return $stm->rowCount(); - } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadFeed: ' . $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 ' - . 'SET e.is_read = 1 ' - . 'WHERE f.id=? AND e.is_read = 0 AND e.id <= ?'; - $values = array ($id, $idMax); - $stm = $this->bd->prepare ($sql); - if (!($stm && $stm->execute ($values))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR); - $this->bd->rollBack (); - return false; - } - $affected = $stm->rowCount(); - - if ($affected > 0) { - $sql = 'UPDATE `' . $this->prefix . 'feed` f ' - . 'SET f.cache_nbUnreads=f.cache_nbUnreads-' . $affected - . ' WHERE f.id=?'; - $values = array ($id); - $stm = $this->bd->prepare ($sql); - if (!($stm && $stm->execute ($values))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR); - $this->bd->rollBack (); - return false; - } - } - - $this->bd->commit (); - return $affected; - } + $this->bd->commit(); + return $affected; } - public function searchByGuid ($feed_id, $id) { + public function searchByGuid($feed_id, $id) { // un guid est unique pour un flux donné $sql = 'SELECT id, guid, title, author, ' . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') . ', link, date, is_read, is_favorite, id_feed, tags ' . 'FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid=?'; - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); - $values = array ( + $values = array( $feed_id, $id ); - $stm->execute ($values); - $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $entries = self::daoToEntry ($res); - return isset ($entries[0]) ? $entries[0] : null; + $stm->execute($values); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + $entries = self::daoToEntry($res); + return isset($entries[0]) ? $entries[0] : null; } - public function searchById ($id) { + public function searchById($id) { $sql = 'SELECT id, guid, title, author, ' . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') . ', link, date, is_read, is_favorite, id_feed, tags ' . 'FROM `' . $this->prefix . 'entry` WHERE id=?'; - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); - $values = array ($id); + $values = array($id); - $stm->execute ($values); - $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $entries = self::daoToEntry ($res); - return isset ($entries[0]) ? $entries[0] : null; + $stm->execute($values); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + $entries = self::daoToEntry($res); + return isset($entries[0]) ? $entries[0] : null; + } + + protected function sqlConcat($s1, $s2) { + return 'CONCAT(' . $s1 . ',' . $s2 . ')'; //MySQL } private function sqlListWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { @@ -432,39 +318,39 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $joinFeed = true; break; case 's': //Deprecated: use $state instead - $where .= 'e1.is_favorite = 1 '; + $where .= 'e1.is_favorite=1 '; break; case 'c': - $where .= 'f.category = ? '; + $where .= 'f.category=? '; $values[] = intval($id); $joinFeed = true; break; case 'f': - $where .= 'e1.id_feed = ? '; + $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 . ']!'); + throw new FreshRSS_EntriesGetter_Exception('Bad type in Entry->listByType: [' . $type . ']!'); } if ($state & FreshRSS_Entry::STATE_NOT_READ) { if (!($state & FreshRSS_Entry::STATE_READ)) { - $where .= 'AND e1.is_read = 0 '; + $where .= 'AND e1.is_read=0 '; } } elseif ($state & FreshRSS_Entry::STATE_READ) { - $where .= 'AND e1.is_read = 1 '; + $where .= 'AND e1.is_read=1 '; } if ($state & FreshRSS_Entry::STATE_FAVORITE) { if (!($state & FreshRSS_Entry::STATE_NOT_FAVORITE)) { - $where .= 'AND e1.is_favorite = 1 '; + $where .= 'AND e1.is_favorite=1 '; } } elseif ($state & FreshRSS_Entry::STATE_NOT_FAVORITE) { - $where .= 'AND e1.is_favorite = 0 '; + $where .= 'AND e1.is_favorite=0 '; } switch ($order) { @@ -472,7 +358,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { case 'ASC': break; default: - throw new FreshRSS_EntriesGetter_Exception ('Bad order in Entry->listByType: [' . $order . ']!'); + throw new FreshRSS_EntriesGetter_Exception('Bad order in Entry->listByType: [' . $order . ']!'); } if ($firstId !== '') { $where .= 'AND e1.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; @@ -480,7 +366,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { if (($date_min > 0) && ($type !== 's')) { $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'; + $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 } @@ -533,7 +419,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $search .= 'AND e1.tags LIKE ? '; $values[] = '%' . $word .'%'; } else { - $search .= 'AND CONCAT(e1.title, ' . ($this->isCompressed() ? 'UNCOMPRESS(content_bin)' : 'content') . ') LIKE ? '; + $search .= 'AND ' . $this->sqlconcat('e1.title', $this->isCompressed() ? 'UNCOMPRESS(content_bin)' : 'content') . ' LIKE ? '; $values[] = '%' . $word .'%'; } } @@ -542,7 +428,7 @@ 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 ' : '') + . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e1.id_feed=f.id ' : '') . 'WHERE ' . $where . $search . 'ORDER BY e1.id ' . $order @@ -558,13 +444,13 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { . 'FROM `' . $this->prefix . 'entry` e ' . 'INNER JOIN (' . $sql - . ') e2 ON e2.id = e.id ' + . ') e2 ON e2.id=e.id ' . 'ORDER BY e.id ' . $order; - $stm = $this->bd->prepare ($sql); - $stm->execute ($values); + $stm = $this->bd->prepare($sql); + $stm->execute($values); - return self::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToEntry($stm->fetchAll(PDO::FETCH_ASSOC)); } public function listIdsWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { //For API @@ -578,69 +464,85 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { 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); - $values = array ($id); - $stm->execute ($values); - return $stm->fetchAll (PDO::FETCH_COLUMN, 0); + $stm = $this->bd->prepare($sql); + $values = array($id); + $stm->execute($values); + return $stm->fetchAll(PDO::FETCH_COLUMN, 0); } - public function countUnreadRead () { - $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE priority > 0' - . ' UNION SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE priority > 0 AND is_read = 0'; - $stm = $this->bd->prepare ($sql); - $stm->execute (); - $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0); + public function countUnreadRead() { + $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE priority > 0' + . ' UNION SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE priority > 0 AND is_read=0'; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); $all = empty($res[0]) ? 0 : $res[0]; $unread = empty($res[1]) ? 0 : $res[1]; return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread); } - public function count ($minPriority = null) { - $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id'; + public function count($minPriority = null) { + $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id'; if ($minPriority !== null) { $sql = ' WHERE priority > ' . intval($minPriority); } - $stm = $this->bd->prepare ($sql); - $stm->execute (); - $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0); + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); return $res[0]; } - public function countNotRead ($minPriority = null) { - $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE is_read = 0'; + public function countNotRead($minPriority = null) { + $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE is_read=0'; if ($minPriority !== null) { $sql = ' AND priority > ' . intval($minPriority); } - $stm = $this->bd->prepare ($sql); - $stm->execute (); - $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0); + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); return $res[0]; } - public function countUnreadReadFavorites () { - $sql = 'SELECT COUNT(id) FROM `' . $this->prefix . 'entry` WHERE is_favorite=1' - . ' UNION SELECT COUNT(id) FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 AND is_read = 0'; - $stm = $this->bd->prepare ($sql); - $stm->execute (); - $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0); + public function countUnreadReadFavorites() { + $sql = 'SELECT c FROM (' + . 'SELECT COUNT(id) AS c, 1 as o FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 ' + . 'UNION SELECT COUNT(id) AS c, 2 AS o FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 AND is_read=0' + . ') u ORDER BY o'; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); $all = empty($res[0]) ? 0 : $res[0]; $unread = empty($res[1]) ? 0 : $res[1]; return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread); } public function optimizeTable() { - $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`'; - $stm = $this->bd->prepare ($sql); - $stm->execute (); + $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`'; //MySQL + $stm = $this->bd->prepare($sql); + $stm->execute(); + } + + public function size($all = false) { + $db = Minz_Configuration::dataBase(); + $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema=?'; //MySQL + $values = array($db['base']); + if (!$all) { + $sql .= ' AND table_name LIKE ?'; + $values[] = $this->prefix . '%'; + } + $stm = $this->bd->prepare($sql); + $stm->execute($values); + $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); + return $res[0]; } - public static function daoToEntry ($listDAO) { - $list = array (); + public static function daoToEntry($listDAO) { + $list = array(); - if (!is_array ($listDAO)) { - $listDAO = array ($listDAO); + if (!is_array($listDAO)) { + $listDAO = array($listDAO); } foreach ($listDAO as $key => $dao) { - $entry = new FreshRSS_Entry ( + $entry = new FreshRSS_Entry( $dao['id_feed'], $dao['guid'], $dao['title'], @@ -652,13 +554,13 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $dao['is_favorite'], $dao['tags'] ); - if (isset ($dao['id'])) { - $entry->_id ($dao['id']); + if (isset($dao['id'])) { + $entry->_id($dao['id']); } $list[] = $entry; } - unset ($listDAO); + unset($listDAO); return $list; } diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 0a837b3aa..4680c3eaa 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -2,9 +2,38 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { + protected function sqlConcat($s1, $s2) { + return $s1 . '||' . $s2; + } + + protected function updateCacheUnreads($catId = false, $feedId = false) { + $sql = 'UPDATE `' . $this->prefix . 'feed` ' + . 'SET cache_nbUnreads=(' + . 'SELECT COUNT(*) AS nbUnreads FROM `' . $this->prefix . 'entry` e ' + . 'WHERE e.id_feed=feed.id AND e.is_read=0) ' + . 'WHERE 1'; + $values = array(); + if ($feedId !== false) { + $sql .= ' AND id=?'; + $values[] = $feedId; + } + if ($catId !== false) { + $sql .= ' AND category=?'; + $values[] = $catId; + } + $stm = $this->bd->prepare($sql); + if ($stm && $stm->execute($values)) { + return true; + } else { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error updateCacheUnreads: ' . $info[2], Minz_Log::ERROR); + return false; + } + } + public function markRead($ids, $is_read = true) { - if (is_array($ids)) { //Many IDs at once - if (true) { //Not supported yet in SQLite, so always call IDs one by one + if (is_array($ids)) { //Many IDs at once (used by API) + if (true) { //Speed heuristics //TODO: Not implemented yet for SQLite (so always call IDs one by one) $affected = 0; foreach ($ids as $id) { $affected += $this->markRead($id, $is_read); @@ -13,13 +42,13 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { } } else { $this->bd->beginTransaction(); - $sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=? WHERE id=? AND is_read<>?'; - $values = array($is_read ? 1 : 0, $ids, $is_read ? 1 : 0); + $sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=? WHERE id=? AND is_read=?'; + $values = array($is_read ? 1 : 0, $ids, $is_read ? 0 : 1); $stm = $this->bd->prepare($sql); - if (!($stm && $stm->execute ($values))) { + if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); Minz_Log::record('SQL error markRead 1: ' . $info[2], Minz_Log::ERROR); - $this->bd->rollBack (); + $this->bd->rollBack(); return false; } $affected = $stm->rowCount(); @@ -28,10 +57,10 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { . 'WHERE id=(SELECT e.id_feed FROM `' . $this->prefix . 'entry` e WHERE e.id=?)'; $values = array($ids); $stm = $this->bd->prepare($sql); - if (!($stm && $stm->execute ($values))) { + if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); Minz_Log::record('SQL error markRead 2: ' . $info[2], Minz_Log::ERROR); - $this->bd->rollBack (); + $this->bd->rollBack(); return false; } } @@ -39,4 +68,70 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { return $affected; } } + + public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0) { + if ($idMax == 0) { + $idMax = time() . '000000'; + Minz_Log::record($nb . 'Calling markReadEntries(0) is deprecated!', Minz_Log::DEBUG); + } + + $this->bd->beginTransaction(); + $sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=1 WHERE is_read=0 AND id <= ?'; + if ($onlyFavorites) { + $sql .= ' AND is_favorite=1'; + } elseif ($priorityMin >= 0) { + $sql .= ' AND id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.priority > ' . intval($priorityMin) . ')'; + } + $values = array($idMax); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute($values))) { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadEntries 1: ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack(); + return false; + } + $affected = $stm->rowCount(); + if (($affected > 0) && (!$this->updateCacheUnreads(false, false))) { + $this->bd->rollBack(); + return false; + } + $this->bd->commit(); + return $affected; + } + + public function markReadCat($id, $idMax = 0) { + if ($idMax == 0) { + $idMax = time() . '000000'; + Minz_Log::record($nb . 'Calling markReadCat(0) is deprecated!', Minz_Log::DEBUG); + } + $this->bd->beginTransaction(); + + $sql = 'UPDATE `' . $this->prefix . 'entry` ' + . 'SET is_read=1 ' + . 'WHERE is_read=0 AND id <= ? AND ' + . 'id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.category=?)'; + $values = array($idMax, $id); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute($values))) { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR); + $this->bd->rollBack(); + return false; + } + $affected = $stm->rowCount(); + if (($affected > 0) && (!$this->updateCacheUnreads($id, false))) { + $this->bd->rollBack(); + return false; + } + $this->bd->commit(); + return $affected; + } + + public function optimizeTable() { + //TODO: Search for an equivalent in SQLite + } + + public function size($all = false) { + return @filesize(DATA_PATH . '/' . Minz_Session::param('currentUser', '_') . '.sqlite'); + } } diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 9534d61bd..5281b371d 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -1,21 +1,21 @@ prefix . 'feed` (url, category, name, website, description, lastUpdate, priority, httpAuth, error, keep_history) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, -2)'; - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); - $values = array ( + $values = array( substr($valuesTmp['url'], 0, 511), $valuesTmp['category'], substr($valuesTmp['name'], 0, 255), substr($valuesTmp['website'], 0, 255), substr($valuesTmp['description'], 0, 1023), $valuesTmp['lastUpdate'], - base64_encode ($valuesTmp['httpAuth']), + base64_encode($valuesTmp['httpAuth']), ); - if ($stm && $stm->execute ($values)) { + if ($stm && $stm->execute($values)) { return $this->bd->lastInsertId(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); @@ -54,26 +54,26 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $feed_search->id(); } - public function updateFeed ($id, $valuesTmp) { + public function updateFeed($id, $valuesTmp) { $set = ''; foreach ($valuesTmp as $key => $v) { $set .= $key . '=?, '; if ($key == 'httpAuth') { - $valuesTmp[$key] = base64_encode ($v); + $valuesTmp[$key] = base64_encode($v); } } - $set = substr ($set, 0, -2); + $set = substr($set, 0, -2); $sql = 'UPDATE `' . $this->prefix . 'feed` SET ' . $set . ' WHERE id=?'; - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); foreach ($valuesTmp as $v) { $values[] = $v; } $values[] = $id; - if ($stm && $stm->execute ($values)) { + if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); @@ -82,11 +82,11 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } } - public function updateLastUpdate ($id, $inError = 0, $updateCache = true) { + public function updateLastUpdate($id, $inError = 0, $updateCache = true) { if ($updateCache) { $sql = 'UPDATE `' . $this->prefix . 'feed` ' //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE - . 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=feed.id),' - . 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=feed.id AND e2.is_read=0),' + . 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),' + . 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0),' . 'lastUpdate=?, error=? ' . 'WHERE id=?'; } else { @@ -95,15 +95,15 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { . 'WHERE id=?'; } - $values = array ( + $values = array( time(), $inError, $id, ); - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); - if ($stm && $stm->execute ($values)) { + if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); @@ -112,22 +112,22 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } } - public function changeCategory ($idOldCat, $idNewCat) { - $catDAO = new FreshRSS_CategoryDAO (); - $newCat = $catDAO->searchById ($idNewCat); + public function changeCategory($idOldCat, $idNewCat) { + $catDAO = new FreshRSS_CategoryDAO(); + $newCat = $catDAO->searchById($idNewCat); if (!$newCat) { - $newCat = $catDAO->getDefault (); + $newCat = $catDAO->getDefault(); } $sql = 'UPDATE `' . $this->prefix . 'feed` SET category=? WHERE category=?'; - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); - $values = array ( - $newCat->id (), + $values = array( + $newCat->id(), $idOldCat ); - if ($stm && $stm->execute ($values)) { + if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); @@ -136,23 +136,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } } - public function deleteFeed ($id) { - /*//For MYISAM (MySQL 5.5-) without FOREIGN KEY - $sql = 'DELETE FROM `' . $this->prefix . 'entry` WHERE id_feed=?'; - $stm = $this->bd->prepare ($sql); - $values = array ($id); - if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); - return false; - }*/ - + public function deleteFeed($id) { $sql = 'DELETE FROM `' . $this->prefix . 'feed` WHERE id=?'; - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); - $values = array ($id); + $values = array($id); - if ($stm && $stm->execute ($values)) { + if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); @@ -160,25 +150,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return false; } } - public function deleteFeedByCategory ($id) { - /*//For MYISAM (MySQL 5.5-) without FOREIGN KEY - $sql = 'DELETE FROM `' . $this->prefix . 'entry` e ' - . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' - . 'WHERE f.category=?'; - $stm = $this->bd->prepare ($sql); - $values = array ($id); - if (!($stm && $stm->execute ($values))) { - $info = $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); - return false; - }*/ - + public function deleteFeedByCategory($id) { $sql = 'DELETE FROM `' . $this->prefix . 'feed` WHERE category=?'; - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); - $values = array ($id); + $values = array($id); - if ($stm && $stm->execute ($values)) { + if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); @@ -187,52 +165,52 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } } - public function searchById ($id) { + public function searchById($id) { $sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE id=?'; - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); - $values = array ($id); + $values = array($id); - $stm->execute ($values); - $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $feed = self::daoToFeed ($res); + $stm->execute($values); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + $feed = self::daoToFeed($res); - if (isset ($feed[$id])) { + if (isset($feed[$id])) { return $feed[$id]; } else { return null; } } - public function searchByUrl ($url) { + public function searchByUrl($url) { $sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE url=?'; - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); - $values = array ($url); + $values = array($url); - $stm->execute ($values); - $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $feed = current (self::daoToFeed ($res)); + $stm->execute($values); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + $feed = current(self::daoToFeed($res)); - if (isset ($feed)) { + if (isset($feed)) { return $feed; } else { return null; } } - public function listFeeds () { + public function listFeeds() { $sql = 'SELECT * FROM `' . $this->prefix . 'feed` ORDER BY name'; - $stm = $this->bd->prepare ($sql); - $stm->execute (); + $stm = $this->bd->prepare($sql); + $stm->execute(); - return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC)); } 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 (); + $stm = $this->bd->prepare($sql); + $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_ASSOC); $feedCategoryNames = array(); foreach ($res as $line) { @@ -244,49 +222,49 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $feedCategoryNames; } - public function listFeedsOrderUpdate ($cacheDuration = 1500) { + public function listFeedsOrderUpdate($cacheDuration = 1500) { $sql = 'SELECT id, url, name, website, lastUpdate, pathEntries, httpAuth, keep_history ' . 'FROM `' . $this->prefix . 'feed` ' . 'WHERE lastUpdate < ' . (time() - intval($cacheDuration)) . ' ORDER BY lastUpdate'; - $stm = $this->bd->prepare ($sql); - $stm->execute (); + $stm = $this->bd->prepare($sql); + $stm->execute(); - return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC)); } - public function listByCategory ($cat) { + public function listByCategory($cat) { $sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE category=? ORDER BY name'; - $stm = $this->bd->prepare ($sql); + $stm = $this->bd->prepare($sql); - $values = array ($cat); + $values = array($cat); - $stm->execute ($values); + $stm->execute($values); - return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC)); } - public function countEntries ($id) { + public function countEntries($id) { $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` WHERE id_feed=?'; - $stm = $this->bd->prepare ($sql); - $values = array ($id); - $stm->execute ($values); - $res = $stm->fetchAll (PDO::FETCH_ASSOC); + $stm = $this->bd->prepare($sql); + $values = array($id); + $stm->execute($values); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); return $res[0]['count']; } - public function countNotRead ($id) { + 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); - $values = array ($id); - $stm->execute ($values); - $res = $stm->fetchAll (PDO::FETCH_ASSOC); + $stm = $this->bd->prepare($sql); + $values = array($id); + $stm->execute($values); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); return $res[0]['count']; } - public function updateCachedValues () { //For one single feed, call updateLastUpdate($id) + public function updateCachedValues() { //For one single feed, call updateLastUpdate($id) $sql = 'UPDATE `' . $this->prefix . 'feed` f ' . 'INNER JOIN (' . 'SELECT e.id_feed, ' @@ -296,7 +274,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { . '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); + $stm = $this->bd->prepare($sql); if ($stm && $stm->execute()) { return $stm->rowCount(); @@ -307,39 +285,39 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } } - public function truncate ($id) { - $sql = 'DELETE e.* FROM `' . $this->prefix . 'entry` e WHERE e.id_feed=?'; + public function truncate($id) { + $sql = 'DELETE FROM `' . $this->prefix . 'entry` WHERE id_feed=?'; $stm = $this->bd->prepare($sql); $values = array($id); - $this->bd->beginTransaction (); - if (!($stm && $stm->execute ($values))) { + $this->bd->beginTransaction(); + if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR); - $this->bd->rollBack (); + $this->bd->rollBack(); return false; } $affected = $stm->rowCount(); $sql = 'UPDATE `' . $this->prefix . 'feed` ' . 'SET cache_nbEntries=0, cache_nbUnreads=0 WHERE id=?'; - $values = array ($id); - $stm = $this->bd->prepare ($sql); - if (!($stm && $stm->execute ($values))) { + $values = array($id); + $stm = $this->bd->prepare($sql); + if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR); - $this->bd->rollBack (); + $this->bd->rollBack(); return false; } - $this->bd->commit (); + $this->bd->commit(); return $affected; } - public function cleanOldEntries ($id, $date_min, $keep = 15) { //Remember to call updateLastUpdate($id) just after - $sql = 'DELETE e.* FROM `' . $this->prefix . 'entry` e ' - . 'WHERE e.id_feed = :id_feed AND e.id <= :id_max AND e.is_favorite = 0 AND e.id NOT IN ' - . '(SELECT id FROM (SELECT e2.id FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed = :id_feed ORDER BY id DESC LIMIT :keep) keep)'; //Double select because of: MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' - $stm = $this->bd->prepare ($sql); + public function cleanOldEntries($id, $date_min, $keep = 15) { //Remember to call updateLastUpdate($id) just after + $sql = 'DELETE FROM `' . $this->prefix . 'entry` ' + . 'WHERE id_feed = :id_feed AND id <= :id_max AND is_favorite=0 AND id NOT IN ' + . '(SELECT id FROM (SELECT e2.id FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed = :id_feed ORDER BY id DESC LIMIT :keep) keep)'; //Double select MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' + $stm = $this->bd->prepare($sql); $id_max = intval($date_min) . '000000'; @@ -347,7 +325,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm->bindParam(':id_max', $id_max, PDO::PARAM_INT); $stm->bindParam(':keep', $keep, PDO::PARAM_INT); - if ($stm && $stm->execute ()) { + if ($stm && $stm->execute()) { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); @@ -356,18 +334,18 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } } - public static function daoToFeed ($listDAO, $catID = null) { - $list = array (); + public static function daoToFeed($listDAO, $catID = null) { + $list = array(); - if (!is_array ($listDAO)) { - $listDAO = array ($listDAO); + if (!is_array($listDAO)) { + $listDAO = array($listDAO); } foreach ($listDAO as $key => $dao) { - if (!isset ($dao['name'])) { + if (!isset($dao['name'])) { continue; } - if (isset ($dao['id'])) { + if (isset($dao['id'])) { $key = $dao['id']; } if ($catID === null) { @@ -384,13 +362,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $myFeed->_lastUpdate(isset($dao['lastUpdate']) ? $dao['lastUpdate'] : 0); $myFeed->_priority(isset($dao['priority']) ? $dao['priority'] : 10); $myFeed->_pathEntries(isset($dao['pathEntries']) ? $dao['pathEntries'] : ''); - $myFeed->_httpAuth(isset($dao['httpAuth']) ? base64_decode ($dao['httpAuth']) : ''); + $myFeed->_httpAuth(isset($dao['httpAuth']) ? base64_decode($dao['httpAuth']) : ''); $myFeed->_error(isset($dao['error']) ? $dao['error'] : 0); $myFeed->_keepHistory(isset($dao['keep_history']) ? $dao['keep_history'] : -2); $myFeed->_nbNotRead(isset($dao['cache_nbUnreads']) ? $dao['cache_nbUnreads'] : 0); $myFeed->_nbEntries(isset($dao['cache_nbEntries']) ? $dao['cache_nbEntries'] : 0); - if (isset ($dao['id'])) { - $myFeed->_id ($dao['id']); + if (isset($dao['id'])) { + $myFeed->_id($dao['id']); } $list[$key] = $myFeed; } diff --git a/app/Models/FeedDAOSQLite.php b/app/Models/FeedDAOSQLite.php index f3c92149e..bb75c860b 100644 --- a/app/Models/FeedDAOSQLite.php +++ b/app/Models/FeedDAOSQLite.php @@ -2,4 +2,18 @@ class FreshRSS_FeedDAOSQLite extends FreshRSS_FeedDAO { + public function updateCachedValues() { //For one single feed, call updateLastUpdate($id) + $sql = 'UPDATE `' . $this->prefix . 'feed` ' + . 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=feed.id),' + . 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=feed.id AND e2.is_read=0)'; + $stm = $this->bd->prepare($sql); + if ($stm && $stm->execute()) { + return $stm->rowCount(); + } else { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record('SQL error updateCachedValues: ' . $info[2], Minz_Log::ERROR); + return false; + } + } + } diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index cbbc0bebc..1f56f09c2 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -92,20 +92,6 @@ class Minz_ModelPdo { $this->bd->rollBack(); } - public function size($all = false) { - $db = Minz_Configuration::dataBase(); - $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = ?'; - $values = array($db['base']); - if (!$all) { - $sql .= ' AND table_name LIKE ?'; - $values[] = $this->prefix . '%'; - } - $stm = $this->bd->prepare($sql); - $stm->execute($values); - $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); - return $res[0]; - } - public static function clean() { self::$sharedBd = null; self::$sharedPrefix = ''; diff --git a/p/api/greader.php b/p/api/greader.php index 843cd93c4..7a961225f 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -364,7 +364,7 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex $count++; //Shift by one element } - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); $entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, '', $start_time); $items = array(); @@ -458,7 +458,7 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude break; } - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); $ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, '', '', $start_time); $itemRefs = array(); @@ -481,7 +481,7 @@ function editTag($e_ids, $a, $r) { $e_ids[$i] = hex2dec(basename($e_id)); //Strip prefix 'tag:google.com,2005:reader/item/' } - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); switch ($a) { case 'user/-/state/com.google/read': @@ -512,13 +512,15 @@ function editTag($e_ids, $a, $r) { function markAllAsRead($streamId, $olderThanId) { logMe("markAllAsRead($streamId, $olderThanId)\n"); - $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); 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); + $categoryDAO = new FreshRSS_CategoryDAO(); + $cat = $categoryDAO->searchByName($c_name); + $entryDAO->markReadCat($cat === null ? -1 : $cat->id(), $olderThanId); } elseif ($streamId === 'user/-/state/com.google/reading-list') { $entryDAO->markReadEntries($olderThanId, false, -1); } -- cgit v1.2.3 From bc8eb560afd50290745ea6a500c0f930df2559eb Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 6 Jul 2014 00:09:31 +0200 Subject: Add TTL to control feed freshness https://github.com/marienfressinaud/FreshRSS/issues/250 --- app/Controllers/configureController.php | 3 + app/Controllers/entryController.php | 2 +- app/Controllers/feedController.php | 2 +- app/Controllers/javascriptController.php | 2 +- app/Models/Configuration.php | 5 ++ app/Models/Feed.php | 150 ++++++++++++++++--------------- app/Models/FeedDAO.php | 19 ++-- app/i18n/en.php | 3 +- app/i18n/fr.php | 1 + app/views/configure/archiving.phtml | 21 +++++ app/views/configure/feed.phtml | 21 +++++ lib/lib_rss.php | 2 +- 12 files changed, 150 insertions(+), 81 deletions(-) (limited to 'lib') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index a608df162..5fd09a263 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -109,6 +109,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { 'priority' => intval(Minz_Request::param ('priority', 0)), 'httpAuth' => $httpAuth, 'keep_history' => intval(Minz_Request::param ('keep_history', -2)), + 'ttl' => intval(Minz_Request::param('ttl', -2)), ); if ($feedDAO->updateFeed ($id, $values)) { @@ -274,9 +275,11 @@ class FreshRSS_configure_Controller extends Minz_ActionController { if (Minz_Request::isPost()) { $old = Minz_Request::param('old_entries', 3); $keepHistoryDefault = Minz_Request::param('keep_history_default', 0); + $ttlDefault = Minz_Request::param('ttl_default', -2); $this->view->conf->_old_entries($old); $this->view->conf->_keep_history_default($keepHistoryDefault); + $this->view->conf->_ttl_default($ttlDefault); $this->view->conf->save(); invalidateHttpCache(); diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index 2d7fa718a..ac43587ea 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -125,7 +125,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $date_min = time() - (3600 * 24 * 30 * $nb_month_old); $feedDAO = FreshRSS_Factory::createFeedDao(); - $feeds = $feedDAO->listFeedsOrderUpdate(); + $feeds = $feedDAO->listFeeds(); $nbTotal = 0; invalidateHttpCache(); diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index d30b60877..36425ca9b 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -233,7 +233,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feeds = array ($feed); } } else { - $feeds = $feedDAO->listFeedsOrderUpdate (); + $feeds = $feedDAO->listFeedsOrderUpdate($this->view->conf->ttl_default); } // on calcule la date des articles les plus anciens qu'on accepte diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php index 737908c91..67148350f 100755 --- a/app/Controllers/javascriptController.php +++ b/app/Controllers/javascriptController.php @@ -8,7 +8,7 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { public function actualizeAction () { header('Content-Type: text/javascript; charset=UTF-8'); $feedDAO = FreshRSS_Factory::createFeedDao(); - $this->view->feeds = $feedDAO->listFeedsOrderUpdate(); + $this->view->feeds = $feedDAO->listFeedsOrderUpdate($this->view->conf->ttl_default); } public function nbUnreadsPerFeedAction() { diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index ffd20deca..f2084b833 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -7,6 +7,7 @@ class FreshRSS_Configuration { 'language' => 'en', 'old_entries' => 3, 'keep_history_default' => 0, + 'ttl_default' => 3600, 'mail_login' => '', 'token' => '', 'passwordHash' => '', //CRYPT_BLOWFISH @@ -159,6 +160,10 @@ class FreshRSS_Configuration { $value = intval($value); $this->data['keep_history_default'] = $value >= -1 ? $value : 0; } + public function _ttl_default($value) { + $value = intval($value); + $this->data['ttl_default'] = $value >= -1 ? $value : 3600; + } public function _shortcuts ($values) { foreach ($values as $key => $value) { if (isset($this->data['shortcuts'][$key])) { diff --git a/app/Models/Feed.php b/app/Models/Feed.php index c9f1230ad..576f37760 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -16,18 +16,19 @@ class FreshRSS_Feed extends Minz_Model { private $httpAuth = ''; private $error = false; private $keep_history = -2; + private $ttl = -2; private $hash = null; private $lockPath = ''; - public function __construct ($url, $validate=true) { + public function __construct($url, $validate=true) { if ($validate) { - $this->_url ($url); + $this->_url($url); } else { $this->url = $url; } } - public function id () { + public function id() { return $this->id; } @@ -38,72 +39,75 @@ class FreshRSS_Feed extends Minz_Model { return $this->hash; } - public function url () { + public function url() { return $this->url; } - public function category () { + public function category() { return $this->category; } - public function entries () { + public function entries() { return $this->entries === null ? array() : $this->entries; } - public function name () { + public function name() { return $this->name; } - public function website () { + public function website() { return $this->website; } - public function description () { + public function description() { return $this->description; } - public function lastUpdate () { + public function lastUpdate() { return $this->lastUpdate; } - public function priority () { + public function priority() { return $this->priority; } - public function pathEntries () { + public function pathEntries() { return $this->pathEntries; } - public function httpAuth ($raw = true) { + public function httpAuth($raw = true) { if ($raw) { return $this->httpAuth; } else { - $pos_colon = strpos ($this->httpAuth, ':'); - $user = substr ($this->httpAuth, 0, $pos_colon); - $pass = substr ($this->httpAuth, $pos_colon + 1); + $pos_colon = strpos($this->httpAuth, ':'); + $user = substr($this->httpAuth, 0, $pos_colon); + $pass = substr($this->httpAuth, $pos_colon + 1); - return array ( + return array( 'username' => $user, 'password' => $pass ); } } - public function inError () { + public function inError() { return $this->error; } - public function keepHistory () { + public function keepHistory() { return $this->keep_history; } - public function nbEntries () { + public function ttl() { + return $this->ttl; + } + public function nbEntries() { if ($this->nbEntries < 0) { $feedDAO = FreshRSS_Factory::createFeedDao(); - $this->nbEntries = $feedDAO->countEntries ($this->id ()); + $this->nbEntries = $feedDAO->countEntries($this->id()); } return $this->nbEntries; } - public function nbNotRead () { + public function nbNotRead() { if ($this->nbNotRead < 0) { $feedDAO = FreshRSS_Factory::createFeedDao(); - $this->nbNotRead = $feedDAO->countNotRead ($this->id ()); + $this->nbNotRead = $feedDAO->countNotRead($this->id()); } return $this->nbNotRead; } public function faviconPrepare() { $file = DATA_PATH . '/favicons/' . $this->hash() . '.txt'; - if (!file_exists ($file)) { + if (!file_exists($file)) { $t = $this->website; if ($t == '') { $t = $this->url; @@ -116,92 +120,98 @@ class FreshRSS_Feed extends Minz_Model { @unlink($path . '.ico'); @unlink($path . '.txt'); } - public function favicon () { - return Minz_Url::display ('/f.php?' . $this->hash()); + public function favicon() { + return Minz_Url::display('/f.php?' . $this->hash()); } - public function _id ($value) { + public function _id($value) { $this->id = $value; } - public function _url ($value, $validate=true) { + public function _url($value, $validate=true) { $this->hash = null; if ($validate) { $value = checkUrl($value); } - if (empty ($value)) { - throw new FreshRSS_BadUrl_Exception ($value); + if (empty($value)) { + throw new FreshRSS_BadUrl_Exception($value); } $this->url = $value; } - public function _category ($value) { + public function _category($value) { $value = intval($value); $this->category = $value >= 0 ? $value : 0; } - public function _name ($value) { + public function _name($value) { $this->name = $value === null ? '' : $value; } - public function _website ($value, $validate=true) { + public function _website($value, $validate=true) { if ($validate) { $value = checkUrl($value); } - if (empty ($value)) { + if (empty($value)) { $value = ''; } $this->website = $value; } - public function _description ($value) { + public function _description($value) { $this->description = $value === null ? '' : $value; } - public function _lastUpdate ($value) { + public function _lastUpdate($value) { $this->lastUpdate = $value; } - public function _priority ($value) { + public function _priority($value) { $value = intval($value); $this->priority = $value >= 0 ? $value : 10; } - public function _pathEntries ($value) { + public function _pathEntries($value) { $this->pathEntries = $value; } - public function _httpAuth ($value) { + public function _httpAuth($value) { $this->httpAuth = $value; } - public function _error ($value) { + public function _error($value) { $this->error = (bool)$value; } - public function _keepHistory ($value) { + public function _keepHistory($value) { $value = intval($value); $value = min($value, 1000000); $value = max($value, -2); $this->keep_history = $value; } - public function _nbNotRead ($value) { + public function _ttl($value) { + $value = intval($value); + $value = min($value, 100000000); + $value = max($value, -2); + $this->ttl = $value; + } + public function _nbNotRead($value) { $this->nbNotRead = intval($value); } - public function _nbEntries ($value) { + public function _nbEntries($value) { $this->nbEntries = intval($value); } - public function load ($loadDetails = false) { + public function load($loadDetails = false) { if ($this->url !== null) { if (CACHE_PATH === false) { - throw new Minz_FileNotExistException ( + throw new Minz_FileNotExistException( 'CACHE_PATH', Minz_Exception::ERROR ); } else { - $url = htmlspecialchars_decode ($this->url, ENT_QUOTES); + $url = htmlspecialchars_decode($this->url, ENT_QUOTES); if ($this->httpAuth != '') { - $url = preg_replace ('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url); + $url = preg_replace('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url); } $feed = customSimplePie(); - $feed->set_feed_url ($url); + $feed->set_feed_url($url); if (!$loadDetails) { //Only activates auto-discovery when adding a new feed $feed->set_autodiscovery_level(SIMPLEPIE_LOCATOR_NONE); } $mtime = $feed->init(); if ((!$mtime) || $feed->error()) { - throw new FreshRSS_Feed_Exception ($feed->error() . ' [' . $url . ']'); + throw new FreshRSS_Feed_Exception($feed->error() . ' [' . $url . ']'); } if ($loadDetails) { @@ -209,7 +219,7 @@ class FreshRSS_Feed extends Minz_Model { $subscribe_url = $feed->subscribe_url(false); $title = strtr(html_only_entity_decode($feed->get_title()), array('<' => '<', '>' => '>', '"' => '"')); //HTML to HTML-PRE //ENT_COMPAT except & - $this->_name ($title == '' ? $this->url : $title); + $this->_name($title == '' ? $this->url : $title); $this->_website(html_only_entity_decode($feed->get_link())); $this->_description(html_only_entity_decode($feed->get_description())); @@ -221,12 +231,12 @@ class FreshRSS_Feed extends Minz_Model { if ($subscribe_url !== null && $subscribe_url !== $this->url) { if ($this->httpAuth != '') { // on enlève les id si authentification HTTP - $subscribe_url = preg_replace ('#((.+)://)((.+)@)(.+)#', '${1}${5}', $subscribe_url); + $subscribe_url = preg_replace('#((.+)://)((.+)@)(.+)#', '${1}${5}', $subscribe_url); } - $this->_url ($subscribe_url); + $this->_url($subscribe_url); } - if (($mtime === true) || ($mtime > $this->lastUpdate)) { + if (($mtime === true) ||($mtime > $this->lastUpdate)) { syslog(LOG_DEBUG, 'FreshRSS no cache ' . $mtime . ' > ' . $this->lastUpdate . ' for ' . $subscribe_url); $this->loadEntries($feed); // et on charge les articles du flux } else { @@ -240,25 +250,25 @@ class FreshRSS_Feed extends Minz_Model { } } - private function loadEntries ($feed) { - $entries = array (); + private function loadEntries($feed) { + $entries = array(); - foreach ($feed->get_items () as $item) { - $title = html_only_entity_decode (strip_tags ($item->get_title ())); - $author = $item->get_author (); - $link = $item->get_permalink (); - $date = @strtotime ($item->get_date ()); + foreach ($feed->get_items() as $item) { + $title = html_only_entity_decode(strip_tags($item->get_title())); + $author = $item->get_author(); + $link = $item->get_permalink(); + $date = @strtotime($item->get_date()); // gestion des tags (catégorie == tag) - $tags_tmp = $item->get_categories (); - $tags = array (); + $tags_tmp = $item->get_categories(); + $tags = array(); if ($tags_tmp !== null) { foreach ($tags_tmp as $tag) { - $tags[] = html_only_entity_decode ($tag->get_label ()); + $tags[] = html_only_entity_decode($tag->get_label()); } } - $content = html_only_entity_decode ($item->get_content ()); + $content = html_only_entity_decode($item->get_content()); $elinks = array(); foreach ($item->get_enclosures() as $enclosure) { @@ -276,16 +286,16 @@ class FreshRSS_Feed extends Minz_Model { } } - $entry = new FreshRSS_Entry ( - $this->id (), - $item->get_id (), + $entry = new FreshRSS_Entry( + $this->id(), + $item->get_id(), $title === null ? '' : $title, - $author === null ? '' : html_only_entity_decode ($author->name), + $author === null ? '' : html_only_entity_decode($author->name), $content === null ? '' : $content, $link === null ? '' : $link, - $date ? $date : time () + $date ? $date : time() ); - $entry->_tags ($tags); + $entry->_tags($tags); // permet de récupérer le contenu des flux tronqués $entry->loadCompleteContent($this->pathEntries()); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 5281b371d..83f3a6231 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -2,7 +2,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { public function addFeed($valuesTmp) { - $sql = 'INSERT INTO `' . $this->prefix . 'feed` (url, category, name, website, description, lastUpdate, priority, httpAuth, error, keep_history) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, -2)'; + $sql = 'INSERT INTO `' . $this->prefix . 'feed` (url, category, name, website, description, lastUpdate, priority, httpAuth, error, keep_history, ttl) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, -2, -2)'; $stm = $this->bd->prepare($sql); $values = array( @@ -222,13 +222,19 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $feedCategoryNames; } - public function listFeedsOrderUpdate($cacheDuration = 1500) { - $sql = 'SELECT id, url, name, website, lastUpdate, pathEntries, httpAuth, keep_history ' + public function listFeedsOrderUpdate($defaultCacheDuration = 3600) { + $sql = 'SELECT id, url, name, website, lastUpdate, pathEntries, httpAuth, keep_history, ttl ' . 'FROM `' . $this->prefix . 'feed` ' - . 'WHERE lastUpdate < ' . (time() - intval($cacheDuration)) - . ' ORDER BY lastUpdate'; + . 'WHERE ttl <> -1 AND lastUpdate < (' . (time() + 60) . '-(CASE WHEN ttl=-2 THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) ' + . 'ORDER BY lastUpdate'; $stm = $this->bd->prepare($sql); - $stm->execute(); + if (!($stm && $stm->execute())) { + $sql2 = 'ALTER TABLE `' . $this->prefix . 'feed` ADD COLUMN ttl INT NOT NULL DEFAULT -2'; //v0.7.3 + $stm = $this->bd->prepare($sql2); + $stm->execute(); + $stm = $this->bd->prepare($sql); + $stm->execute(); + } return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC)); } @@ -365,6 +371,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $myFeed->_httpAuth(isset($dao['httpAuth']) ? base64_decode($dao['httpAuth']) : ''); $myFeed->_error(isset($dao['error']) ? $dao['error'] : 0); $myFeed->_keepHistory(isset($dao['keep_history']) ? $dao['keep_history'] : -2); + $myFeed->_ttl(isset($dao['ttl']) ? $dao['ttl'] : -2); $myFeed->_nbNotRead(isset($dao['cache_nbUnreads']) ? $dao['cache_nbUnreads'] : 0); $myFeed->_nbEntries(isset($dao['cache_nbEntries']) ? $dao['cache_nbEntries'] : 0); if (isset($dao['id'])) { diff --git a/app/i18n/en.php b/app/i18n/en.php index 19cf4a06d..569903782 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -41,7 +41,7 @@ return array ( 'query_state_15' => 'Display all articles', 'query_number' => 'Query n°%d', 'add_query' => 'Add a query', - 'no_query' => 'You haven’t created user queries yet.', + 'no_query' => 'You haven’t created any user query yet.', 'query_filter' => 'Filter applied:', 'no_query_filter' => 'No filter', 'about' => 'About', @@ -197,6 +197,7 @@ return array ( 'by_feed' => 'by feed', 'by_default' => 'By default', 'keep_history' => 'Minimum number of articles to keep', + 'ttl' => 'Do not automatically refresh more often than', 'categorize' => 'Store in a category', 'truncate' => 'Delete all articles', 'advanced' => 'Advanced', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 54fe55ea0..5fc60bd15 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -197,6 +197,7 @@ return array ( 'by_feed' => 'par flux', 'by_default' => 'Par défaut', 'keep_history' => 'Nombre minimum d’articles à conserver', + 'ttl' => 'Ne pas automatiquement rafraîchir plus souvent que', 'categorize' => 'Ranger dans une catégorie', 'truncate' => 'Supprimer tous les articles', 'advanced' => 'Avancé', diff --git a/app/views/configure/archiving.phtml b/app/views/configure/archiving.phtml index e144d0f45..04fa19b0d 100644 --- a/app/views/configure/archiving.phtml +++ b/app/views/configure/archiving.phtml @@ -24,6 +24,27 @@ ?> ()
    +
    + +
    + () +
    +
    diff --git a/app/views/configure/feed.phtml b/app/views/configure/feed.phtml index 27b0990ff..a8dd9a8cb 100644 --- a/app/views/configure/feed.phtml +++ b/app/views/configure/feed.phtml @@ -103,6 +103,27 @@ ?>
    +
    + +
    + +
    +
    diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 1b1e4b021..7ca611b04 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -109,7 +109,7 @@ function customSimplePie() { $simplePie = new SimplePie(); $simplePie->set_useragent(Minz_Translate::t('freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION); $simplePie->set_cache_location(CACHE_PATH); - $simplePie->set_cache_duration(1500); + $simplePie->set_cache_duration(800); $simplePie->strip_htmltags(array( 'base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', -- cgit v1.2.3 From 35be1769de28df3fff1a26e40d1d6b1e587a2847 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 1 Aug 2014 20:20:25 +0200 Subject: Basic protection against XSRF using Referer https://github.com/marienfressinaud/FreshRSS/issues/554 Also edited the error controler to use the log message passed in Minz_Error::error(). --- app/Controllers/errorController.php | 52 +++++++++++++++++++++++-------------- app/FreshRSS.php | 11 +++++++- app/views/error/index.phtml | 13 ++-------- lib/Minz/Translate.php | 2 +- 4 files changed, 45 insertions(+), 33 deletions(-) (limited to 'lib') diff --git a/app/Controllers/errorController.php b/app/Controllers/errorController.php index dc9a2ee25..922650b3d 100644 --- a/app/Controllers/errorController.php +++ b/app/Controllers/errorController.php @@ -1,26 +1,38 @@ view->code = 'Error 403 - Forbidden'; - break; - case 404: - $this->view->code = 'Error 404 - Not found'; - break; - case 500: - $this->view->code = 'Error 500 - Internal Server Error'; - break; - case 503: - $this->view->code = 'Error 503 - Service Unavailable'; - break; - default: - $this->view->code = 'Error 404 - Not found'; + public function indexAction() { + switch (Minz_Request::param('code')) { + case 403: + $this->view->code = 'Error 403 - Forbidden'; + break; + case 404: + $this->view->code = 'Error 404 - Not found'; + break; + case 500: + $this->view->code = 'Error 500 - Internal Server Error'; + break; + case 503: + $this->view->code = 'Error 503 - Service Unavailable'; + break; + default: + $this->view->code = 'Error 404 - Not found'; } - - $this->view->logs = Minz_Request::param ('logs'); - - Minz_View::prependTitle ($this->view->code . ' · '); + + $errors = Minz_Request::param('logs', array()); + $this->view->errorMessage = trim(implode($errors)); + if ($this->view->errorMessage == '') { + switch(Minz_Request::param('code')) { + case 403: + $this->view->errorMessage = Minz_Translate::t('forbidden_access'); + break; + case 404: + default: + $this->view->errorMessage = Minz_Translate::t('page_not_found'); + break; + } + } + + Minz_View::prependTitle($this->view->code . ' · '); } } diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 84cf3429b..cd6048f75 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -6,6 +6,16 @@ class FreshRSS extends Minz_FrontController { } $loginOk = $this->accessControl(Minz_Session::param('currentUser', '')); $this->loadParamsView(); + if (Minz_Request::isPost() && !empty($_SERVER['HTTP_REFERER']) && + Minz_Request::getDomainName() !== parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) { + $loginOk = false; //Basic protection against XSRF attacks + Minz_Error::error( + 403, + array('error' => array(Minz_Translate::t('access_denied') . ' [HTTP_REFERER=' . + htmlspecialchars(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']) . ']')) + ); + } + Minz_View::_param('loginOk', $loginOk); $this->loadStylesAndScripts($loginOk); //TODO: Do not load that when not needed, e.g. some Ajax requests $this->loadNotifications(); } @@ -95,7 +105,6 @@ class FreshRSS extends Minz_FrontController { break; } } - Minz_View::_param ('loginOk', $loginOk); return $loginOk; } diff --git a/app/views/error/index.phtml b/app/views/error/index.phtml index 6a09c3aa2..ef4fbd39d 100644 --- a/app/views/error/index.phtml +++ b/app/views/error/index.phtml @@ -1,18 +1,9 @@

    code; ?>

    -

    -
    - + errorMessage; ?>
    +

    diff --git a/lib/Minz/Translate.php b/lib/Minz/Translate.php index df48350e9..8c2f90041 100644 --- a/lib/Minz/Translate.php +++ b/lib/Minz/Translate.php @@ -75,5 +75,5 @@ function _t($key) { unset($args[0]); array_unshift($args, $key); - return call_user_func_array("Minz_Translate::t", $args); + return call_user_func_array('Minz_Translate::t', $args); } -- cgit v1.2.3 From 274c8096e3ccc8ea008c1a038134ffddc302fd0d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 2 Aug 2014 19:57:15 +0200 Subject: Experimental: Removed lazyload.js and use postpone attribute instead https://github.com/marienfressinaud/FreshRSS/issues/316 The performance of lazyload.js was not good enough, and not really needed anyway. This change mostly affects mainly situations when the content of articles is shown by default, not so much when they are collapsed Using HTML5 lazyload and postpone attributes by default on all img, audio, iframe, video. http://www.w3.org/TR/resource-priorities/#attr-postpone Postpone attribute is removed by JavaScript if the user does not want the lazyload behaviour. In the case when users do want the lazyload behaviour, in normal view with articles hidden, we furthermore use the data-original approach to be sure to support current browsers. +Corrected some bugs with enclosures, and some images not appearing before the first scroll. +Now faster regex processing img and iframe at once (was not practical with lazyload.js) --- CHANGELOG | 2 ++ README.md | 1 - app/FreshRSS.php | 10 +++------- app/Models/Feed.php | 6 +++--- app/views/configure/reading.phtml | 2 +- app/views/helpers/javascript_vars.phtml | 1 - app/views/helpers/view/normal_view.phtml | 10 +++------- app/views/helpers/view/reader_view.phtml | 18 ++++++------------ lib/lib_rss.php | 20 ++++++-------------- p/scripts/jquery.lazyload.min.js | 15 --------------- p/scripts/main.js | 25 ++++++++----------------- 11 files changed, 32 insertions(+), 78 deletions(-) delete mode 100644 p/scripts/jquery.lazyload.min.js (limited to 'lib') diff --git a/CHANGELOG b/CHANGELOG index 33cb810c4..969af92a7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,8 @@ * Improvements * Security * Basic protection against XSRF (Cross-Site Request Forgery) based on HTTP Referer (POST requests only) +* Misc. + * Changed lazyload implementation * Bux fixes in export function, add/remove users, keyboard shortcuts, etc. diff --git a/README.md b/README.md index fff08472b..8963e040c 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,6 @@ mysqldump -u utilisateur -p --databases freshrss > freshrss.sql ## Uniquement pour certaines options * [bcrypt.js](https://github.com/dcodeIO/bcrypt.js) * [phpQuery](http://code.google.com/p/phpquery/) -* [Lazy Load](http://www.appelsiini.net/projects/lazyload) ## Si les fonctions natives ne sont pas disponibles * [Services_JSON](http://pear.php.net/pepr/pepr-proposal-show.php?id=198) diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 3443589c6..7c333b090 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -136,13 +136,9 @@ class FreshRSS extends Minz_FrontController { Minz_View::appendScript('https://login.persona.org/include.js'); break; } - $includeLazyLoad = $this->conf->lazyload && ($this->conf->display_posts || Minz_Request::param ('output') === 'reader'); - Minz_View::appendScript (Minz_Url::display ('/scripts/jquery.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.min.js')), false, !$includeLazyLoad, !$includeLazyLoad); - if ($includeLazyLoad) { - Minz_View::appendScript (Minz_Url::display ('/scripts/jquery.lazyload.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.lazyload.min.js'))); - } - Minz_View::appendScript (Minz_Url::display ('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); - Minz_View::appendScript (Minz_Url::display ('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js'))); + Minz_View::appendScript(Minz_Url::display('/scripts/jquery.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.min.js'))); + Minz_View::appendScript(Minz_Url::display('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); + Minz_View::appendScript(Minz_Url::display('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js'))); } private function loadNotifications () { diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 576f37760..fe1e52ea2 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -277,11 +277,11 @@ class FreshRSS_Feed extends Minz_Model { $elinks[$elink] = '1'; $mime = strtolower($enclosure->get_type()); if (strpos($mime, 'image/') === 0) { - $content .= '
    '; + $content .= '
    '; } elseif (strpos($mime, 'audio/') === 0) { - $content .= '
    diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 7ca611b04..86c0a4ae4 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -121,10 +121,10 @@ function customSimplePie() { 'onmouseover', 'onmousemove', 'onmouseout', 'onfocus', 'onblur', 'onkeypress', 'onkeydown', 'onkeyup', 'onselect', 'onchange', 'seamless'))); $simplePie->add_attributes(array( - 'img' => array('lazyload' => ''), //http://www.w3.org/TR/resource-priorities/ - 'audio' => array('preload' => 'none'), - 'iframe' => array('postpone' => '', 'sandbox' => 'allow-scripts allow-same-origin'), - 'video' => array('postpone' => '', 'preload' => 'none'), + 'img' => array('lazyload' => '', 'postpone' => ''), //http://www.w3.org/TR/resource-priorities/ + 'audio' => array('lazyload' => '', 'postpone' => '', 'preload' => 'none'), + 'iframe' => array('lazyload' => '', 'postpone' => '', 'sandbox' => 'allow-scripts allow-same-origin'), + 'video' => array('lazyload' => '', 'postpone' => '', 'preload' => 'none'), )); $simplePie->set_url_replacements(array( 'a' => 'href', @@ -183,16 +183,8 @@ function get_content_by_parsing ($url, $path) { */ function lazyimg($content) { return preg_replace( - '/]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', - '', - $content - ); -} - -function lazyIframe($content) { - return preg_replace( - '/]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', - '', + '/<((?:img|iframe)[^>]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', + '<$1src="' . Minz_Url::display('/themes/icons/grey.gif') . '" data-original="$2"$3>', $content ); } diff --git a/p/scripts/jquery.lazyload.min.js b/p/scripts/jquery.lazyload.min.js deleted file mode 100644 index 8dd097dc3..000000000 --- a/p/scripts/jquery.lazyload.min.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Lazy Load - jQuery plugin for lazy loading images - * - * Copyright (c) 2007-2013 Mika Tuupola - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/mit-license.php - * - * Project home: - * http://www.appelsiini.net/projects/lazyload - * - * Version: 1.9.0 - * - */ -!function(a,b,c,d){var e=a(b);a.fn.lazyload=function(f){function g(){var b=0;i.each(function(){var c=a(this);if(!j.skip_invisible||c.is(":visible"))if(a.abovethetop(this,j)||a.leftofbegin(this,j));else if(a.belowthefold(this,j)||a.rightoffold(this,j)){if(++b>j.failure_limit)return!1}else c.trigger("appear"),b=0})}var h,i=this,j={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!0,appear:null,load:null,placeholder:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"};return f&&(d!==f.failurelimit&&(f.failure_limit=f.failurelimit,delete f.failurelimit),d!==f.effectspeed&&(f.effect_speed=f.effectspeed,delete f.effectspeed),a.extend(j,f)),h=j.container===d||j.container===b?e:a(j.container),0===j.event.indexOf("scroll")&&h.bind(j.event,function(){return g()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,(c.attr("src")===d||c.attr("src")===!1)&&c.attr("src",j.placeholder),c.one("appear",function(){if(!this.loaded){if(j.appear){var d=i.length;j.appear.call(b,d,j)}a("").bind("load",function(){var d=c.data(j.data_attribute);c.hide(),c.is("img")?c.attr("src",d):c.css("background-image","url('"+d+"')"),c[j.effect](j.effect_speed),b.loaded=!0;var e=a.grep(i,function(a){return!a.loaded});if(i=a(e),j.load){var f=i.length;j.load.call(b,f,j)}}).attr("src",c.data(j.data_attribute))}}),0!==j.event.indexOf("scroll")&&c.bind(j.event,function(){b.loaded||c.trigger("appear")})}),e.bind("resize",function(){g()}),/iphone|ipod|ipad.*os 5/gi.test(navigator.appVersion)&&e.bind("pageshow",function(b){b.originalEvent&&b.originalEvent.persisted&&i.each(function(){a(this).trigger("appear")})}),a(c).ready(function(){g()}),this},a.belowthefold=function(c,f){var g;return g=f.container===d||f.container===b?(b.innerHeight?b.innerHeight:e.height())+e.scrollTop():a(f.container).offset().top+a(f.container).height(),g<=a(c).offset().top-f.threshold},a.rightoffold=function(c,f){var g;return g=f.container===d||f.container===b?e.width()+e.scrollLeft():a(f.container).offset().left+a(f.container).width(),g<=a(c).offset().left-f.threshold},a.abovethetop=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollTop():a(f.container).offset().top,g>=a(c).offset().top+f.threshold+a(c).height()},a.leftofbegin=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollLeft():a(f.container).offset().left,g>=a(c).offset().left+f.threshold+a(c).width()},a.inviewport=function(b,c){return!(a.rightoffold(b,c)||a.leftofbegin(b,c)||a.belowthefold(b,c)||a.abovethetop(b,c))},a.extend(a.expr[":"],{"below-the-fold":function(b){return a.belowthefold(b,{threshold:0})},"above-the-top":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-screen":function(b){return a.rightoffold(b,{threshold:0})},"left-of-screen":function(b){return!a.rightoffold(b,{threshold:0})},"in-viewport":function(b){return a.inviewport(b,{threshold:0})},"above-the-fold":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-fold":function(b){return a.rightoffold(b,{threshold:0})},"left-of-fold":function(b){return!a.rightoffold(b,{threshold:0})}})}(jQuery,window,document); \ No newline at end of file diff --git a/p/scripts/main.js b/p/scripts/main.js index b6214e508..acb7bd527 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -421,21 +421,7 @@ function inMarkViewport(flux, box_to_follow, relative_follow) { return (windowBot >= begin && bot >= windowBot); } -function init_lazyload() { - if ($.fn.lazyload) { - if (is_global_mode()) { - $(".flux_content img").lazyload({ - container: $("#panel") - }); - } else { - $(".flux_content img").lazyload(); - } - } -} - function init_posts() { - init_lazyload(); - var box_to_follow = $(window), relative_follow = false; if (is_global_mode()) { @@ -827,7 +813,6 @@ function load_more_posts() { }); init_load_more(box_load_more); - init_lazyload(); $('#load_more').removeClass('loading'); load_more = false; @@ -841,6 +826,12 @@ function focus_search() { function init_load_more(box) { box_load_more = box; + if (!does_lazyload) { + $('img[postpone], audio[postpone], iframe[postpone], video[postpone]').each(function () { + this.removeAttribute('postpone'); + }); + } + var $next_link = $("#load_more"); if (!$next_link.length) { // no more article to load @@ -1093,7 +1084,7 @@ function faviconNbUnread(n) { ctx.fillStyle = 'rgba(255, 255, 255, 127)'; ctx.fillRect(0, 8, 1 + ctx.measureText(text).width, 7); ctx.fillStyle = '#F00'; - ctx.fillText(text, 0, 16); + ctx.fillText(text, 0, canvas.height); } link.href = canvas.toDataURL('image/png'); $('link[rel~=icon]').remove(); @@ -1104,7 +1095,7 @@ function faviconNbUnread(n) { } function init_all() { - if (!(window.$ && window.url_freshrss && ((!full_lazyload) || $.fn.lazyload))) { + if (!(window.$ && window.url_freshrss)) { if (window.console) { console.log('FreshRSS waiting for JS…'); } -- cgit v1.2.3 From d477373ef2879bdeeaa3c157287c0fab98afefdc Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 9 Aug 2014 19:58:39 +0200 Subject: SQLite: Bug creation new users Not tested much yet. Some MySQL parts changed a bit too to double-check. https://github.com/marienfressinaud/FreshRSS/issues/574 --- app/Models/UserDAO.php | 35 ++++++++++++++++++++++------------- app/SQL/install.sql.sqlite.php | 1 + lib/Minz/ModelPdo.php | 20 ++++++++++++-------- 3 files changed, 35 insertions(+), 21 deletions(-) (limited to 'lib') diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index 1763fac67..9f64fb4a7 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -4,18 +4,21 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { public function createUser($username) { $db = Minz_Configuration::dataBase(); require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); - - if (defined('SQL_CREATE_TABLES')) { + + $userPDO = new Minz_ModelPdo($username); + + $ok = false; + if (defined('SQL_CREATE_TABLES')) { //E.g. MySQL $sql = sprintf(SQL_CREATE_TABLES, $db['prefix'] . $username . '_', Minz_Translate::t('default_category')); - $stm = $this->bd->prepare($sql); + $stm = $userPDO->bd->prepare($sql); $ok = $stm && $stm->execute(); - } else { + } else { //E.g. SQLite global $SQL_CREATE_TABLES; if (is_array($SQL_CREATE_TABLES)) { $ok = true; foreach ($SQL_CREATE_TABLES as $instruction) { $sql = sprintf($instruction, '', Minz_Translate::t('default_category')); - $stm = $c->prepare($sql); + $stm = $userPDO->bd->prepare($sql); $ok &= ($stm && $stm->execute()); } } @@ -24,7 +27,7 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { if ($ok) { return true; } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = empty($stm) ? array(2 => 'syntax error') : $stm->errorInfo(); Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); return false; } @@ -34,14 +37,20 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { $db = Minz_Configuration::dataBase(); require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); - $sql = sprintf(SQL_DROP_TABLES, $db['prefix'] . $username . '_'); - $stm = $this->bd->prepare($sql); - if ($stm && $stm->execute()) { - return true; + if ($db['type'] === 'sqlite') { + return unlink(DATA_PATH . '/' . $username . '.sqlite'); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); - return false; + $userPDO = new Minz_ModelPdo($username); + + $sql = sprintf(SQL_DROP_TABLES, $db['prefix'] . $username . '_'); + $stm = $userPDO->bd->prepare($sql); + if ($stm && $stm->execute()) { + return true; + } else { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + return false; + } } } } diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php index b90a5ef5e..7988ada04 100644 --- a/app/SQL/install.sql.sqlite.php +++ b/app/SQL/install.sql.sqlite.php @@ -1,4 +1,5 @@ bd = self::$sharedBd; $this->prefix = self::$sharedPrefix; return; @@ -42,6 +42,10 @@ class Minz_ModelPdo { $db = Minz_Configuration::dataBase(); + if ($currentUser === null) { + $currentUser = Minz_Session::param('currentUser', '_'); + } + try { $type = $db['type']; if ($type === 'mysql') { @@ -51,9 +55,9 @@ class Minz_ModelPdo { $driver_options = array( PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', ); - $this->prefix = $db['prefix'] . Minz_Session::param('currentUser', '_') . '_'; + $this->prefix = $db['prefix'] . $currentUser . '_'; } elseif ($type === 'sqlite') { - $string = 'sqlite:' . DATA_PATH . '/' . Minz_Session::param('currentUser', '_') . '.sqlite'; + $string = 'sqlite:' . DATA_PATH . '/' . $currentUser . '.sqlite'; $driver_options = array( //PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, ); @@ -67,7 +71,7 @@ class Minz_ModelPdo { self::$sharedDbType = $type; self::$sharedPrefix = $this->prefix; - $this->bd = new FreshPDO( + $this->bd = new MinzPDO( $string, $db['user'], $db['password'], @@ -98,7 +102,7 @@ class Minz_ModelPdo { } } -class FreshPDO extends PDO { +class MinzPDO extends PDO { private static function check($statement) { if (preg_match('/^(?:UPDATE|INSERT|DELETE)/i', $statement)) { invalidateHttpCache(); @@ -106,12 +110,12 @@ class FreshPDO extends PDO { } public function prepare($statement, $driver_options = array()) { - FreshPDO::check($statement); + MinzPDO::check($statement); return parent::prepare($statement, $driver_options); } public function exec($statement) { - FreshPDO::check($statement); + MinzPDO::check($statement); return parent::exec($statement); } } -- cgit v1.2.3 From e7dba0ce7cfaf5e84687593a8b0d58d89fbff302 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 9 Aug 2014 23:29:13 +0200 Subject: Add basic system of update - Check on update.freshrss.org for new updates - Download script - Apply script - Need translations and verifications NOTE: current script on server indicates version 0.7.3 is an update of 0.8-dev ==> IT'S ONLY FOR MY TESTS! Script just does a backup of ./data actually... See https://github.com/marienfressinaud/FreshRSS/issues/411 --- app/Controllers/updateController.php | 106 +++++++++++++++++++++++++++++++++++ app/i18n/en.php | 2 + app/i18n/fr.php | 2 + app/layout/aside_configure.phtml | 38 ++++++++----- app/layout/header.phtml | 6 ++ app/views/update/index.phtml | 19 +++++++ constants.php | 2 + lib/Minz/View.php | 15 +++-- 8 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 app/Controllers/updateController.php create mode 100644 app/views/update/index.phtml (limited to 'lib') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php new file mode 100644 index 000000000..a15cb9fd5 --- /dev/null +++ b/app/Controllers/updateController.php @@ -0,0 +1,106 @@ +view->loginOk && Minz_Configuration::isAdmin($current_user)) { + Minz_Error::error( + 403, + array('error' => array(_t('access_denied'))) + ); + } + + Minz_View::prependTitle(_t('update_system') . ' · '); + } + + public function indexAction() { + if (file_exists(UPDATE_FILENAME)) { + // There is an update file to apply! + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply', _url('update', 'apply')) + ); + + return; + } + } + + public function checkAction() { + $this->view->change_view('update', 'index'); + + if (file_exists(UPDATE_FILENAME)) { + // There is already an update file to apply: we don't need to check + // the webserver! + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply', _url('update', 'apply')) + ); + + return; + } + + $c = curl_init(FRESHRSS_UPDATE_WEBSITE); + curl_setopt($c, CURLOPT_RETURNTRANSFER, true); + $result = curl_exec($c); + + if (curl_getinfo($c, CURLINFO_HTTP_CODE) == 200) { + $res_array = explode("\n", $result, 2); + $status = $res_array[0]; + + if (strpos($status, 'UPDATE') === 0) { + $script = $res_array[1]; + if (file_put_contents(UPDATE_FILENAME, $script) !== false) { + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply', _url('update', 'apply')) + ); + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('update_problem') + ); + } + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('no_update') + ); + } + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE) + ); + } + curl_close($c); + } + + public function applyAction() { + require(UPDATE_FILENAME); + $res = apply_update(); + + if ($res === true) { + @unlink(UPDATE_FILENAME); + + Minz_Session::_param('notification', array( + 'type' => 'good', + 'content' => Minz_Translate::t('update_finished') + )); + + Minz_Request::forward(array(), true); + } else { + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => Minz_Translate::t('update_failed', $res) + )); + + Minz_Request::forward(array('c' => 'update'), true); + } + } +} \ No newline at end of file diff --git a/app/i18n/en.php b/app/i18n/en.php index d80299b10..6110ccb11 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -408,4 +408,6 @@ return array ( 'stats_entry_per_category' => 'Entries per category', 'stats_top_feed' => 'Top ten feeds', 'stats_entry_count' => 'Entry count', + + 'update_can_apply' => 'There is an available update. Apply', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 4be028ac3..5f88aa069 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -408,4 +408,6 @@ return array ( 'stats_entry_per_category' => 'Articles par catégorie', 'stats_top_feed' => 'Les dix plus gros flux', 'stats_entry_count' => 'Nombre d’articles', + + 'update_can_apply' => 'Il y’a une mise à jour à appliquer. Appliquer', ); diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index e66f2f64c..03bea4836 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -1,25 +1,33 @@ diff --git a/app/layout/header.phtml b/app/layout/header.phtml index 2e42bfdea..028e63d8a 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -75,6 +75,12 @@ if (Minz_Configuration::canLogIn()) {
  • + +
  • +
  • diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml new file mode 100644 index 000000000..a1a872845 --- /dev/null +++ b/app/views/update/index.phtml @@ -0,0 +1,19 @@ +partial('aside_configure'); ?> + +
    +

    + + message)) { ?> +

    + message['title']; ?> + message['body']; ?> +

    + + + message) || $this->message['status'] !== 'good') { ?> +

    + + last_update_time); ?> +

    + +
    diff --git a/constants.php b/constants.php index e32b8cbc3..a968b82f4 100644 --- a/constants.php +++ b/constants.php @@ -1,6 +1,7 @@ change_view(Minz_Request::controllerName(), + Minz_Request::actionName()); + self::$title = Minz_Configuration::title (); + } + + /** + * Change le fichier de vue en fonction d'un controller / action + */ + public function change_view($controller_name, $action_name) { $this->view_filename = APP_PATH . self::VIEWS_PATH_NAME . '/' - . Minz_Request::controllerName () . '/' - . Minz_Request::actionName () . '.phtml'; - - self::$title = Minz_Configuration::title (); + . $controller_name . '/' + . $action_name . '.phtml'; } /** -- cgit v1.2.3 From eceb7756cfcf117c2a18984291181a84697ed3cd Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 10 Aug 2014 20:29:43 +0200 Subject: Add possibility to keep logged in with form Add an option to keep logged in. Change lifetime of session cookie to 1 year. See https://github.com/marienfressinaud/FreshRSS/issues/465 --- app/Controllers/indexController.php | 9 +++++++++ app/i18n/en.php | 1 + app/i18n/fr.php | 1 + app/views/index/formLogin.phtml | 23 +++++++++++++++-------- lib/Minz/Session.php | 35 ++++++++++++++++++++++++++++++----- 5 files changed, 56 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 3119073b8..18b99d0df 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -298,6 +298,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { public function formLoginAction () { if (Minz_Request::isPost()) { $ok = false; + $keep_logged_in = Minz_Request::param('keep_logged_in', false); $nonce = Minz_Session::param('nonce'); $username = Minz_Request::param('username', ''); $c = Minz_Request::param('challenge', ''); @@ -312,6 +313,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { if ($ok) { Minz_Session::_param('currentUser', $username); Minz_Session::_param('passwordHash', $s); + if ($keep_logged_in) { + // New cookie with a lifetime of 1 year! + Minz_Session::keepCookie(31536000); + Minz_Session::regenerateID(); + } } else { Minz_Log::record('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c, Minz_Log::WARNING); } @@ -371,6 +377,9 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('currentUser'); Minz_Session::_param('mail'); Minz_Session::_param('passwordHash'); + Minz_Session::keepCookie(0); + Minz_Session::regenerateID(); + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } } diff --git a/app/i18n/en.php b/app/i18n/en.php index d80299b10..3c55f62a2 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -3,6 +3,7 @@ return array ( // LAYOUT 'login' => 'Login', + 'keep_logged_in' => 'Keep me logged in', 'login_with_persona' => 'Login with Persona', 'logout' => 'Logout', 'search' => 'Search words or #tags', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 4be028ac3..63d779471 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -3,6 +3,7 @@ return array ( // LAYOUT 'login' => 'Connexion', + 'session_active' => 'Rester connecté', 'login_with_persona' => 'Connexion avec Persona', 'logout' => 'Déconnexion', 'search' => 'Rechercher des mots ou des #tags', diff --git a/app/views/index/formLogin.phtml b/app/views/index/formLogin.phtml index cc925ea59..f01a950b6 100644 --- a/app/views/index/formLogin.phtml +++ b/app/views/index/formLogin.phtml @@ -1,32 +1,39 @@
    -

    - +
    - +
    - +
    - + +
    +
    +
    +

    - - + +

    -

    +

    diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index ddabc4658..c859be2ed 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -15,13 +15,15 @@ class Minz_Session { * Le nom de session est utilisé comme nom pour les cookies et les URLs (i.e. PHPSESSID). * Il ne doit contenir que des caractères alphanumériques ; il doit être court et descriptif */ - public static function init ($name) { + public static function init($name) { + $cookie = session_get_cookie_params(); + self::keepCookie($cookie['lifetime']); + // démarre la session - session_name ($name); - session_set_cookie_params (0, dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirname($_SERVER['REQUEST_URI'])), null, false, true); - session_start (); + session_name($name); + session_start(); - if (isset ($_SESSION)) { + if (isset($_SESSION)) { self::$session = $_SESSION; } } @@ -68,4 +70,27 @@ class Minz_Session { Minz_Translate::reset (); } } + + + /** + * Spécifie la durée de vie des cookies + * @param $l la durée de vie + */ + public static function keepCookie($l) { + $cookie_dir = dirname( + empty($_SERVER['SCRIPT_NAME']) ? '' : $_SERVER['SCRIPT_NAME'] + ) . '/'; + session_set_cookie_params($l, $cookie_dir, $_SERVER['HTTP_HOST'], + false, true); + } + + + /** + * Régénère un id de session. + * Utile pour appeler session_set_cookie_params après session_start() + */ + public static function regenerateID() { + session_regenerate_id(true); + } + } -- cgit v1.2.3 From df47217839ccddb8e03015959c61b61e748d9700 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 10 Aug 2014 23:41:36 +0200 Subject: Set session.gc_maxlifetime Take the maxvalue between 1440 (24m) and cookie lifetime when calling Minz_Session::keepCookie() See https://github.com/marienfressinaud/FreshRSS/issues/465 --- lib/Minz/Session.php | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index c859be2ed..fb3c5d74b 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -82,6 +82,9 @@ class Minz_Session { ) . '/'; session_set_cookie_params($l, $cookie_dir, $_SERVER['HTTP_HOST'], false, true); + + $l_session = max(1440, $l); + ini_set('session.gc_maxlifetime', $l_session); } -- cgit v1.2.3 From bc71a577fe3154080df9949b394c7ae552773c7b Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 11 Aug 2014 18:39:22 +0200 Subject: Revert "Set session.gc_maxlifetime" This reverts commit df47217839ccddb8e03015959c61b61e748d9700. See https://github.com/marienfressinaud/FreshRSS/issues/465 --- lib/Minz/Session.php | 3 --- 1 file changed, 3 deletions(-) (limited to 'lib') diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index fb3c5d74b..c859be2ed 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -82,9 +82,6 @@ class Minz_Session { ) . '/'; session_set_cookie_params($l, $cookie_dir, $_SERVER['HTTP_HOST'], false, true); - - $l_session = max(1440, $l); - ini_set('session.gc_maxlifetime', $l_session); } -- cgit v1.2.3 From 1b20f6bd025a08a7a741b2751d837f736758eb2d Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 12 Aug 2014 20:59:27 +0200 Subject: New wrappers Minz_Request::good() and bad() 1. Set a notification message in session variable 2. Redirect to a specific url First use in importExportController.php See https://github.com/marienfressinaud/FreshRSS/conversations/576 --- app/Controllers/importExportController.php | 57 ++++++------------------------ lib/Minz/Request.php | 25 +++++++++++++ 2 files changed, 35 insertions(+), 47 deletions(-) (limited to 'lib') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 15871ed80..92b39b575 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -43,17 +43,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $zip = zip_open($file['tmp_name']); if (!is_resource($zip)) { - Minz_Log::error( - 'Zip file cannot be imported. Error code: ' . $zip - ); - // zip_open cannot open file: something is wrong - Minz_Session::_param('notification', array( - 'type' => 'bad', - 'content' => _t('zip_error') - )); - - Minz_Request::forward(array('c' => 'importExport'), true); + Minz_Log::error('Zip file cannot be imported. Error code: ' . $zip); + Minz_Request::bad(_t('zip_error'), array('c' => 'importExport')); } while (($zipfile = zip_read($zip)) !== false) { @@ -72,12 +64,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { zip_close($zip); } elseif ($type_file === 'zip') { // Zip extension is not loaded - Minz_Session::_param('notification', array( - 'type' => 'bad', - 'content' => _t('no_zip_extension') - )); - - Minz_Request::forward(array('c' => 'importExport'), true); + Minz_Request::bad(_t('no_zip_extension'), array('c' => 'importExport')); } elseif ($type_file !== 'unknown') { $list_files[$type_file][] = file_get_contents( $file['tmp_name'] @@ -100,35 +87,16 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } // And finally, we get import status and redirect to the home page - $notif = null; - if ($error === true) { - $content_notif = Minz_Translate::t( - 'feeds_imported_with_errors' - ); - } else { - $content_notif = Minz_Translate::t( - 'feeds_imported' - ); - } - - Minz_Session::_param('notification', array( - 'type' => 'good', - 'content' => $content_notif - )); Minz_Session::_param('actualize_feeds', true); - Minz_Request::forward(array( - 'c' => 'index', - 'a' => 'index' - ), true); + $content_notif = $error === true ? _t('feeds_imported_with_errors') : + _t('feeds_imported'); + Minz_Request::good($content_notif); } // What are you doing? you have to call this controller // with a POST request! - Minz_Request::forward(array( - 'c' => 'importExport', - 'a' => 'index' - )); + Minz_Request::forward(array('c' => 'importExport')); } private function guessFileType($filename) { @@ -362,17 +330,12 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $nb_files = count($export_files); if ($nb_files > 1) { - // If there are more than 1 file to export, we need an .zip + // If there are more than 1 file to export, we need a zip archive. try { $this->exportZip($export_files); } catch (Exception $e) { # Oops, there is no Zip extension! - $notif = array( - 'type' => 'bad', - 'content' => _t('export_no_zip_extension') - ); - Minz_Session::_param('notification', $notif); - Minz_Request::forward(array('c' => 'importExport'), true); + Minz_Request::bad(_t('export_no_zip_extension'), array('c' => 'importExport')); } } elseif ($nb_files === 1) { // Only one file? Guess its type and export it. @@ -386,7 +349,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->exportFile($filename, $export_files[$filename], $type); } else { - Minz_Request::forward(array('c' => 'importExport'), true); + Minz_Request::forward(array('c' => 'importExport')); } } } diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 755784522..2f745a04c 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -146,6 +146,31 @@ class Minz_Request { } } + + /** + * Wrappers good notifications + redirection + * @param $msg notification content + * @param $url url array to where we should be forwarded + */ + public static function good($msg, $url = array()) { + Minz_Session::_param('notification', array( + 'type' => 'good', + 'content' => $msg + )); + + Minz_Request::forward($url, true); + } + + public static function bad($msg, $url = array()) { + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => $msg + )); + + Minz_Request::forward($url, true); + } + + /** * Permet de récupérer une variable de type $_GET * @param $param nom de la variable -- cgit v1.2.3 From ed7d8aa44fe5a005380056b7d164fc53079506fb Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 12 Aug 2014 21:04:39 +0200 Subject: Use REQUEST_URI instead of SCRIPT_NAME for cookies See https://github.com/marienfressinaud/FreshRSS/commit/eceb7756cfcf117c2a18984291181a84697ed3cd#commitcomment-7345438 --- lib/Minz/Session.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index c859be2ed..efc8332e5 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -78,7 +78,7 @@ class Minz_Session { */ public static function keepCookie($l) { $cookie_dir = dirname( - empty($_SERVER['SCRIPT_NAME']) ? '' : $_SERVER['SCRIPT_NAME'] + empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'] ) . '/'; session_set_cookie_params($l, $cookie_dir, $_SERVER['HTTP_HOST'], false, true); -- cgit v1.2.3 From 08a9009c19b2ae84555340622c344a2da38ae019 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 12 Aug 2014 21:09:13 +0200 Subject: Fix a Minz_Session TODO Not use additional variable to manipulate session variables Fix coding style --- lib/Minz/Session.php | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index efc8332e5..906acc03c 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -2,17 +2,11 @@ /** * La classe Session gère la session utilisateur - * C'est un singleton */ class Minz_Session { - /** - * $session stocke les variables de session - */ - private static $session = array (); //TODO: Try to avoid having another local copy - /** * Initialise la session, avec un nom - * Le nom de session est utilisé comme nom pour les cookies et les URLs (i.e. PHPSESSID). + * Le nom de session est utilisé comme nom pour les cookies et les URLs(i.e. PHPSESSID). * Il ne doit contenir que des caractères alphanumériques ; il doit être court et descriptif */ public static function init($name) { @@ -22,10 +16,6 @@ class Minz_Session { // démarre la session session_name($name); session_start(); - - if (isset($_SESSION)) { - self::$session = $_SESSION; - } } @@ -34,8 +24,8 @@ class Minz_Session { * @param $p le paramètre à récupérer * @return la valeur de la variable de session, false si n'existe pas */ - public static function param ($p, $default = false) { - return isset(self::$session[$p]) ? self::$session[$p] : $default; + public static function param($p, $default = false) { + return isset($_SESSION[$p]) ? $_SESSION[$p] : $default; } @@ -44,13 +34,11 @@ class Minz_Session { * @param $p le paramètre à créer ou modifier * @param $v la valeur à attribuer, false pour supprimer */ - public static function _param ($p, $v = false) { + public static function _param($p, $v = false) { if ($v === false) { - unset ($_SESSION[$p]); - unset (self::$session[$p]); + unset($_SESSION[$p]); } else { $_SESSION[$p] = $v; - self::$session[$p] = $v; } } @@ -59,15 +47,15 @@ class Minz_Session { * Permet d'effacer une session * @param $force si à false, n'efface pas le paramètre de langue */ - public static function unset_session ($force = false) { - $language = self::param ('language'); + public static function unset_session($force = false) { + $language = self::param('language'); session_destroy(); - self::$session = array (); + $_SESSION = array(); if (!$force) { - self::_param ('language', $language); - Minz_Translate::reset (); + self::_param('language', $language); + Minz_Translate::reset(); } } -- cgit v1.2.3 From 22e2bf9239c3c5ee87a59910d88107ff359b24df Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 12 Aug 2014 21:12:02 +0200 Subject: Fix coding style of Minz_Request --- lib/Minz/Request.php | 84 ++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) (limited to 'lib') diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 2f745a04c..f9b8e2564 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -10,7 +10,7 @@ class Minz_Request { private static $controller_name = ''; private static $action_name = ''; - private static $params = array (); + private static $params = array(); private static $default_controller_name = 'index'; private static $default_action_name = 'index'; @@ -18,25 +18,25 @@ class Minz_Request { /** * Getteurs */ - public static function controllerName () { + public static function controllerName() { return self::$controller_name; } - public static function actionName () { + public static function actionName() { return self::$action_name; } - public static function params () { + public static function params() { return self::$params; } - static function htmlspecialchars_utf8 ($p) { + static function htmlspecialchars_utf8($p) { if (is_array($p)) { return array_map('self::htmlspecialchars_utf8', $p); } return htmlspecialchars($p, ENT_COMPAT, 'UTF-8'); } - public static function param ($key, $default = false, $specialchars = false) { - if (isset (self::$params[$key])) { + public static function param($key, $default = false, $specialchars = false) { + if (isset(self::$params[$key])) { $p = self::$params[$key]; - if(is_object($p) || $specialchars) { + if (is_object($p) || $specialchars) { return $p; } else { return self::htmlspecialchars_utf8($p); @@ -45,32 +45,32 @@ class Minz_Request { return $default; } } - public static function defaultControllerName () { + public static function defaultControllerName() { return self::$default_controller_name; } - public static function defaultActionName () { + public static function defaultActionName() { return self::$default_action_name; } /** * Setteurs */ - public static function _controllerName ($controller_name) { + public static function _controllerName($controller_name) { self::$controller_name = $controller_name; } - public static function _actionName ($action_name) { + public static function _actionName($action_name) { self::$action_name = $action_name; } - public static function _params ($params) { + public static function _params($params) { if (!is_array($params)) { - $params = array ($params); + $params = array($params); } self::$params = $params; } - public static function _param ($key, $value = false) { + public static function _param($key, $value = false) { if ($value === false) { - unset (self::$params[$key]); + unset(self::$params[$key]); } else { self::$params[$key] = $value; } @@ -79,14 +79,14 @@ class Minz_Request { /** * Initialise la Request */ - public static function init () { - self::magicQuotesOff (); + public static function init() { + self::magicQuotesOff(); } /** * Retourn le nom de domaine du site */ - public static function getDomainName () { + public static function getDomainName() { return $_SERVER['HTTP_HOST']; } @@ -94,7 +94,7 @@ class Minz_Request { * Détermine la base de l'url * @return la base de l'url */ - public static function getBaseUrl () { + public static function getBaseUrl() { $defaultBaseUrl = Minz_Configuration::baseUrl(); if (!empty($defaultBaseUrl)) { return $defaultBaseUrl; @@ -109,13 +109,13 @@ class Minz_Request { * Récupère l'URI de la requête * @return l'URI */ - public static function getURI () { - if (isset ($_SERVER['REQUEST_URI'])) { - $base_url = self::getBaseUrl (); + public static function getURI() { + if (isset($_SERVER['REQUEST_URI'])) { + $base_url = self::getBaseUrl(); $uri = $_SERVER['REQUEST_URI']; - $len_base_url = strlen ($base_url); - $real_uri = substr ($uri, $len_base_url); + $len_base_url = strlen($base_url); + $real_uri = substr($uri, $len_base_url); } else { $real_uri = ''; } @@ -129,16 +129,16 @@ class Minz_Request { * @param $redirect si vrai, force la redirection http * > sinon, le dispatcher recharge en interne */ - public static function forward ($url = array (), $redirect = false) { - $url = Minz_Url::checkUrl ($url); + public static function forward($url = array(), $redirect = false) { + $url = Minz_Url::checkUrl($url); if ($redirect) { - header ('Location: ' . Minz_Url::display ($url, 'php')); - exit (); + header('Location: ' . Minz_Url::display($url, 'php')); + exit(); } else { - self::_controllerName ($url['c']); - self::_actionName ($url['a']); - self::_params (array_merge ( + self::_controllerName($url['c']); + self::_actionName($url['a']); + self::_params(array_merge( self::$params, $url['params'] )); @@ -179,10 +179,10 @@ class Minz_Request { * $_GET si $param = false * $default si $_GET[$param] n'existe pas */ - public static function fetchGET ($param = false, $default = false) { + public static function fetchGET($param = false, $default = false) { if ($param === false) { return $_GET; - } elseif (isset ($_GET[$param])) { + } elseif (isset($_GET[$param])) { return $_GET[$param]; } else { return $default; @@ -197,10 +197,10 @@ class Minz_Request { * $_POST si $param = false * $default si $_POST[$param] n'existe pas */ - public static function fetchPOST ($param = false, $default = false) { + public static function fetchPOST($param = false, $default = false) { if ($param === false) { return $_POST; - } elseif (isset ($_POST[$param])) { + } elseif (isset($_POST[$param])) { return $_POST[$param]; } else { return $default; @@ -213,15 +213,15 @@ class Minz_Request { * $_POST * $_COOKIE */ - private static function magicQuotesOff () { - if (get_magic_quotes_gpc ()) { - $_GET = Minz_Helper::stripslashes_r ($_GET); - $_POST = Minz_Helper::stripslashes_r ($_POST); - $_COOKIE = Minz_Helper::stripslashes_r ($_COOKIE); + private static function magicQuotesOff() { + if (get_magic_quotes_gpc()) { + $_GET = Minz_Helper::stripslashes_r($_GET); + $_POST = Minz_Helper::stripslashes_r($_POST); + $_COOKIE = Minz_Helper::stripslashes_r($_COOKIE); } } - public static function isPost () { + public static function isPost() { return $_SERVER['REQUEST_METHOD'] === 'POST'; } } -- cgit v1.2.3 From ede94098be5d330d4bf120eb8064c5c87eed7ef0 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 12 Aug 2014 21:15:12 +0200 Subject: Fix missing REQUEST_METHOD Useful when executing actualize_script.php --- lib/Minz/Request.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index f9b8e2564..f3ecaf55c 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -222,6 +222,7 @@ class Minz_Request { } public static function isPost() { - return $_SERVER['REQUEST_METHOD'] === 'POST'; + return isset($_SERVER['REQUEST_METHOD']) && + $_SERVER['REQUEST_METHOD'] === 'POST'; } } -- cgit v1.2.3 From 7900c5e550acafaf0b877635840a8a270eb06078 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 12 Aug 2014 21:56:34 +0200 Subject: Move htmlspecialchars_utf8 from Request to Helper And remove html_chars_utf8 to use htmlspecialchars_utf8 instead in importExportController --- app/Controllers/importExportController.php | 10 +++++----- lib/Minz/Helper.php | 11 +++++++++++ lib/Minz/Request.php | 8 +------- lib/lib_rss.php | 4 ---- 4 files changed, 17 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 92b39b575..a8e2c2bc2 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -166,15 +166,15 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } // We get different useful information - $url = html_chars_utf8($feed_elt['xmlUrl']); - $name = html_chars_utf8($feed_elt['text']); + $url = Minz_Helper::htmlspecialchars_utf8($feed_elt['xmlUrl']); + $name = Minz_Helper::htmlspecialchars_utf8($feed_elt['text']); $website = ''; if (isset($feed_elt['htmlUrl'])) { - $website = html_chars_utf8($feed_elt['htmlUrl']); + $website = Minz_Helper::htmlspecialchars_utf8($feed_elt['htmlUrl']); } $description = ''; if (isset($feed_elt['description'])) { - $description = html_chars_utf8($feed_elt['description']); + $description = Minz_Helper::htmlspecialchars_utf8($feed_elt['description']); } $error = false; @@ -200,7 +200,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { private function addCategoryOpml($cat_elt, $parent_cat) { // Create a new Category object - $cat = new FreshRSS_Category(html_chars_utf8($cat_elt['text'])); + $cat = new FreshRSS_Category(Minz_Helper::htmlspecialchars_utf8($cat_elt['text'])); $id = $this->catDAO->addCategoryObject($cat); $error = ($id === false); diff --git a/lib/Minz/Helper.php b/lib/Minz/Helper.php index b058211d3..13bfdd93e 100644 --- a/lib/Minz/Helper.php +++ b/lib/Minz/Helper.php @@ -19,4 +19,15 @@ class Minz_Helper { return stripslashes($var); } } + + /** + * Wrapper for htmlspecialchars. + * Force UTf-8 value and can be used on array too. + */ + public static function htmlspecialchars_utf8($p) { + if (is_array($p)) { + return array_map('self::htmlspecialchars_utf8', $p); + } + return htmlspecialchars($p, ENT_COMPAT, 'UTF-8'); + } } diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index f3ecaf55c..52f53012f 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -27,19 +27,13 @@ class Minz_Request { public static function params() { return self::$params; } - static function htmlspecialchars_utf8($p) { - if (is_array($p)) { - return array_map('self::htmlspecialchars_utf8', $p); - } - return htmlspecialchars($p, ENT_COMPAT, 'UTF-8'); - } public static function param($key, $default = false, $specialchars = false) { if (isset(self::$params[$key])) { $p = self::$params[$key]; if (is_object($p) || $specialchars) { return $p; } else { - return self::htmlspecialchars_utf8($p); + return Minz_Helper::htmlspecialchars_utf8($p); } } else { return $default; diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 86c0a4ae4..823f53716 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -230,7 +230,3 @@ function cryptAvailable() { } return false; } - -function html_chars_utf8($str) { - return htmlspecialchars($str, ENT_COMPAT, 'UTF-8'); -} -- cgit v1.2.3 From 93b2a5f240b061103fce5cf563a3cd5cae2c6bfe Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 12 Aug 2014 21:59:07 +0200 Subject: Coding style in Minz_Helper --- lib/Minz/Helper.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/Minz/Helper.php b/lib/Minz/Helper.php index 13bfdd93e..8d8b177ae 100644 --- a/lib/Minz/Helper.php +++ b/lib/Minz/Helper.php @@ -12,9 +12,9 @@ class Minz_Helper { * Annule les effets des magic_quotes pour une variable donnée * @param $var variable à traiter (tableau ou simple variable) */ - public static function stripslashes_r ($var) { - if (is_array ($var)){ - return array_map (array ('Helper', 'stripslashes_r'), $var); + public static function stripslashes_r($var) { + if (is_array($var)){ + return array_map(array('Helper', 'stripslashes_r'), $var); } else { return stripslashes($var); } @@ -24,10 +24,10 @@ class Minz_Helper { * Wrapper for htmlspecialchars. * Force UTf-8 value and can be used on array too. */ - public static function htmlspecialchars_utf8($p) { - if (is_array($p)) { - return array_map('self::htmlspecialchars_utf8', $p); + public static function htmlspecialchars_utf8($var) { + if (is_array($var)) { + return array_map(array('Helper', 'htmlspecialchars_utf8'), $var); } - return htmlspecialchars($p, ENT_COMPAT, 'UTF-8'); + return htmlspecialchars($var, ENT_COMPAT, 'UTF-8'); } } -- cgit v1.2.3 From f2b1826b475a7ad71ee68b13d22c03259e631195 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 12 Aug 2014 22:27:07 +0200 Subject: Fix missing Minz_ prefix in Minz_Helper Nice bug :) It means another hidden bug is now corrected! --- lib/Minz/Helper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/Minz/Helper.php b/lib/Minz/Helper.php index 8d8b177ae..f4a547c4e 100644 --- a/lib/Minz/Helper.php +++ b/lib/Minz/Helper.php @@ -14,7 +14,7 @@ class Minz_Helper { */ public static function stripslashes_r($var) { if (is_array($var)){ - return array_map(array('Helper', 'stripslashes_r'), $var); + return array_map(array('Minz_Helper', 'stripslashes_r'), $var); } else { return stripslashes($var); } @@ -26,7 +26,7 @@ class Minz_Helper { */ public static function htmlspecialchars_utf8($var) { if (is_array($var)) { - return array_map(array('Helper', 'htmlspecialchars_utf8'), $var); + return array_map(array('Minz_Helper', 'htmlspecialchars_utf8'), $var); } return htmlspecialchars($var, ENT_COMPAT, 'UTF-8'); } -- cgit v1.2.3 From ee1b8f6f72e8c2cbd3e0ad7b4322a4bb6863c028 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 13 Aug 2014 00:09:48 +0200 Subject: Long term cookie to keep session open Token system https://github.com/marienfressinaud/FreshRSS/issues/465 --- app/Controllers/indexController.php | 41 +++++++++++++++++++++++++++++-------- app/FreshRSS.php | 28 ++++++++++++++++++++++--- data/tokens/.gitignore | 1 + data/tokens/index.html | 13 ++++++++++++ lib/Minz/Session.php | 19 ++++++++++++----- 5 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 data/tokens/.gitignore create mode 100644 data/tokens/index.html (limited to 'lib') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index b907c8eed..dd5b91e47 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -295,10 +295,39 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('passwordHash'); } + private static function makeLongTermCookie($username, $passwordHash) { + do { + $token = sha1(Minz_Configuration::salt() . $username . uniqid(mt_rand(), true)); + $tokenFile = DATA_PATH . '/tokens/' . $token . '.txt'; + } while (file_exists($tokenFile)); + if (@file_put_contents($tokenFile, $username . "\t" . $passwordHash) === false) { + return false; + } + $expire = time() + 2629744; //1 month //TODO: Use a configuration instead + Minz_Session::setLongTermCookie('FreshRSS_login', $token, $expire); + Minz_Session::_param('token', $token); + return $token; + } + + private static function deleteLongTermCookie() { + Minz_Session::deleteLongTermCookie('FreshRSS_login'); + $token = Minz_Session::param('token', null); + if (ctype_alnum($token)) { + @unlink(DATA_PATH . '/tokens/' . $token . '.txt'); + } + Minz_Session::_param('token'); + if (rand(0, 10) === 1) { + self::purgeTokens(); + } + } + + private static function purgeTokens() { + //TODO: Delete old token files + } + public function formLoginAction () { if (Minz_Request::isPost()) { $ok = false; - $keep_logged_in = Minz_Request::param('keep_logged_in', false); $nonce = Minz_Session::param('nonce'); $username = Minz_Request::param('username', ''); $c = Minz_Request::param('challenge', ''); @@ -313,10 +342,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { if ($ok) { Minz_Session::_param('currentUser', $username); Minz_Session::_param('passwordHash', $s); - if ($keep_logged_in) { - // New cookie with a lifetime of 1 month. - Minz_Session::keepCookie(2592000); - Minz_Session::regenerateID(); + if (Minz_Request::param('keep_logged_in', false)) { + self::makeLongTermCookie($username, $s); } } else { Minz_Log::record('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c, Minz_Log::WARNING); @@ -377,9 +404,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('currentUser'); Minz_Session::_param('mail'); Minz_Session::_param('passwordHash'); - Minz_Session::keepCookie(0); - Minz_Session::regenerateID(); - + self::deleteLongTermCookie(); Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } } diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 7c333b090..30f711e20 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -20,13 +20,35 @@ class FreshRSS extends Minz_FrontController { $this->loadNotifications(); } + private static function getCredentialsFromLongTermCookie() { + $token = Minz_Session::getLongTermCookie('FreshRSS_login'); + if (!ctype_alnum($token)) { + return array(); + } + $tokenFile = DATA_PATH . '/tokens/' . $token . '.txt'; + $mtime = @filemtime($tokenFile); + if ($mtime + 2629744 < time()) { //1 month //TODO: Use a configuration instead + @unlink($tokenFile); + return array(); //Expired or token does not exist + } + $credentials = @file_get_contents($tokenFile); + return $credentials === false ? array() : explode("\t", $credentials, 2); + } + private function accessControl($currentUser) { if ($currentUser == '') { switch (Minz_Configuration::authType()) { case 'form': - $currentUser = Minz_Configuration::defaultUser(); - Minz_Session::_param('passwordHash'); - $loginOk = false; + $credentials = self::getCredentialsFromLongTermCookie(); + if (isset($credentials[1])) { + $currentUser = trim($credentials[0]); + Minz_Session::_param('passwordHash', trim($credentials[1])); + } + $loginOk = $currentUser != ''; + if (!$loginOk) { + $currentUser = Minz_Configuration::defaultUser(); + Minz_Session::_param('passwordHash'); + } break; case 'http_auth': $currentUser = httpAuthUser(); diff --git a/data/tokens/.gitignore b/data/tokens/.gitignore new file mode 100644 index 000000000..2211df63d --- /dev/null +++ b/data/tokens/.gitignore @@ -0,0 +1 @@ +*.txt diff --git a/data/tokens/index.html b/data/tokens/index.html new file mode 100644 index 000000000..85faaa37e --- /dev/null +++ b/data/tokens/index.html @@ -0,0 +1,13 @@ + + + + + +Redirection + + + + +

    Redirection

    + + diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index 906acc03c..af4de75bb 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -65,11 +65,8 @@ class Minz_Session { * @param $l la durée de vie */ public static function keepCookie($l) { - $cookie_dir = dirname( - empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'] - ) . '/'; - session_set_cookie_params($l, $cookie_dir, $_SERVER['HTTP_HOST'], - false, true); + $cookie_dir = empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI']; + session_set_cookie_params($l, $cookie_dir, '', false, true); } @@ -81,4 +78,16 @@ class Minz_Session { session_regenerate_id(true); } + public static function deleteLongTermCookie($name) { + setcookie($name, '', 1, '', '', false, true); + } + + public static function setLongTermCookie($name, $value, $expire) { + setcookie($name, $value, $expire, '', '', false, true); + } + + public static function getLongTermCookie($name) { + return isset($_COOKIE[$name]) ? $_COOKIE[$name] : null; + } + } -- cgit v1.2.3 From a126d99b3c87c12d6da86a32f0615ad36ec99d60 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 30 Aug 2014 18:31:50 +0200 Subject: Bug referer for systems with non-standard HTTP port Now tests also for the scheme and port, which must be identical to the ones in the referer. https://github.com/marienfressinaud/FreshRSS/issues/565#issuecomment-53916915 https://github.com/marienfressinaud/FreshRSS/issues/554 --- app/FreshRSS.php | 3 +-- lib/Minz/Request.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 30f711e20..cf6390f68 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -6,8 +6,7 @@ class FreshRSS extends Minz_FrontController { } $loginOk = $this->accessControl(Minz_Session::param('currentUser', '')); $this->loadParamsView(); - if (Minz_Request::isPost() && (empty($_SERVER['HTTP_REFERER']) || - Minz_Request::getDomainName() !== parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST))) { + if (Minz_Request::isPost() && !Minz_Request::isRefererFromSameDomain()) { $loginOk = false; //Basic protection against XSRF attacks Minz_Error::error( 403, diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 52f53012f..ec4e25a6b 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -84,6 +84,20 @@ class Minz_Request { return $_SERVER['HTTP_HOST']; } + public static function isRefererFromSameDomain() { + if (empty($_SERVER['HTTP_REFERER'])) { + return false; + } + $host = parse_url(((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https://' : 'http://') . + (empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST'])); + $referer = parse_url($_SERVER['HTTP_REFERER']); + if (empty($host['scheme']) || empty($referer['scheme']) || $host['scheme'] !== $referer['scheme'] || + empty($host['host']) || empty($referer['host']) || $host['host'] !== $referer['host']) { + return false; + } + return (isset($host['port']) ? $host['port'] : 0) === (isset($referer['port']) ? $referer['port'] : 0); + } + /** * Détermine la base de l'url * @return la base de l'url -- cgit v1.2.3 From c3fd8877c021b86180b3bea4d4260e6478f0558e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 6 Sep 2014 16:04:49 +0200 Subject: Bug warning in case of invalid CDATA --- lib/SimplePie/SimplePie/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/SimplePie/SimplePie/Parser.php b/lib/SimplePie/SimplePie/Parser.php index 9300b4ba9..7fb7bd9be 100644 --- a/lib/SimplePie/SimplePie/Parser.php +++ b/lib/SimplePie/SimplePie/Parser.php @@ -142,7 +142,7 @@ class SimplePie_Parser $dom = new DOMDocument(); $dom->recover = true; $dom->strictErrorChecking = false; - $dom->loadXML($data); + @$dom->loadXML($data); $this->encoding = $encoding = $dom->encoding = 'UTF-8'; $data2 = $dom->saveXML(); if (function_exists('mb_convert_encoding')) -- cgit v1.2.3 From 17d1e67e822dd7b903328195b5957eaf38a6f47b Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 16 Sep 2014 19:01:24 +0200 Subject: Fix sqlite bug ON DELETE CASCADE Foreign key constraints are not enabled by default. See https://github.com/marienfressinaud/FreshRSS/issues/579 See http://stackoverflow.com/questions/13534040/sqlite3-foreign-keys-on-pdo --- lib/Minz/ModelPdo.php | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index 45a1e9451..b4bfca746 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -77,6 +77,9 @@ class Minz_ModelPdo { $db['password'], $driver_options ); + if ($type === 'sqlite') { + $this->bd->exec('PRAGMA foreign_keys = ON;'); + } self::$sharedBd = $this->bd; } catch (Exception $e) { throw new Minz_PDOConnectionException( -- cgit v1.2.3 From fc7d2a0bf271e43a9e6001dccef61ec0762eb840 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Wed, 17 Sep 2014 14:21:56 +0200 Subject: Move http referer test into lib_rss Rename method (coding style) Change in app/FreshRSS.php Improve test of app/install.php --- app/FreshRSS.php | 2 +- app/install.php | 2 +- lib/Minz/Request.php | 14 -------------- lib/lib_rss.php | 14 ++++++++++++++ 4 files changed, 16 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 6cca27f78..347b8392f 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -6,7 +6,7 @@ class FreshRSS extends Minz_FrontController { } $loginOk = $this->accessControl(Minz_Session::param('currentUser', '')); $this->loadParamsView(); - if (Minz_Request::isPost() && !Minz_Request::isRefererFromSameDomain()) { + if (Minz_Request::isPost() && !is_referer_from_same_domain()) { $loginOk = false; //Basic protection against XSRF attacks Minz_Error::error( 403, diff --git a/app/install.php b/app/install.php index f31764545..362ffec48 100644 --- a/app/install.php +++ b/app/install.php @@ -307,7 +307,7 @@ function checkStep1() { $log = LOG_PATH && is_writable(LOG_PATH); $favicons = is_writable(DATA_PATH . '/favicons'); $persona = is_writable(DATA_PATH . '/persona'); - $http_referer = !empty($_SERVER['HTTP_REFERER']); + $http_referer = is_referer_from_same_domain(); return array( 'php' => $php ? 'ok' : 'ko', diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index ec4e25a6b..52f53012f 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -84,20 +84,6 @@ class Minz_Request { return $_SERVER['HTTP_HOST']; } - public static function isRefererFromSameDomain() { - if (empty($_SERVER['HTTP_REFERER'])) { - return false; - } - $host = parse_url(((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https://' : 'http://') . - (empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST'])); - $referer = parse_url($_SERVER['HTTP_REFERER']); - if (empty($host['scheme']) || empty($referer['scheme']) || $host['scheme'] !== $referer['scheme'] || - empty($host['host']) || empty($referer['host']) || $host['host'] !== $referer['host']) { - return false; - } - return (isset($host['port']) ? $host['port'] : 0) === (isset($referer['port']) ? $referer['port'] : 0); - } - /** * Détermine la base de l'url * @return la base de l'url diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 823f53716..31c9cdbc1 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -230,3 +230,17 @@ function cryptAvailable() { } return false; } + +function is_referer_from_same_domain() { + if (empty($_SERVER['HTTP_REFERER'])) { + return false; + } + $host = parse_url(((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https://' : 'http://') . + (empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST'])); + $referer = parse_url($_SERVER['HTTP_REFERER']); + if (empty($host['scheme']) || empty($referer['scheme']) || $host['scheme'] !== $referer['scheme'] || + empty($host['host']) || empty($referer['host']) || $host['host'] !== $referer['host']) { + return false; + } + return (isset($host['port']) ? $host['port'] : 0) === (isset($referer['port']) ? $referer['port'] : 0); +} -- cgit v1.2.3 From d284958d52f633738e8cc736f7f4a50e0c984ecf Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Fri, 19 Sep 2014 19:44:40 +0200 Subject: Improve redirection when deleting an idle feed --- app/Controllers/feedController.php | 9 ++++++--- app/views/stats/idle.phtml | 6 +++++- lib/Minz/Request.php | 5 +++++ 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 3326b2059..65d4b3a37 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -412,10 +412,13 @@ class FreshRSS_feed_Controller extends Minz_ActionController { Minz_Session::_param ('notification', $notif); - if ($type == 'category') { - Minz_Request::forward (array ('c' => 'configure', 'a' => 'categorize'), true); + $redirect_url = Minz_Request::param('r', false, true); + if ($redirect_url) { + Minz_Request::forward($redirect_url); + } elseif ($type == 'category') { + Minz_Request::forward(array ('c' => 'configure', 'a' => 'categorize'), true); } else { - Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed'), true); + Minz_Request::forward(array ('c' => 'configure', 'a' => 'feed'), true); } } } diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml index 3ce8d3d3e..6f3d4a117 100644 --- a/app/views/stats/idle.phtml +++ b/app/views/stats/idle.phtml @@ -6,6 +6,10 @@

    'stats', 'a' => 'idle'), + 'php', true + )); $nothing = true; foreach ($this->idleFeeds as $period => $feeds) { if (!empty($feeds)) { @@ -22,7 +26,7 @@
    - +
  • diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 52f53012f..f7a24c026 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -124,6 +124,11 @@ class Minz_Request { * > sinon, le dispatcher recharge en interne */ public static function forward($url = array(), $redirect = false) { + if (!is_array($url)) { + header('Location: ' . $url); + exit(); + } + $url = Minz_Url::checkUrl($url); if ($redirect) { -- cgit v1.2.3 From 6c8b36f04ea1bc2c022c331bb0980b6c9dccb83c Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 5 Oct 2014 15:55:20 +0200 Subject: Let's begin the big refactoring! Minz_Translate::t\s? replaces by _t See https://github.com/marienfressinaud/FreshRSS/issues/655 --- app/Controllers/entryController.php | 8 ++-- app/Controllers/errorController.php | 4 +- app/Controllers/feedController.php | 30 ++++++------- app/Controllers/indexController.php | 22 +++++----- app/Controllers/statsController.php | 4 +- app/Controllers/usersController.php | 8 ++-- app/FreshRSS.php | 2 +- app/Models/CategoryDAO.php | 2 +- app/Models/StatsDAO.php | 2 +- app/Models/UserDAO.php | 4 +- app/layout/aside_stats.phtml | 14 +++--- app/views/configure/archiving.phtml | 36 ++++++++-------- app/views/configure/display.phtml | 44 +++++++++---------- app/views/configure/reading.phtml | 64 +++++++++++++-------------- app/views/configure/sharing.phtml | 24 +++++------ app/views/configure/users.phtml | 74 ++++++++++++++++---------------- app/views/error/index.phtml | 2 +- app/views/feed/add.phtml | 34 +++++++-------- app/views/helpers/feed/update.phtml | 60 +++++++++++++------------- app/views/helpers/javascript_vars.phtml | 8 ++-- app/views/helpers/logs_pagination.phtml | 8 ++-- app/views/helpers/view/normal_view.phtml | 14 +++--- app/views/helpers/view/reader_view.phtml | 2 +- app/views/helpers/view/rss_view.phtml | 2 +- app/views/index/about.phtml | 26 +++++------ app/views/index/logs.phtml | 8 ++-- lib/lib_rss.php | 8 ++-- 27 files changed, 256 insertions(+), 258 deletions(-) (limited to 'lib') diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index ab66d9198..048ac1c69 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -5,7 +5,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { if (!$this->view->loginOk) { Minz_Error::error ( 403, - array ('error' => array (Minz_Translate::t ('access_denied'))) + array ('error' => array (_t('access_denied'))) ); } @@ -75,7 +75,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $notif = array ( 'type' => 'good', - 'content' => Minz_Translate::t ('feeds_marked_read') + 'content' => _t('feeds_marked_read') ); Minz_Session::_param ('notification', $notif); } else { @@ -111,7 +111,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $notif = array ( 'type' => 'good', - 'content' => Minz_Translate::t ('optimization_complete') + 'content' => _t('optimization_complete') ); Minz_Session::_param ('notification', $notif); } @@ -155,7 +155,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $notif = array( 'type' => 'good', - 'content' => Minz_Translate::t('purge_completed', $nbTotal) + 'content' => _t('purge_completed', $nbTotal) ); Minz_Session::_param('notification', $notif); diff --git a/app/Controllers/errorController.php b/app/Controllers/errorController.php index 922650b3d..64a5c06fd 100644 --- a/app/Controllers/errorController.php +++ b/app/Controllers/errorController.php @@ -24,11 +24,11 @@ class FreshRSS_error_Controller extends Minz_ActionController { if ($this->view->errorMessage == '') { switch(Minz_Request::param('code')) { case 403: - $this->view->errorMessage = Minz_Translate::t('forbidden_access'); + $this->view->errorMessage = _t('forbidden_access'); break; case 404: default: - $this->view->errorMessage = Minz_Translate::t('page_not_found'); + $this->view->errorMessage = _t('page_not_found'); break; } } diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 92ce40634..029f9fa68 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -15,7 +15,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { ) { Minz_Error::error ( 403, - array ('error' => array (Minz_Translate::t ('access_denied'))) + array ('error' => array (_t('access_denied'))) ); } } @@ -84,7 +84,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { // on est déjà abonné à ce flux $notif = array ( 'type' => 'bad', - 'content' => Minz_Translate::t ('already_subscribed', $feed->name ()) + 'content' => _t('already_subscribed', $feed->name ()) ); Minz_Session::_param ('notification', $notif); } else { @@ -93,7 +93,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { // problème au niveau de la base de données $notif = array ( 'type' => 'bad', - 'content' => Minz_Translate::t ('feed_not_added', $feed->name ()) + 'content' => _t('feed_not_added', $feed->name ()) ); Minz_Session::_param ('notification', $notif); } else { @@ -131,7 +131,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { // ok, ajout terminé $notif = array ( 'type' => 'good', - 'content' => Minz_Translate::t ('feed_added', $feed->name ()) + 'content' => _t('feed_added', $feed->name ()) ); Minz_Session::_param ('notification', $notif); @@ -143,14 +143,14 @@ class FreshRSS_feed_Controller extends Minz_ActionController { Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); $notif = array ( 'type' => 'bad', - 'content' => Minz_Translate::t ('invalid_url', $url) + 'content' => _t('invalid_url', $url) ); Minz_Session::_param ('notification', $notif); } catch (FreshRSS_Feed_Exception $e) { Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); $notif = array ( 'type' => 'bad', - 'content' => Minz_Translate::t ('internal_problem_feed', Minz_Url::display(array('a' => 'logs'))) + 'content' => _t('internal_problem_feed', Minz_Url::display(array('a' => 'logs'))) ); Minz_Session::_param ('notification', $notif); } catch (Minz_FileNotExistException $e) { @@ -158,7 +158,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); $notif = array ( 'type' => 'bad', - 'content' => Minz_Translate::t ('internal_problem_feed', Minz_Url::display(array('a' => 'logs'))) + 'content' => _t('internal_problem_feed', Minz_Url::display(array('a' => 'logs'))) ); Minz_Session::_param ('notification', $notif); } @@ -170,7 +170,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } else { // GET request so we must ask confirmation to user - Minz_View::prependTitle(Minz_Translate::t('add_rss_feed') . ' · '); + Minz_View::prependTitle(_t('add_rss_feed') . ' · '); $this->view->categories = $this->catDAO->listCategories(false); $this->view->feed = new FreshRSS_Feed($url); try { @@ -186,9 +186,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { // Already subscribe so we redirect to the feed configuration page $notif = array( 'type' => 'bad', - 'content' => Minz_Translate::t( - 'already_subscribed', $feed->name() - ) + 'content' => _t('already_subscribed', $feed->name()) ); Minz_Session::_param('notification', $notif); @@ -210,7 +208,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $n = $feedDAO->truncate($id); $notif = array( 'type' => $n === false ? 'bad' : 'good', - 'content' => Minz_Translate::t ('n_entries_deleted', $n) + 'content' => _t('n_entries_deleted', $n) ); Minz_Session::_param ('notification', $notif); invalidateHttpCache(); @@ -336,19 +334,19 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feed = reset ($feeds); $notif = array ( 'type' => 'good', - 'content' => Minz_Translate::t ('feed_actualized', $feed->name ()) + 'content' => _t('feed_actualized', $feed->name ()) ); } elseif ($flux_update > 1) { // plusieurs flux on été mis à jour $notif = array ( 'type' => 'good', - 'content' => Minz_Translate::t ('n_feeds_actualized', $flux_update) + 'content' => _t('n_feeds_actualized', $flux_update) ); } else { // aucun flux n'a été mis à jour, oups $notif = array ( 'type' => 'good', - 'content' => Minz_Translate::t ('no_feed_to_refresh') + 'content' => _t('no_feed_to_refresh') ); } @@ -370,7 +368,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { // ressenti utilisateur $notif = array ( 'type' => 'good', - 'content' => Minz_Translate::t ('feeds_actualized') + 'content' => _t('feeds_actualized') ); Minz_Session::_param ('notification', $notif); // et on désactive le layout car ne sert à rien diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 1b6563bb3..346739523 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -14,7 +14,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { if ($output === 'rss' && !$token_is_ok) { Minz_Error::error ( 403, - array ('error' => array (Minz_Translate::t ('access_denied'))) + array ('error' => array (_t('access_denied'))) ); return; } elseif ($output !== 'rss') { @@ -62,7 +62,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Log::record ('Not found [' . $getType . '][' . $getId . ']', Minz_Log::DEBUG); Minz_Error::error ( 404, - array ('error' => array (Minz_Translate::t ('page_not_found'))) + array ('error' => array (_t('page_not_found'))) ); return; } @@ -145,7 +145,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Log::record ($e->getMessage (), Minz_Log::NOTICE); Minz_Error::error ( 404, - array ('error' => array (Minz_Translate::t ('page_not_found'))) + array ('error' => array (_t('page_not_found'))) ); } } @@ -158,12 +158,12 @@ class FreshRSS_index_Controller extends Minz_ActionController { private function checkAndProcessType ($getType, $getId) { switch ($getType) { case 'a': - $this->view->currentName = Minz_Translate::t ('your_rss_feeds'); + $this->view->currentName = _t('your_rss_feeds'); $this->nb_not_read_cat = $this->view->nb_not_read; $this->view->get_c = $getType; return true; case 's': - $this->view->currentName = Minz_Translate::t ('your_favorites'); + $this->view->currentName = _t('your_favorites'); $this->nb_not_read_cat = $this->view->nb_favorites['unread']; $this->view->get_c = $getType; return true; @@ -202,18 +202,18 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function aboutAction () { - Minz_View::prependTitle (Minz_Translate::t ('about') . ' · '); + Minz_View::prependTitle (_t('about') . ' · '); } public function logsAction () { if (!$this->view->loginOk) { Minz_Error::error ( 403, - array ('error' => array (Minz_Translate::t ('access_denied'))) + array ('error' => array (_t('access_denied'))) ); } - Minz_View::prependTitle (Minz_Translate::t ('logs') . ' · '); + Minz_View::prependTitle (_t('logs') . ' · '); if (Minz_Request::isPost ()) { FreshRSS_LogDAO::truncate(); @@ -279,7 +279,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } else { $res = array (); $res['status'] = 'failure'; - $res['reason'] = $reason == '' ? Minz_Translate::t ('invalid_login') : $reason; + $res['reason'] = $reason == '' ? _t('invalid_login') : $reason; Minz_Log::record ('Persona: ' . $res['reason'], Minz_Log::WARNING); } @@ -368,7 +368,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { if (!$ok) { $notif = array( 'type' => 'bad', - 'content' => Minz_Translate::t('invalid_login') + 'content' => _t('invalid_login') ); Minz_Session::_param('notification', $notif); } @@ -403,7 +403,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } elseif (!Minz_Configuration::canLogIn()) { Minz_Error::error ( 403, - array ('error' => array (Minz_Translate::t ('access_denied'))) + array ('error' => array (_t('access_denied'))) ); } invalidateHttpCache(); diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php index 3069be34d..4adb5e75d 100644 --- a/app/Controllers/statsController.php +++ b/app/Controllers/statsController.php @@ -120,11 +120,11 @@ class FreshRSS_stats_Controller extends Minz_ActionController { public function firstAction() { if (!$this->view->loginOk) { Minz_Error::error( - 403, array('error' => array(Minz_Translate::t('access_denied'))) + 403, array('error' => array(_t('access_denied'))) ); } - Minz_View::prependTitle(Minz_Translate::t('stats') . ' · '); + Minz_View::prependTitle(_t('stats') . ' · '); } } diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index a9e6c32bc..8eb82f5d5 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -8,7 +8,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { if (!$this->view->loginOk) { Minz_Error::error( 403, - array('error' => array(Minz_Translate::t('access_denied'))) + array('error' => array(_t('access_denied'))) ); } } @@ -90,7 +90,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { $notif = array( 'type' => $ok ? 'good' : 'bad', - 'content' => Minz_Translate::t($ok ? 'configuration_updated' : 'error_occurred') + 'content' => _t($ok ? 'configuration_updated' : 'error_occurred') ); Minz_Session::_param('notification', $notif); } @@ -162,7 +162,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { $notif = array( 'type' => $ok ? 'good' : 'bad', - 'content' => Minz_Translate::t($ok ? 'user_created' : 'error_occurred', $new_user_name) + 'content' => _t($ok ? 'user_created' : 'error_occurred', $new_user_name) ); Minz_Session::_param('notification', $notif); } @@ -194,7 +194,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { $notif = array( 'type' => $ok ? 'good' : 'bad', - 'content' => Minz_Translate::t($ok ? 'user_deleted' : 'error_occurred', $username) + 'content' => _t($ok ? 'user_deleted' : 'error_occurred', $username) ); Minz_Session::_param('notification', $notif); } diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 58aac4059..16f64fd8b 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -10,7 +10,7 @@ class FreshRSS extends Minz_FrontController { $loginOk = false; //Basic protection against XSRF attacks Minz_Error::error( 403, - array('error' => array(Minz_Translate::t('access_denied') . ' [HTTP_REFERER=' . + array('error' => array(_t('access_denied') . ' [HTTP_REFERER=' . htmlspecialchars(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']) . ']')) ); } diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index f11f87f47..5def50a26 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -134,7 +134,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { $def_cat = $this->searchById (1); if ($def_cat == null) { - $cat = new FreshRSS_Category (Minz_Translate::t ('default_category')); + $cat = new FreshRSS_Category (_t('default_category')); $cat->_id (1); $values = array ( diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php index 08dd4cd5c..113944508 100644 --- a/app/Models/StatsDAO.php +++ b/app/Models/StatsDAO.php @@ -416,7 +416,7 @@ SQL; */ private function convertToTranslatedJson($data = array()) { $translated = array_map(function ($a) { - return Minz_Translate::t($a); + return _t($a); }, $data); return json_encode($translated); diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index 9f64fb4a7..0c96d7175 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -9,7 +9,7 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { $ok = false; if (defined('SQL_CREATE_TABLES')) { //E.g. MySQL - $sql = sprintf(SQL_CREATE_TABLES, $db['prefix'] . $username . '_', Minz_Translate::t('default_category')); + $sql = sprintf(SQL_CREATE_TABLES, $db['prefix'] . $username . '_', _t('default_category')); $stm = $userPDO->bd->prepare($sql); $ok = $stm && $stm->execute(); } else { //E.g. SQLite @@ -17,7 +17,7 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { if (is_array($SQL_CREATE_TABLES)) { $ok = true; foreach ($SQL_CREATE_TABLES as $instruction) { - $sql = sprintf($instruction, '', Minz_Translate::t('default_category')); + $sql = sprintf($instruction, '', _t('default_category')); $stm = $userPDO->bd->prepare($sql); $ok &= ($stm && $stm->execute()); } diff --git a/app/layout/aside_stats.phtml b/app/layout/aside_stats.phtml index fbfb9d84d..1cd31a99c 100644 --- a/app/layout/aside_stats.phtml +++ b/app/layout/aside_stats.phtml @@ -1,12 +1,12 @@ diff --git a/app/views/configure/archiving.phtml b/app/views/configure/archiving.phtml index c9cc7fe02..3180fe933 100644 --- a/app/views/configure/archiving.phtml +++ b/app/views/configure/archiving.phtml @@ -1,31 +1,31 @@ partial('aside_configure'); ?>
    - +
    - -

    + +

    - +
    - -   + +  
    - +
    () + ?> ()
    - +
    () + ?> ()
    - - + +
    - +
    -

    +

    -

    nb_total), ' ', Minz_Translate::t('articles'), ', ', formatBytes($this->size_user); ?>

    +

    nb_total), ' ', _t('articles'), ', ', formatBytes($this->size_user); ?>

    - - + +
    -

    +

    size_total); ?>

    diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 8eb3a156b..f1b80ab15 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -1,13 +1,13 @@ partial ('aside_configure'); ?>
    - + - +
    - +
    themes as $theme) { ?>conf->content_width; ?>
    - +
    - + - - - - - + + + + + - + @@ -80,7 +80,7 @@ - + @@ -93,16 +93,16 @@
    - +
    - +
    - - + +
    diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml index 8b2da2a28..7e4efc264 100644 --- a/app/views/configure/reading.phtml +++ b/app/views/configure/reading.phtml @@ -1,13 +1,13 @@ partial ('aside_configure'); ?>
    - +
    - +
    - +
    @@ -15,22 +15,22 @@
    - +
    - +
    @@ -50,7 +50,7 @@
    @@ -59,8 +59,8 @@
    @@ -69,8 +69,8 @@
    @@ -79,8 +79,8 @@
    @@ -89,8 +89,8 @@
    @@ -99,8 +99,8 @@
    @@ -109,48 +109,48 @@
    - +
    - +
    - - + +
    diff --git a/app/views/configure/sharing.phtml b/app/views/configure/sharing.phtml index 02ce331da..ee276a94e 100644 --- a/app/views/configure/sharing.phtml +++ b/app/views/configure/sharing.phtml @@ -1,7 +1,7 @@ partial ('aside_configure'); ?>
    - + @@ -9,28 +9,28 @@ data-advanced='
    - - + +
    - +
    '> - + conf->sharing as $key => $sharing): ?> conf->shares[$sharing['type']]; ?>
    ' />
    - - + +
    - + @@ -42,7 +42,7 @@
    @@ -51,8 +51,8 @@
    - - + +
    diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml index 272896fb2..04e662fa3 100644 --- a/app/views/configure/users.phtml +++ b/app/views/configure/users.phtml @@ -1,36 +1,36 @@ partial('aside_configure'); ?>
    - +
    - +
    - +
    - +
    />
    - +
    - +
    /> @@ -41,36 +41,36 @@
    - + conf->mail_login; ?>
    placeholder="alice@example.net" /> - +
    - - + +
    - +
    - +
    @@ -80,7 +80,7 @@
    @@ -90,7 +90,7 @@
    @@ -100,7 +100,7 @@
    @@ -108,12 +108,12 @@
    - + conf->token; ?>
    - /> - +
    @@ -123,24 +123,24 @@
    - - + +
    - +
    - +
    conf->availableLanguages (); ?> @@ -173,25 +173,25 @@
    - +
    - +
    - +
    - + conf->mail_login; ?>
    @@ -200,8 +200,8 @@
    - - + +
    diff --git a/app/views/error/index.phtml b/app/views/error/index.phtml index ef4fbd39d..5e1949800 100644 --- a/app/views/error/index.phtml +++ b/app/views/error/index.phtml @@ -3,7 +3,7 @@

    code; ?>

    errorMessage; ?>
    - +

    diff --git a/app/views/feed/add.phtml b/app/views/feed/add.phtml index 849dacac6..17e52a571 100644 --- a/app/views/feed/add.phtml +++ b/app/views/feed/add.phtml @@ -1,16 +1,16 @@ feed) { ?>
    -

    +

    load_ok) { ?> -

    +

    - + load_ok) { ?>
    - +
    @@ -18,7 +18,7 @@ feed->description(); if ($desc != '') { ?>
    - +
    @@ -26,7 +26,7 @@
    - +
    feed->website(); ?> @@ -35,17 +35,17 @@
    - +
    - +
    - +
    - +
    - + feed->httpAuth(false); ?>
    - +
    - +
    - +
    - - + +
    diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml index 678c5f132..8bd645d11 100644 --- a/app/views/helpers/feed/update.phtml +++ b/app/views/helpers/feed/update.phtml @@ -12,27 +12,27 @@ feed->nbEntries (); ?> feed->inError ()) { ?> -

    +

    -

    +

    - +
    - +
    - +
    - +
    @@ -41,18 +41,18 @@
    - +
    - +
    - +
    feed->priority () > 0 ? ' checked="checked"' : ''; ?> /> - +
    @@ -83,7 +83,7 @@
    - +
    @@ -96,21 +96,21 @@
    - +
    - +
    - +
    - +
    @@ -150,24 +150,24 @@
    - - + +
    - +
    - +
    - - + +
    - - + +
    diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index 4f7e3db0c..ba02b9fad 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -52,10 +52,10 @@ echo 'authType="', $authType, '",', 'url_login="', _url ('index', 'login'), '",', 'url_logout="', _url ('index', 'logout'), '",'; -echo 'str_confirmation_default="', Minz_Translate::t('confirm_action'), '"', ",\n"; -echo 'str_notif_title_articles="', Minz_Translate::t('notif_title_new_articles'), '"', ",\n"; -echo 'str_notif_body_articles="', Minz_Translate::t('notif_body_new_articles'), '"', ",\n"; -echo 'str_category_empty="', Minz_Translate::t('category_empty'), '"', ",\n"; +echo 'str_confirmation_default="', _t('confirm_action'), '"', ",\n"; +echo 'str_notif_title_articles="', _t('notif_title_new_articles'), '"', ",\n"; +echo 'str_notif_body_articles="', _t('notif_body_new_articles'), '"', ",\n"; +echo 'str_category_empty="', _t('category_empty'), '"', ",\n"; echo 'html5_notif_timeout=', $this->conf->html5_notif_timeout,",\n"; diff --git a/app/views/helpers/logs_pagination.phtml b/app/views/helpers/logs_pagination.phtml index e3d14810e..191cfa8de 100755 --- a/app/views/helpers/logs_pagination.phtml +++ b/app/views/helpers/logs_pagination.phtml @@ -9,14 +9,14 @@
  • currentPage > 1) { ?> - « + «
  • currentPage - 1; ?>
  • currentPage > 1) { ?> - +
  • @@ -34,13 +34,13 @@ currentPage + 1; ?>
  • currentPage < $this->nbPage) { ?> - +
  • nbPage; ?>
  • currentPage < $this->nbPage) { ?> - » + »
  • diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index e469edf58..ee745144f 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -30,12 +30,12 @@ if (!empty($this->entries)) {
    - +
    entries as $item) { if ($display_today && $item->isDay (FreshRSS_Days::TODAY, $this->today)) { ?>
    currentName; ?>
    entries)) { } if ($display_yesterday && $item->isDay (FreshRSS_Days::YESTERDAY, $this->today)) { ?>
    currentName; ?>
    entries)) { } if ($display_others && $item->isDay (FreshRSS_Days::BEFORE_YESTERDAY, $this->today)) { ?>
    currentName; ?>
    entries)) {

    title (); ?>

    author(); - echo $author != '' ? '
    ' . Minz_Translate::t('by_author', $author) . '
    ' : '', + echo $author != '' ? '
    ' . _t('by_author', $author) . '
    ' : '', $lazyload && $hidePosts ? lazyimg($item->content()) : $item->content(); ?>
    @@ -133,7 +133,7 @@ if (!empty($this->entries)) { - +
    diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 31c9cdbc1..4f6beb9fd 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -77,11 +77,11 @@ function formatBytes($bytes, $precision = 2, $system = 'IEC') { } function timestamptodate ($t, $hour = true) { - $month = Minz_Translate::t (date('M', $t)); + $month = _t(date('M', $t)); if ($hour) { - $date = Minz_Translate::t ('format_date_hour', $month); + $date = _t('format_date_hour', $month); } else { - $date = Minz_Translate::t ('format_date', $month); + $date = _t('format_date', $month); } return @date ($date, $t); @@ -107,7 +107,7 @@ function html_only_entity_decode($text) { function customSimplePie() { $simplePie = new SimplePie(); - $simplePie->set_useragent(Minz_Translate::t('freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION); + $simplePie->set_useragent(_t('freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION); $simplePie->set_cache_location(CACHE_PATH); $simplePie->set_cache_duration(800); $simplePie->strip_htmltags(array( -- cgit v1.2.3 From d8f4681382986524b91acb0500847e9f24badf20 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 5 Oct 2014 19:35:29 +0200 Subject: Replace Minz_Log::record by corresponding methods Please not use Minz_Log::record anymore! See https://github.com/marienfressinaud/FreshRSS/issues/655 --- app/Controllers/entryController.php | 2 +- app/Controllers/feedController.php | 14 +++++++------- app/Controllers/indexController.php | 20 ++++++++++---------- app/Controllers/javascriptController.php | 2 +- app/FreshRSS.php | 2 +- app/Models/CategoryDAO.php | 6 +++--- app/Models/EntryDAO.php | 30 +++++++++++++++--------------- app/Models/EntryDAOSQLite.php | 14 +++++++------- app/Models/FeedDAO.php | 20 ++++++++++---------- app/Models/FeedDAOSQLite.php | 2 +- app/Models/UserDAO.php | 4 ++-- app/views/index/index.phtml | 2 +- lib/Minz/FrontController.php | 4 ++-- lib/Minz/View.php | 12 +++--------- p/api/greader.php | 6 +++--- p/i/index.php | 2 +- 16 files changed, 68 insertions(+), 74 deletions(-) (limited to 'lib') diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index ec90666ed..d7be05663 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -143,7 +143,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $nb = $feedDAO->cleanOldEntries($feed->id(), $date_min, $feedHistory); if ($nb > 0) { $nbTotal += $nb; - Minz_Log::record($nb . ' old entries cleaned in feed [' . $feed->url() . ']', Minz_Log::DEBUG); + Minz_Log::debug($nb . ' old entries cleaned in feed [' . $feed->url() . ']'); //$feedDAO->updateLastUpdate($feed->id()); } } diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 93a8d7c2e..70d5c4e22 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -140,14 +140,14 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } } catch (FreshRSS_BadUrl_Exception $e) { - Minz_Log::record($e->getMessage(), Minz_Log::WARNING); + Minz_Log::warning($e->getMessage()); $notif = array( 'type' => 'bad', 'content' => _t('invalid_url', $url) ); Minz_Session::_param('notification', $notif); } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::record($e->getMessage(), Minz_Log::WARNING); + Minz_Log::warning($e->getMessage()); $notif = array( 'type' => 'bad', 'content' => _t('internal_problem_feed', @@ -156,7 +156,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { Minz_Session::_param('notification', $notif); } catch (Minz_FileNotExistException $e) { // Répertoire de cache n'existe pas - Minz_Log::record($e->getMessage(), Minz_Log::ERROR); + Minz_Log::error($e->getMessage()); $notif = array( 'type' => 'bad', 'content' => _t('internal_problem_feed', @@ -258,7 +258,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0; foreach ($feeds as $feed) { if (!$feed->lock()) { - Minz_Log::record('Feed already being actualized: ' . $feed->url(), Minz_Log::NOTICE); + Minz_Log::notice('Feed already being actualized: ' . $feed->url()); continue; } try { @@ -307,7 +307,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } $nb = $feedDAO->cleanOldEntries($feed->id(), $date_min, max($feedHistory, count($entries) + 10)); if ($nb > 0) { - Minz_Log::record($nb . ' old entries cleaned in feed [' . $feed->url() . ']', Minz_Log::DEBUG); + Minz_Log::debug($nb . ' old entries cleaned in feed [' . $feed->url() . ']'); } } @@ -318,11 +318,11 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } $flux_update++; if (($feed->url() !== $url)) { //HTTP 301 Moved Permanently - Minz_Log::record('Feed ' . $url . ' moved permanently to ' . $feed->url(), Minz_Log::NOTICE); + Minz_Log::notice('Feed ' . $url . ' moved permanently to ' . $feed->url()); $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); } } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::record($e->getMessage(), Minz_Log::NOTICE); + Minz_Log::notice($e->getMessage()); $feedDAO->updateLastUpdate($feed->id(), 1); } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 73638acb3..0d2eff700 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -59,7 +59,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $getType = $get[0]; $getId = substr($get, 2); if (!$this->checkAndProcessType($getType, $getId)) { - Minz_Log::record('Not found [' . $getType . '][' . $getId . ']', Minz_Log::DEBUG); + Minz_Log::debug('Not found [' . $getType . '][' . $getId . ']'); Minz_Error::error( 404, array('error' => array(_t('page_not_found'))) @@ -122,12 +122,12 @@ 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 ($this->view->state === FreshRSS_Entry::STATE_NOT_READ && empty($entries) && ($state_param === null) && ($filter == '')) { - Minz_Log::record('Conflicting information about nbNotRead!', Minz_Log::DEBUG); + Minz_Log::debug('Conflicting information about nbNotRead!'); $feedDAO = FreshRSS_Factory::createFeedDao(); try { $feedDAO->updateCachedValues(); } catch (Exception $ex) { - Minz_Log::record('Failed to automatically correct nbNotRead! ' + $ex->getMessage(), Minz_Log::NOTICE); + Minz_Log::notice('Failed to automatically correct nbNotRead! ' + $ex->getMessage()); } $this->view->state = FreshRSS_Entry::STATE_ALL; $entries = $entryDAO->listWhere($getType, $getId, $this->view->state, $order, $nb, $first, $filter); @@ -143,7 +143,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $this->view->entries = $entries; } catch (FreshRSS_EntriesGetter_Exception $e) { - Minz_Log::record($e->getMessage(), Minz_Log::NOTICE); + Minz_Log::notice($e->getMessage()); Minz_Error::error( 404, array('error' => array(_t('page_not_found'))) @@ -281,7 +281,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $res = array(); $res['status'] = 'failure'; $res['reason'] = $reason == '' ? _t('invalid_login') : $reason; - Minz_Log::record('Persona: ' . $res['reason'], Minz_Log::WARNING); + Minz_Log::warning('Persona: ' . $res['reason']); } header('Content-Type: application/json; charset=UTF-8'); @@ -358,13 +358,13 @@ class FreshRSS_index_Controller extends Minz_ActionController { self::deleteLongTermCookie(); } } else { - Minz_Log::record('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c, Minz_Log::WARNING); + Minz_Log::warning('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c); } } catch (Minz_Exception $me) { - Minz_Log::record('Login failure: ' . $me->getMessage(), Minz_Log::WARNING); + Minz_Log::warning('Login failure: ' . $me->getMessage()); } } else { - Minz_Log::record('Invalid credential parameters: user=' . $username . ' challenge=' . $c . ' nonce=' . $nonce, Minz_Log::DEBUG); + Minz_Log::debug('Invalid credential parameters: user=' . $username . ' challenge=' . $c . ' nonce=' . $nonce); } if (!$ok) { $notif = array( @@ -395,10 +395,10 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('currentUser', $username); Minz_Session::_param('passwordHash', $s); } else { - Minz_Log::record('Unsafe password mismatch for user ' . $username, Minz_Log::WARNING); + Minz_Log::warning('Unsafe password mismatch for user ' . $username); } } catch (Minz_Exception $me) { - Minz_Log::record('Unsafe login failure: ' . $me->getMessage(), Minz_Log::WARNING); + Minz_Log::warning('Unsafe login failure: ' . $me->getMessage()); } Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } elseif (!Minz_Configuration::canLogIn()) { diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php index 2a0dbd3d9..14e6f36de 100755 --- a/app/Controllers/javascriptController.php +++ b/app/Controllers/javascriptController.php @@ -37,7 +37,7 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { return; //Success } } catch (Minz_Exception $me) { - Minz_Log::record('Nonce failure: ' . $me->getMessage(), Minz_Log::WARNING); + Minz_Log::warning('Nonce failure: ' . $me->getMessage()); } } $this->view->nonce = ''; //Failure diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 61e8d83f0..efd302ecc 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -101,7 +101,7 @@ class FreshRSS extends Minz_FrontController { 'content' => 'Invalid configuration for user [' . $currentUser . ']!', ); Minz_Session::_param('notification', $notif); - Minz_Log::record($notif['content'] . ' ' . $me->getMessage(), Minz_Log::WARNING); + Minz_Log::warning($notif['content'] . ' ' . $me->getMessage()); Minz_Session::_param('currentUser', ''); } catch (Exception $e) { die($e->getMessage()); diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index ce1babfdd..2e333d2f1 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -13,7 +13,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { return $this->bd->lastInsertId(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error addCategory: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error addCategory: ' . $info[2] ); return false; } } @@ -44,7 +44,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error updateCategory: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error updateCategory: ' . $info[2]); return false; } } @@ -59,7 +59,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error deleteCategory: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error deleteCategory: ' . $info[2]); return false; } } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 34717123c..5a136499a 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -40,11 +40,11 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); if ((int)($info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries - Minz_Log::record('SQL error addEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] - . ' while adding entry in feed ' . $valuesTmp['id_feed'] . ' with title: ' . $valuesTmp['title'], Minz_Log::ERROR); + Minz_Log::error('SQL error addEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] + . ' while adding entry in feed ' . $valuesTmp['id_feed'] . ' with title: ' . $valuesTmp['title']); } /*else { - Minz_Log::record ('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] - . ' while adding entry in feed ' . $valuesTmp['id_feed'] . ' with title: ' . $valuesTmp['title'], Minz_Log::DEBUG); + Minz_Log::debug('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] + . ' while adding entry in feed ' . $valuesTmp['id_feed'] . ' with title: ' . $valuesTmp['title']); }*/ return false; } @@ -94,7 +94,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markFavorite: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error markFavorite: ' . $info[2]); return false; } } @@ -124,7 +124,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return true; } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error updateCacheUnreads: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error updateCacheUnreads: ' . $info[2]); return false; } } @@ -147,7 +147,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error markRead: ' . $info[2]); return false; } $affected = $stm->rowCount(); @@ -166,7 +166,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markRead: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error markRead: ' . $info[2]); return false; } } @@ -175,7 +175,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0) { if ($idMax == 0) { $idMax = time() . '000000'; - Minz_Log::record('Calling markReadEntries(0) is deprecated!', Minz_Log::DEBUG); + Minz_Log::debug('Calling markReadEntries(0) is deprecated!'); } $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id ' @@ -190,7 +190,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error markReadEntries: ' . $info[2]); return false; } $affected = $stm->rowCount(); @@ -203,7 +203,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { public function markReadCat($id, $idMax = 0) { if ($idMax == 0) { $idMax = time() . '000000'; - Minz_Log::record('Calling markReadCat(0) is deprecated!', Minz_Log::DEBUG); + Minz_Log::debug('Calling markReadCat(0) is deprecated!'); } $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id ' @@ -213,7 +213,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error markReadCat: ' . $info[2]); return false; } $affected = $stm->rowCount(); @@ -226,7 +226,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { public function markReadFeed($id, $idMax = 0) { if ($idMax == 0) { $idMax = time() . '000000'; - Minz_Log::record('Calling markReadFeed(0) is deprecated!', Minz_Log::DEBUG); + Minz_Log::debug('Calling markReadFeed(0) is deprecated!'); } $this->bd->beginTransaction(); @@ -237,7 +237,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error markReadFeed: ' . $info[2]); $this->bd->rollBack(); return false; } @@ -251,7 +251,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadFeed: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error markReadFeed: ' . $info[2]); $this->bd->rollBack(); return false; } diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 9dc395c3c..66078aca9 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -26,7 +26,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { return true; } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error updateCacheUnreads: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error updateCacheUnreads: ' . $info[2]); return false; } } @@ -47,7 +47,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markRead 1: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error markRead 1: ' . $info[2]); $this->bd->rollBack(); return false; } @@ -59,7 +59,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markRead 2: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error markRead 2: ' . $info[2]); $this->bd->rollBack(); return false; } @@ -72,7 +72,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0) { if ($idMax == 0) { $idMax = time() . '000000'; - Minz_Log::record('Calling markReadEntries(0) is deprecated!', Minz_Log::DEBUG); + Minz_Log::debug('Calling markReadEntries(0) is deprecated!'); } $sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=1 WHERE is_read=0 AND id <= ?'; @@ -85,7 +85,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadEntries: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error markReadEntries: ' . $info[2]); return false; } $affected = $stm->rowCount(); @@ -98,7 +98,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { public function markReadCat($id, $idMax = 0) { if ($idMax == 0) { $idMax = time() . '000000'; - Minz_Log::record('Calling markReadCat(0) is deprecated!', Minz_Log::DEBUG); + Minz_Log::debug('Calling markReadCat(0) is deprecated!'); } $sql = 'UPDATE `' . $this->prefix . 'entry` ' @@ -109,7 +109,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error markReadCat: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error markReadCat: ' . $info[2]); return false; } $affected = $stm->rowCount(); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index b89ae2045..852de6e36 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -19,7 +19,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $this->bd->lastInsertId(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error addFeed: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error addFeed: ' . $info[2]); return false; } } @@ -77,7 +77,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error updateFeed: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error updateFeed: ' . $info[2]); return false; } } @@ -107,7 +107,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error updateLastUpdate: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error updateLastUpdate: ' . $info[2]); return false; } } @@ -131,7 +131,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error changeCategory: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error changeCategory: ' . $info[2]); return false; } } @@ -146,7 +146,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error deleteFeed: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error deleteFeed: ' . $info[2]); return false; } } @@ -160,7 +160,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error deleteFeedByCategory: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error deleteFeedByCategory: ' . $info[2]); return false; } } @@ -289,7 +289,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error updateCachedValues: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error updateCachedValues: ' . $info[2]); return false; } } @@ -301,7 +301,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $this->bd->beginTransaction(); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error truncate: ' . $info[2]); $this->bd->rollBack(); return false; } @@ -313,7 +313,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm = $this->bd->prepare($sql); if (!($stm && $stm->execute($values))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error truncate: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error truncate: ' . $info[2]); $this->bd->rollBack(); return false; } @@ -338,7 +338,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error cleanOldEntries: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error cleanOldEntries: ' . $info[2]); return false; } } diff --git a/app/Models/FeedDAOSQLite.php b/app/Models/FeedDAOSQLite.php index 0d1872389..7599fda53 100644 --- a/app/Models/FeedDAOSQLite.php +++ b/app/Models/FeedDAOSQLite.php @@ -11,7 +11,7 @@ class FreshRSS_FeedDAOSQLite extends FreshRSS_FeedDAO { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record('SQL error updateCachedValues: ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error updateCachedValues: ' . $info[2]); return false; } } diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index 0c96d7175..15215258c 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -28,7 +28,7 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { return true; } else { $info = empty($stm) ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error : ' . $info[2]); return false; } } @@ -48,7 +48,7 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { return true; } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR); + Minz_Log::error('SQL error : ' . $info[2]); return false; } } diff --git a/app/views/index/index.phtml b/app/views/index/index.phtml index 5e935b81e..584792e29 100644 --- a/app/views/index/index.phtml +++ b/app/views/index/index.phtml @@ -21,5 +21,5 @@ if ($this->loginOk || Minz_Configuration::allowAnonymous()) { $this->renderHelper('view/rss_view'); } else { // Normally, it should not happen, but log it anyway - Minz_Log::record('Something is wrong in ' . __FILE__ . ' line ' . __LINE__, Minz_Log::ERROR); + Minz_Log::error('Something is wrong in ' . __FILE__ . ' line ' . __LINE__); } diff --git a/lib/Minz/FrontController.php b/lib/Minz/FrontController.php index f13882801..e95c56bf3 100644 --- a/lib/Minz/FrontController.php +++ b/lib/Minz/FrontController.php @@ -46,7 +46,7 @@ class Minz_FrontController { ); Minz_Request::forward ($url); } catch (Minz_Exception $e) { - Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); + Minz_Log::error($e->getMessage()); $this->killApp ($e->getMessage ()); } @@ -85,7 +85,7 @@ class Minz_FrontController { $this->dispatcher->run(); } catch (Minz_Exception $e) { try { - Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); + Minz_Log::error($e->getMessage()); } catch (Minz_PermissionDeniedException $e) { $this->killApp ($e->getMessage ()); } diff --git a/lib/Minz/View.php b/lib/Minz/View.php index a0dec1824..b40448491 100644 --- a/lib/Minz/View.php +++ b/lib/Minz/View.php @@ -71,9 +71,7 @@ class Minz_View { */ public function render () { if ((include($this->view_filename)) === false) { - Minz_Log::record ('File not found: `' - . $this->view_filename . '`', - Minz_Log::NOTICE); + Minz_Log::notice('File not found: `' . $this->view_filename . '`'); } } @@ -87,9 +85,7 @@ class Minz_View { . $part . '.phtml'; if ((include($fic_partial)) === false) { - Minz_Log::record ('File not found: `' - . $fic_partial . '`', - Minz_Log::WARNING); + Minz_Log::warning('File not found: `' . $fic_partial . '`'); } } @@ -103,9 +99,7 @@ class Minz_View { . $helper . '.phtml'; if ((include($fic_helper)) === false) {; - Minz_Log::record ('File not found: `' - . $fic_helper . '`', - Minz_Log::WARNING); + Minz_Log::warning('File not found: `' . $fic_helper . '`'); } } diff --git a/p/api/greader.php b/p/api/greader.php index 5a6fdad7d..1a66c30fb 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -160,7 +160,7 @@ function authorizationToUserConf() { return $conf; } else { logMe('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1] . "\n"); - Minz_Log::record('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1], Minz_Log::WARNING); + Minz_Log::warning('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1]); unauthorized(); } } else { @@ -181,7 +181,7 @@ function clientLogin($email, $pass) { //http://web.archive.org/web/2013060409104 $conf = new FreshRSS_Configuration($email); } catch (Exception $e) { logMe($e->getMessage() . "\n"); - Minz_Log::record('Invalid API user ' . $email, Minz_Log::WARNING); + Minz_Log::warning('Invalid API user ' . $email); unauthorized(); } if ($conf->apiPasswordHash != '' && password_verify($pass, $conf->apiPasswordHash)) { @@ -191,7 +191,7 @@ function clientLogin($email, $pass) { //http://web.archive.org/web/2013060409104 'Auth=', $auth, "\n"; exit(); } else { - Minz_Log::record('Password API mismatch for user ' . $email, Minz_Log::WARNING); + Minz_Log::warning('Password API mismatch for user ' . $email); unauthorized(); } } else { diff --git a/p/i/index.php b/p/i/index.php index 7b34eefd1..ec969c159 100755 --- a/p/i/index.php +++ b/p/i/index.php @@ -46,7 +46,7 @@ if (file_exists(DATA_PATH . '/do-install.txt')) { $front_controller->run(); } catch (Exception $e) { echo '### Fatal error! ###
    ', "\n"; - Minz_Log::record($e->getMessage(), Minz_Log::ERROR); + Minz_Log::error($e->getMessage()); echo 'See logs files.'; } } -- cgit v1.2.3 From 6c1fe11395c86f71397629b2ab269e26ee63566c Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 5 Oct 2014 20:27:16 +0200 Subject: SimplePie timeout preference https://github.com/marienfressinaud/FreshRSS/issues/656 TODO: Make a user setting --- lib/lib_rss.php | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 4f6beb9fd..2f9a2ea45 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -110,6 +110,7 @@ function customSimplePie() { $simplePie->set_useragent(_t('freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION); $simplePie->set_cache_location(CACHE_PATH); $simplePie->set_cache_duration(800); + $simplePie->set_timeout(10); //TODO: Make a user setting $simplePie->strip_htmltags(array( 'base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', -- cgit v1.2.3 From e2da6e6e6b871dc3dbc289cdd40ba401a21d8e91 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 5 Oct 2014 23:20:16 +0200 Subject: Refactor feedController See https://github.com/marienfressinaud/FreshRSS/issues/655 --- app/Controllers/feedController.php | 611 ++++++++++++++++++++----------------- lib/Minz/ModelPdo.php | 7 + 2 files changed, 330 insertions(+), 288 deletions(-) (limited to 'lib') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 70d5c4e22..0f52917e4 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -1,6 +1,14 @@ view->loginOk) { // Token is useful in the case that anonymous refresh is forbidden @@ -10,9 +18,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $token_param = Minz_Request::param('token', ''); $token_is_ok = ($token != '' && $token == $token_param); $action = Minz_Request::actionName(); - if (!(($token_is_ok || Minz_Configuration::allowAnonymousRefresh()) && - $action === 'actualize') - ) { + if ($action !== 'actualize' || + !(Minz_Configuration::allowAnonymousRefresh() || $token_is_ok)) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) @@ -21,10 +28,32 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } + /** + * This action subscribes to a feed. + * + * It can be reached by both GET and POST requests. + * + * GET request displays a form to add and configure a feed. + * Request parameter is: + * - url_rss (default: false) + * + * POST request adds a feed in database. + * Parameters are: + * - url_rss (default: false) + * - category (default: false) + * - new_category (required if category == 'nc') + * - http_user (default: false) + * - http_pass (default: false) + * It tries to get website information from RSS feed. + * If no category is given, feed is added to the default one. + * + * If url_rss is false, nothing happened. + */ public function addAction() { - $url = Minz_Request::param('url_rss', false); + $url = Minz_Request::param('url_rss'); if ($url === false) { + // No url, do nothing Minz_Request::forward(array( 'c' => 'subscription', 'a' => 'index' @@ -33,14 +62,19 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feedDAO = FreshRSS_Factory::createFeedDao(); $this->catDAO = new FreshRSS_CategoryDAO(); - $this->catDAO->checkDefault(); + $url_redirect = array( + 'c' => 'subscription', + 'a' => 'index', + 'params' => array(), + ); if (Minz_Request::isPost()) { @set_time_limit(300); - - $cat = Minz_Request::param('category', false); + $cat = Minz_Request::param('category'); if ($cat === 'nc') { + // User want to create a new category, new_category parameter + // must exist $new_cat = Minz_Request::param('new_category'); if (empty($new_cat['name'])) { $cat = false; @@ -48,139 +82,114 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $cat = $this->catDAO->addCategory($new_cat); } } + if ($cat === false) { + // If category was not given or if creating new category failed, + // get the default category + $this->catDAO->checkDefault(); $def_cat = $this->catDAO->getDefault(); $cat = $def_cat->id(); } + // HTTP information are useful if feed is protected behind a + // HTTP authentication $user = Minz_Request::param('http_user'); $pass = Minz_Request::param('http_pass'); - $params = array(); + $http_auth = ''; + if ($user != '' || $pass != '') { + $http_auth = $user . ':' . $pass; + } - $transactionStarted = false; + $transaction_started = false; try { $feed = new FreshRSS_Feed($url); - $feed->_category($cat); - - $httpAuth = ''; - if ($user != '' || $pass != '') { - $httpAuth = $user . ':' . $pass; - } - $feed->_httpAuth($httpAuth); - - $feed->load(true); - - $values = array( - 'url' => $feed->url(), - 'category' => $feed->category(), - 'name' => $feed->name(), - 'website' => $feed->website(), - 'description' => $feed->description(), - 'lastUpdate' => time(), - 'httpAuth' => $feed->httpAuth(), - ); - - if ($feedDAO->searchByUrl($values['url'])) { - // on est déjà abonné à ce flux - $notif = array( - 'type' => 'bad', - 'content' => _t('already_subscribed', $feed->name()) - ); - Minz_Session::_param('notification', $notif); - } else { - $id = $feedDAO->addFeed($values); - if (!$id) { - // problème au niveau de la base de données - $notif = array( - 'type' => 'bad', - 'content' => _t('feed_not_added', $feed->name()) - ); - Minz_Session::_param('notification', $notif); - } else { - $feed->_id($id); - $feed->faviconPrepare(); - - $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0; - - $entryDAO = FreshRSS_Factory::createEntryDao(); - $entries = array_reverse($feed->entries()); //We want chronological order and SimplePie uses reverse order - - // 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); - - //MySQL: http://docs.oracle.com/cd/E17952_01/refman-5.5-en/optimizing-innodb-transaction-management.html - //SQLite: http://stackoverflow.com/questions/1711631/how-do-i-improve-the-performance-of-sqlite - $preparedStatement = $entryDAO->addEntryPrepare(); - $transactionStarted = true; - $feedDAO->beginTransaction(); - // on ajoute les articles en masse sans vérification - foreach ($entries as $entry) { - $values = $entry->toArray(); - $values['id_feed'] = $feed->id(); - $values['id'] = min(time(), $entry->date(true)) . uSecString(); - $values['is_read'] = $is_read; - $entryDAO->addEntry($values, $preparedStatement); - } - $feedDAO->updateLastUpdate($feed->id()); - if ($transactionStarted) { - $feedDAO->commit(); - } - $transactionStarted = false; - - // ok, ajout terminé - $notif = array( - 'type' => 'good', - 'content' => _t('feed_added', $feed->name()) - ); - Minz_Session::_param('notification', $notif); - - // permet de rediriger vers la page de conf du flux - $params['id'] = $feed->id(); - } - } } catch (FreshRSS_BadUrl_Exception $e) { + // Given url was not a valid url! Minz_Log::warning($e->getMessage()); - $notif = array( - 'type' => 'bad', - 'content' => _t('invalid_url', $url) - ); - Minz_Session::_param('notification', $notif); + Minz_Request::bad(_t('invalid_url', $url), $url_redirect); + } + + try { + $feed->load(true); } catch (FreshRSS_Feed_Exception $e) { + // Something went bad (timeout, server not found, etc.) Minz_Log::warning($e->getMessage()); - $notif = array( - 'type' => 'bad', - 'content' => _t('internal_problem_feed', - Minz_Url::display(array('a' => 'logs'))) + Minz_Request::bad( + _t('internal_problem_feed', _url('index', 'logs')), + $url_redirect ); - Minz_Session::_param('notification', $notif); } catch (Minz_FileNotExistException $e) { - // Répertoire de cache n'existe pas + // Cache directory doesn't exist! Minz_Log::error($e->getMessage()); - $notif = array( - 'type' => 'bad', - 'content' => _t('internal_problem_feed', - Minz_Url::display(array('a' => 'logs'))) + Minz_Request::bad( + _t('internal_problem_feed', _url('index', 'logs')), + $url_redirect ); - Minz_Session::_param('notification', $notif); } - if ($transactionStarted) { - $feedDAO->rollBack(); + + if ($feedDAO->searchByUrl($feed->url())) { + Minz_Request::bad(_t('already_subscribed', $feed->name()), $url_redirect); } - Minz_Request::forward(array( - 'c' => 'subscription', - 'a' => 'index', - 'params' => $params - ), true); - } else { + $feed->_category($cat); + $feed->_httpAuth($http_auth); + + $values = array( + 'url' => $feed->url(), + 'category' => $feed->category(), + 'name' => $feed->name(), + 'website' => $feed->website(), + 'description' => $feed->description(), + 'lastUpdate' => time(), + 'httpAuth' => $feed->httpAuth(), + ); + + $id = $feedDAO->addFeed($values); + if (!$id) { + // There was an error in database... we cannot say what here. + Minz_Request::bad(_t('feed_not_added', $feed->name()), $url_redirect); + } + + // Ok, feed has been added in database. Now we have to refresh entries. + $feed->_id($id); + $feed->faviconPrepare(); + + $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0; + + $entryDAO = FreshRSS_Factory::createEntryDao(); + // We want chronological order and SimplePie uses reverse order. + $entries = array_reverse($feed->entries()); + + // Calculate date of oldest entries we accept in DB. + $nb_month_old = $this->view->conf->old_entries; + $date_min = time() - (3600 * 24 * 30 * $nb_month_old); + + // Use a shared statement and a transaction to improve a LOT the + // performances. + $prepared_statement = $entryDAO->addEntryPrepare(); + $feedDAO->beginTransaction(); + foreach ($entries as $entry) { + // Entries are added without any verification. + $values = $entry->toArray(); + $values['id_feed'] = $feed->id(); + $values['id'] = min(time(), $entry->date(true)) . uSecString(); + $values['is_read'] = $is_read; + $entryDAO->addEntry($values, $prepared_statement); + } + $feedDAO->updateLastUpdate($feed->id()); + $feedDAO->commit(); - // GET request so we must ask confirmation to user + // Entries are in DB, we redirect to feed configuration page. + $url_redirect['params']['id'] = $feed->id(); + Minz_Request::good(_t('feed_added', $feed->name()), $url_redirect); + } else { + // GET request: we must ask confirmation to user before adding feed. Minz_View::prependTitle(_t('add_rss_feed') . ' · '); + $this->view->categories = $this->catDAO->listCategories(false); $this->view->feed = new FreshRSS_Feed($url); try { - // We try to get some more information about the feed + // We try to get more information about the feed. $this->view->feed->load(true); $this->view->load_ok = true; } catch (Exception $e) { @@ -189,43 +198,53 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feed = $feedDAO->searchByUrl($this->view->feed->url()); if ($feed) { - // Already subscribe so we redirect to the feed configuration page - $notif = array( - 'type' => 'bad', - 'content' => _t('already_subscribed', $feed->name()) - ); - Minz_Session::_param('notification', $notif); - - Minz_Request::forward(array( - 'c' => 'subscription', - 'a' => 'index', - 'params' => array( - 'id' => $feed->id() - ) - ), true); + // Already subscribe so we redirect to the feed configuration page. + $url_redirect['params']['id'] = $feed->id(); + Minz_Request::good(_t('already_subscribed', $feed->name()), $url_redirect); } } } + /** + * This action remove entries from a given feed. + * + * It should be reached by a POST action. + * + * Parameter is: + * - id (default: false) + */ public function truncateAction() { - if (Minz_Request::isPost()) { - $id = Minz_Request::param('id'); - $feedDAO = FreshRSS_Factory::createFeedDao(); - $n = $feedDAO->truncate($id); - $notif = array( - 'type' => $n === false ? 'bad' : 'good', - 'content' => _t('n_entries_deleted', $n) - ); - Minz_Session::_param('notification', $notif); - invalidateHttpCache(); - Minz_Request::forward(array( - 'c' => 'subscription', - 'a' => 'index', - 'params' => array('id' => $id) - ), true); + $id = Minz_Request::param('id'); + $url_redirect = array( + 'c' => 'subscription', + 'a' => 'index', + 'params' => array('id' => $id) + ); + + if (!Minz_Request::isPost()) { + Minz_Request::forward($url_redirect, true); + } + + $feedDAO = FreshRSS_Factory::createFeedDao(); + $n = $feedDAO->truncate($id); + + invalidateHttpCache(); + if ($n === false) { + Minz_Request::bad(_t('error_occurred'), $url_redirect); + } else { + Minz_Request::good(_t('n_entries_deleted', $n), $url_redirect); } } + /** + * This action actualizes entries from one or several feeds. + * + * Parameters are: + * - id (default: false) + * - force (default: false) + * If id is not specified, all the feeds are actualized. But if force is + * false, process stops at 10 feeds to avoid time execution problem. + */ public function actualizeAction() { @set_time_limit(300); @@ -234,213 +253,229 @@ class FreshRSS_feed_Controller extends Minz_ActionController { Minz_Session::_param('actualize_feeds', false); $id = Minz_Request::param('id'); - $force = Minz_Request::param('force', false); + $force = Minz_Request::param('force'); - // on créé la liste des flux à mettre à actualiser - // si on veut mettre un flux à jour spécifiquement, on le met - // dans la liste, mais seul (permet d'automatiser le traitement) + // Create a list of feeds to actualize. + // If id is set and valid, corresponding feed is added to the list but + // alone in order to automatize further process. $feeds = array(); if ($id) { $feed = $feedDAO->searchById($id); if ($feed) { - $feeds = array($feed); + $feeds[] = $feed; } } else { $feeds = $feedDAO->listFeedsOrderUpdate($this->view->conf->ttl_default); } - // on calcule la date des articles les plus anciens qu'on accepte + // Calculate date of oldest entries we accept in DB. $nb_month_old = max($this->view->conf->old_entries, 1); $date_min = time() - (3600 * 24 * 30 * $nb_month_old); - $i = 0; - $flux_update = 0; + $updated_feeds = 0; $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0; foreach ($feeds as $feed) { if (!$feed->lock()) { Minz_Log::notice('Feed already being actualized: ' . $feed->url()); continue; } - try { - $url = $feed->url(); - $feedHistory = $feed->keepHistory(); + try { + // Load entries $feed->load(false); - $entries = array_reverse($feed->entries()); //We want chronological order and SimplePie uses reverse order - $hasTransaction = false; - - if (count($entries) > 0) { - //For this feed, check last n entry GUIDs already in database - $existingGuids = array_fill_keys( - $entryDAO->listLastGuidsByFeed($feed->id(), - count($entries) + 10), 1); - $useDeclaredDate = empty($existingGuids); - - if ($feedHistory == -2) { //default - $feedHistory = $this->view->conf->keep_history_default; - } + } catch (FreshRSS_Feed_Exception $e) { + Minz_Log::notice($e->getMessage()); + $feedDAO->updateLastUpdate($feed->id(), 1); + continue; + } - $preparedStatement = $entryDAO->addEntryPrepare(); - $hasTransaction = true; - $feedDAO->beginTransaction(); + $url = $feed->url(); + $feed_history = $feed->keepHistory(); + if ($feed_history == -2) { + // TODO: -2 must be a constant! + // -2 means we take the default value from configuration + $feed_history = $this->view->conf->keep_history_default; + } - // On ne vérifie pas strictement que l'article n'est pas déjà en BDD - // La BDD refusera l'ajout car (id_feed, guid) doit être unique - foreach ($entries as $entry) { - $eDate = $entry->date(true); - if ((!isset($existingGuids[$entry->guid()])) && - (($feedHistory != 0) || ($eDate >= $date_min))) { - $values = $entry->toArray(); - //Use declared date at first import, otherwise use discovery date - $values['id'] = ($useDeclaredDate || $eDate < $date_min) ? - min(time(), $eDate) . uSecString() : - uTimeString(); - $values['is_read'] = $is_read; - $entryDAO->addEntry($values, $preparedStatement); - } + // We want chronological order and SimplePie uses reverse order. + $entries = array_reverse($feed->entries()); + if (count($entries) > 0) { + // For this feed, check last n entry GUIDs already in database. + $existing_guids = array_fill_keys($entryDAO->listLastGuidsByFeed( + $feed->id(), count($entries) + 10 + ), 1); + $use_declared_date = empty($existing_guids); + + // Add entries in database if possible. + $prepared_statement = $entryDAO->addEntryPrepare(); + $feedDAO->beginTransaction(); + foreach ($entries as $entry) { + $entry_date = $entry->date(true); + if (isset($existing_guids[$entry->guid()]) || + ($feed_history == 0 && $entry_date < $date_min)) { + // This entry already exists in DB or should not be added + // considering configuration and date. + continue; } - } - if (($feedHistory >= 0) && (rand(0, 30) === 1)) { - if (!$hasTransaction) { - $feedDAO->beginTransaction(); - } - $nb = $feedDAO->cleanOldEntries($feed->id(), $date_min, max($feedHistory, count($entries) + 10)); - if ($nb > 0) { - Minz_Log::debug($nb . ' old entries cleaned in feed [' . $feed->url() . ']'); + $id = uTimeString(); + if ($use_declared_date || $entry_date < $date_min) { + // Use declared date at first import. + $id = min(time(), $entry_date) . uSecString(); } + + $values = $entry->toArray(); + $values['id'] = $id; + $values['is_read'] = $is_read; + $entryDAO->addEntry($values, $prepared_statement); } + } - // on indique que le flux vient d'être mis à jour en BDD - $feedDAO->updateLastUpdate($feed->id(), 0, $hasTransaction); - if ($hasTransaction) { - $feedDAO->commit(); + if ($feed_history >= 0 && rand(0, 30) === 1) { + // TODO: move this function in web cron when available + // Remove old entries once in 30. + if (!$feedDAO->hasTransaction()) { + $feedDAO->beginTransaction(); } - $flux_update++; - if (($feed->url() !== $url)) { //HTTP 301 Moved Permanently - Minz_Log::notice('Feed ' . $url . ' moved permanently to ' . $feed->url()); - $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); + + $nb = $feedDAO->cleanOldEntries($feed->id(), + $date_min, + max($feed_history, count($entries) + 10)); + if ($nb > 0) { + Minz_Log::debug($nb . ' old entries cleaned in feed [' . + $feed->url() . ']'); } - } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::notice($e->getMessage()); - $feedDAO->updateLastUpdate($feed->id(), 1); + } + + $feedDAO->updateLastUpdate($feed->id(), 0, $feedDAO->hasTransaction()); + if ($feedDAO->hasTransaction()) { + $feedDAO->commit(); + } + + if ($feed->url() !== $url) { + // HTTP 301 Moved Permanently + Minz_Log::notice('Feed ' . $url . ' moved permanently to ' . $feed->url()); + $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); } $feed->faviconPrepare(); $feed->unlock(); + $updated_feeds++; unset($feed); - // On arrête à 10 flux pour ne pas surcharger le serveur - // sauf si le paramètre $force est à vrai - $i++; - if ($i >= 10 && !$force) { + // No more than 10 feeds unless $force is true to avoid overloading + // the server. + if ($updated_feeds >= 10 && !$force) { break; } } - $url = array(); - if ($flux_update === 1) { - // on a mis un seul flux à jour - $feed = reset($feeds); + if (Minz_Request::param('ajax')) { + // Most of the time, ajax request is for only one feed. But since + // there are several parallel requests, we should return that there + // are several updated feeds. $notif = array( 'type' => 'good', - 'content' => _t('feed_actualized', $feed->name()) - ); - } elseif ($flux_update > 1) { - // plusieurs flux on été mis à jour - $notif = array( - 'type' => 'good', - 'content' => _t('n_feeds_actualized', $flux_update) - ); - } else { - // aucun flux n'a été mis à jour, oups - $notif = array( - 'type' => 'good', - 'content' => _t('no_feed_to_refresh') + 'content' => _t('feeds_actualized') ); + Minz_Session::_param('notification', $notif); + // No layout in ajax request. + $this->view->_useLayout(false); + return; } - if ($i === 1) { - // Si on a voulu mettre à jour qu'un flux - // on filtre l'affichage par ce flux + // Redirect to the main page with correct notification. + if ($updated_feeds === 1) { $feed = reset($feeds); - $url['params'] = array('get' => 'f_' . $feed->id()); - } - - if (Minz_Request::param('ajax', 0) === 0) { - Minz_Session::_param('notification', $notif); - Minz_Request::forward($url, true); + Minz_Request::good(_t('feed_actualized', $feed->name()), + array('get' => 'f_' . $feed->id())); + } elseif ($updated_feeds > 1) { + Minz_Request::good(_t('n_feeds_actualized', $updated_feeds), array()); } else { - // Une requête Ajax met un seul flux à jour. - // Comme en principe plusieurs requêtes ont lieu, - // on indique que "plusieurs flux ont été mis à jour". - // Cela permet d'avoir une notification plus proche du - // ressenti utilisateur - $notif = array( - 'type' => 'good', - 'content' => _t('feeds_actualized') - ); - Minz_Session::_param('notification', $notif); - // et on désactive le layout car ne sert à rien - $this->view->_useLayout(false); + Minz_Request::good(_t('no_feed_to_refresh'), array()); } } + /** + * This action changes the category of a feed. + * + * This page must be reached by a POST request. + * + * Parameters are: + * - f_id (default: false) + * - c_id (default: false) + * If c_id is false, default category is used. + * + * @todo should handle order of the feed inside the category. + */ public function moveAction() { - if (Minz_Request::isPost()) { - $feed_id = Minz_Request::param('f_id'); - $cat_id = Minz_Request::param('c_id'); + if (!Minz_Request::isPost()) { + Minz_Request::forward(array('c' => 'subscription'), true); + } - $feedDAO = FreshRSS_Factory::createFeedDao(); - $values = array( - 'category' => $cat_id, - ); + $feed_id = Minz_Request::param('f_id'); + $cat_id = Minz_Request::param('c_id'); - $feed = $feedDAO->searchById($feed_id); + if ($cat_id === false) { + // If category was not given get the default one. + $catDAO = new FreshRSS_CategoryDAO(); + $catDAO->checkDefault(); + $def_cat = $catDAO->getDefault(); + $cat_id = $def_cat->id(); + } - if ($feed && ($feed->category() == $cat_id || - $feedDAO->updateFeed($feed_id, $values))) { - // TODO: return something useful - } else { - Minz_Error::error( - 404, - array('error' => array(_t('error_occurred'))) - ); - } + $feedDAO = FreshRSS_Factory::createFeedDao(); + $values = array('category' => $cat_id); + + $feed = $feedDAO->searchById($feed_id); + if ($feed && ($feed->category() == $cat_id || + $feedDAO->updateFeed($feed_id, $values))) { + // TODO: return something useful + } else { + Minz_Log::warning('Cannot move feed `' . $feed_id . '` ' . + 'in the category `' . $cat_id . '`'); + Minz_Error::error( + 404, + array('error' => array(_t('error_occurred'))) + ); } } + /** + * This action deletes a feed. + * + * This page must be reached by a POST request. + * If there are related queries, they are deleted too. + * + * Parameters are: + * - id (default: false) + * - r (default: false) + * r permits to redirect to a given page at the end of this action. + * + * @todo handle "r" redirection in Minz_Request::forward()? + */ public function deleteAction() { - if (Minz_Request::isPost()) { - $id = Minz_Request::param('id'); - $feedDAO = FreshRSS_Factory::createFeedDao(); - - if ($feedDAO->deleteFeed($id)) { - // TODO: Delete old favicon + $redirect_url = Minz_Request::param('r', false, true); + if (!$redirect_url) { + $redirect_url = array('c' => 'subscription', 'a' => 'index'); + } - // Remove related queries - $this->view->conf->remove_query_by_get('f_' . $id); - $this->view->conf->save(); + if (!Minz_Request::isPost()) { + Minz_Request::forward($redirect_url, true); + } - $notif = array( - 'type' => 'good', - 'content' => _t('feed_deleted') - ); - } else { - $notif = array( - 'type' => 'bad', - 'content' => _t('error_occured') - ); - } + $id = Minz_Request::param('id'); + $feedDAO = FreshRSS_Factory::createFeedDao(); + if ($feedDAO->deleteFeed($id)) { + // TODO: Delete old favicon - Minz_Session::_param('notification', $notif); + // Remove related queries + $this->view->conf->remove_query_by_get('f_' . $id); + $this->view->conf->save(); - $redirect_url = Minz_Request::param('r', false, true); - if ($redirect_url) { - Minz_Request::forward($redirect_url); - } else { - Minz_Request::forward(array('c' => 'subscription', 'a' => 'index'), true); - } + Minz_Request::good(_t('feed_deleted'), $redirect_url); + } else { + Minz_Request::bad(_t('error_occurred'), $redirect_url); } } } diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index b4bfca746..69e46fb39 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -16,6 +16,7 @@ class Minz_ModelPdo { public static $useSharedBd = true; private static $sharedBd = null; private static $sharedPrefix; + private static $has_transaction = false; protected static $sharedDbType; /** @@ -91,12 +92,18 @@ class Minz_ModelPdo { public function beginTransaction() { $this->bd->beginTransaction(); + $this->has_transaction = true; + } + public function hasTransaction() { + return $this->has_transaction; } public function commit() { $this->bd->commit(); + $this->has_transaction = false; } public function rollBack() { $this->bd->rollBack(); + $this->has_transaction = false; } public static function clean() { -- cgit v1.2.3 From 031c1d802d74b94d5ef5245f3678138107621179 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 5 Oct 2014 23:57:54 +0200 Subject: Fix a bug (has_transaction) Introduced by the last commit. --- lib/Minz/ModelPdo.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index 69e46fb39..66127ea22 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -92,18 +92,18 @@ class Minz_ModelPdo { public function beginTransaction() { $this->bd->beginTransaction(); - $this->has_transaction = true; + self::$has_transaction = true; } public function hasTransaction() { - return $this->has_transaction; + return self::$has_transaction; } public function commit() { $this->bd->commit(); - $this->has_transaction = false; + self::$has_transaction = false; } public function rollBack() { $this->bd->rollBack(); - $this->has_transaction = false; + self::$has_transaction = false; } public static function clean() { -- cgit v1.2.3 From 6009990935a2d06c252073f6b51ea5378536ef52 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 7 Oct 2014 10:16:38 +0200 Subject: Introduce FreshRSS_Auth::hasAccess('admin') Replace Minz_Configuration::isAdmin($user). FreshRSS_Auth::hasAccess() could be extended to others scopes later. See https://github.com/marienfressinaud/FreshRSS/issues/655 --- app/Controllers/configureController.php | 2 +- app/Controllers/updateController.php | 2 +- app/Controllers/usersController.php | 8 ++++---- app/Models/Auth.php | 19 +++++++++++++++---- app/layout/aside_configure.phtml | 5 +---- app/layout/header.phtml | 5 +---- app/views/configure/archiving.phtml | 2 +- app/views/users/index.phtml | 6 +++--- lib/Minz/Configuration.php | 3 --- 9 files changed, 27 insertions(+), 25 deletions(-) (limited to 'lib') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 7e77a757a..fb8c1466e 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -229,7 +229,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->nb_total = $entryDAO->count(); $this->view->size_user = $entryDAO->size(); - if (Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + if (FreshRSS_Auth::hasAccess('admin')) { $this->view->size_total = $entryDAO->size(true); } } diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 9da1e8657..9d1e1ddf5 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -3,7 +3,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { public function firstAction() { $current_user = Minz_Session::param('currentUser', ''); - if (!FreshRSS_Auth::hasAccess() && Minz_Configuration::isAdmin($current_user)) { + if (!FreshRSS_Auth::hasAccess('admin')) { Minz_Error::error( 403, array('error' => array(_t('access_denied'))) diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index c2b1d163f..11862ce27 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -51,7 +51,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { $this->view->conf->_apiPasswordHash($passwordHash); } - if (Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + if (FreshRSS_Auth::hasAccess('admin')) { $this->view->conf->_mail_login(Minz_Request::param('mail_login', '', true)); } $email = $this->view->conf->mail_login; @@ -65,7 +65,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { $ok &= (file_put_contents($personaFile, Minz_Session::param('currentUser', '_')) !== false); } - if (Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + if (FreshRSS_Auth::hasAccess('admin')) { $current_token = $this->view->conf->token; $token = Minz_Request::param('token', $current_token); $this->view->conf->_token($token); @@ -105,7 +105,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { } public function createAction() { - if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { $db = Minz_Configuration::dataBase(); require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); @@ -177,7 +177,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { } public function deleteAction() { - if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { $db = Minz_Configuration::dataBase(); require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); diff --git a/app/Models/Auth.php b/app/Models/Auth.php index c4a3abd98..992b444a5 100644 --- a/app/Models/Auth.php +++ b/app/Models/Auth.php @@ -99,12 +99,23 @@ class FreshRSS_Auth { } /** - * Returns if current user is connected. + * Returns if current user has access to the given scope. * - * @return boolean true if user is connected, false else. + * @param string $scope general (default) or admin + * @return boolean true if user has corresponding access, false else. */ - public static function hasAccess() { - return self::$login_ok; + public static function hasAccess($scope = 'general') { + $ok = self::$login_ok; + switch ($scope) { + case 'general': + break; + case 'admin': + $ok &= Minz_Session::param('currentUser') === Minz_Configuration::defaultUser(); + break; + default: + $ok = false; + } + return $ok; } /** diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index e17bcb254..59846a7c8 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -22,10 +22,7 @@
  • - +
  • diff --git a/app/layout/header.phtml b/app/layout/header.phtml index fadfd13d7..12c86d61d 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -64,10 +64,7 @@ if (Minz_Configuration::canLogIn()) {
  • - +
  • diff --git a/app/views/configure/archiving.phtml b/app/views/configure/archiving.phtml index a883571aa..adbfdb77e 100644 --- a/app/views/configure/archiving.phtml +++ b/app/views/configure/archiving.phtml @@ -67,7 +67,7 @@
    - +

    diff --git a/app/views/users/index.phtml b/app/views/users/index.phtml index 95659f727..f1cdf01a3 100644 --- a/app/views/users/index.phtml +++ b/app/views/users/index.phtml @@ -11,7 +11,7 @@
    @@ -44,7 +44,7 @@ conf->mail_login; ?>
    - placeholder="alice@example.net" /> + placeholder="alice@example.net" />
    @@ -56,7 +56,7 @@
    - + diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 4e9da58b4..554bc8c96 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -100,9 +100,6 @@ class Minz_Configuration { public static function defaultUser () { return self::$default_user; } - public static function isAdmin($currentUser) { - return $currentUser === self::$default_user; - } public static function allowAnonymous() { return self::$allow_anonymous; } -- cgit v1.2.3 From 1283e73d08a0441dbf84d7e45ff8c1c42bf188b1 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 14 Oct 2014 20:00:06 +0200 Subject: SimplePie: bug date CEST https://github.com/marienfressinaud/FreshRSS/issues/659 https://github.com/simplepie/simplepie/pull/380 --- lib/SimplePie/SimplePie/Parse/Date.php | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/SimplePie/SimplePie/Parse/Date.php b/lib/SimplePie/SimplePie/Parse/Date.php index ef800f125..ba7c0703e 100644 --- a/lib/SimplePie/SimplePie/Parse/Date.php +++ b/lib/SimplePie/SimplePie/Parse/Date.php @@ -331,6 +331,7 @@ class SimplePie_Parse_Date 'CCT' => 23400, 'CDT' => -18000, 'CEDT' => 7200, + 'CEST' => 7200, //FreshRSS 'CET' => 3600, 'CGST' => -7200, 'CGT' => -10800, -- cgit v1.2.3 From 5797344aff9ceebbdeb6e49305f3984a5c89f82c Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 16 Oct 2014 17:15:51 +0200 Subject: Fix a bug to get size of user (SQLite) --- app/Controllers/userController.php | 3 ++- app/Models/EntryDAOSQLite.php | 2 +- lib/Minz/ModelPdo.php | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 4a04737f2..d5c90a382 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -94,14 +94,15 @@ class FreshRSS_user_Controller extends Minz_ActionController { Minz_View::prependTitle(_t('users.manage') . ' · '); + // Get the correct current user. $userDAO = new FreshRSS_UserDAO(); - $username = Minz_Request::param('u', Minz_Session::param('currentUser')); if (!$userDAO->exist($username)) { $username = Minz_Session::param('currentUser'); } $this->view->current_user = $username; + // Get information about the current user. $entryDAO = FreshRSS_Factory::createEntryDao($this->view->current_user); $this->view->nb_articles = $entryDAO->count(); $this->view->size_user = $entryDAO->size(); diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 66078aca9..4a3fe24a2 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -124,6 +124,6 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { } public function size($all = false) { - return @filesize(DATA_PATH . '/' . Minz_Session::param('currentUser', '_') . '.sqlite'); + return @filesize(DATA_PATH . '/' . $this->current_user . '.sqlite'); } } diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index 66127ea22..827c89c69 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -24,6 +24,7 @@ class Minz_ModelPdo { */ protected $bd; + protected $current_user; protected $prefix; public function dbType() { @@ -46,6 +47,7 @@ class Minz_ModelPdo { if ($currentUser === null) { $currentUser = Minz_Session::param('currentUser', '_'); } + $this->current_user = $currentUser; try { $type = $db['type']; -- cgit v1.2.3 From 74be86d7e817bcccdc0052c54fefdc8379d9fe7f Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Fri, 17 Oct 2014 09:33:35 +0200 Subject: Reorganise menus (aside and header) --- app/layout/aside_configure.phtml | 2 +- app/layout/header.phtml | 3 ++- lib/lib_opml.php | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index 7a9d0d839..2e2b87203 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -18,12 +18,12 @@
  • -
  • +
  • diff --git a/app/layout/header.phtml b/app/layout/header.phtml index 7e7c1b477..c680cbcdc 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -62,9 +62,10 @@ if (Minz_Configuration::canLogIn()) {
  • -
  • +
  • +
  • diff --git a/lib/lib_opml.php b/lib/lib_opml.php index 16a9921ea..f320335bb 100644 --- a/lib/lib_opml.php +++ b/lib/lib_opml.php @@ -101,6 +101,7 @@ function libopml_parse_string($xml) { // First, we get all "head" elements. Head is required but its sub-elements // are optional. + // TODO: test head exists! foreach ($opml->head->children() as $key => $value) { if (in_array($key, unserialize(HEAD_ELEMENTS), true)) { $array['head'][$key] = (string)$value; @@ -114,6 +115,7 @@ function libopml_parse_string($xml) { // Then, we get body oulines. Body must contain at least one outline // element. $at_least_one_outline = false; + // TODO: test body exists! foreach ($opml->body->children() as $key => $value) { if ($key === 'outline') { $at_least_one_outline = true; -- cgit v1.2.3 From 7080a32650ab8b19e917d8add944a75cc98381bc Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 20 Oct 2014 11:54:31 +0200 Subject: Add checking installation feature --- app/Controllers/updateController.php | 14 +++++- app/Models/DatabaseDAO.php | 83 ++++++++++++++++++++++++++++++++++++ app/Models/DatabaseDAOSQLite.php | 48 +++++++++++++++++++++ app/Models/Factory.php | 9 ++++ app/SQL/install.sql.mysql.php | 2 - app/SQL/install.sql.sqlite.php | 2 - app/layout/aside_configure.phtml | 7 ++- app/layout/header.phtml | 1 + app/views/update/checkInstall.phtml | 30 +++++++++++++ lib/lib_rss.php | 62 +++++++++++++++++++++++++++ 10 files changed, 252 insertions(+), 6 deletions(-) create mode 100644 app/Models/DatabaseDAO.php create mode 100644 app/Models/DatabaseDAOSQLite.php create mode 100644 app/views/update/checkInstall.phtml (limited to 'lib') diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 9d1e1ddf5..4ebb11f51 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -12,7 +12,6 @@ class FreshRSS_update_Controller extends Minz_ActionController { invalidateHttpCache(); - Minz_View::prependTitle(_t('update_system') . ' · '); $this->view->update_to_apply = false; $this->view->last_update_time = 'unknown'; $this->view->check_last_hour = false; @@ -24,6 +23,8 @@ class FreshRSS_update_Controller extends Minz_ActionController { } public function indexAction() { + Minz_View::prependTitle(_t('update_system') . ' · '); + if (file_exists(UPDATE_FILENAME) && !is_writable(FRESHRSS_PATH)) { $this->view->message = array( 'status' => 'bad', @@ -126,4 +127,15 @@ class FreshRSS_update_Controller extends Minz_ActionController { } } } + + /** + * This action displays information about installation. + */ + public function checkInstallAction() { + Minz_View::prependTitle(_t('gen.title.check_install') . ' · '); + + $this->view->status_php = check_install_php(); + $this->view->status_files = check_install_files(); + $this->view->status_database = check_install_database(); + } } diff --git a/app/Models/DatabaseDAO.php b/app/Models/DatabaseDAO.php new file mode 100644 index 000000000..0d85718e3 --- /dev/null +++ b/app/Models/DatabaseDAO.php @@ -0,0 +1,83 @@ +bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + $tables = array( + $this->prefix . 'category' => false, + $this->prefix . 'feed' => false, + $this->prefix . 'entry' => false, + ); + foreach ($res as $value) { + $tables[array_pop($value)] = true; + } + + return count(array_keys($tables, true, true)) == count($tables); + } + + public function getSchema($table) { + $sql = 'DESC ' . $this->prefix . $table; + $stm = $this->bd->prepare($sql); + $stm->execute(); + + return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC)); + } + + public function checkTable($table, $schema) { + $columns = $this->getSchema($table); + + $ok = (count($columns) == count($schema)); + foreach ($columns as $c) { + $ok &= in_array($c['name'], $schema); + } + + return $ok; + } + + public function categoryIsCorrect() { + return $this->checkTable('category', array( + 'id', 'name' + )); + } + + public function feedIsCorrect() { + return $this->checkTable('feed', array( + 'id', 'url', 'category', 'name', 'website', 'description', 'lastUpdate', + 'priority', 'pathEntries', 'httpAuth', 'error', 'keep_history', 'ttl', + 'cache_nbEntries', 'cache_nbUnreads' + )); + } + + public function entryIsCorrect() { + return $this->checkTable('entry', array( + 'id', 'guid', 'title', 'author', 'content_bin', 'link', 'date', 'is_read', + 'is_favorite', 'id_feed', 'tags' + )); + } + + public function daoToSchema($dao) { + return array( + 'name' => $dao['Field'], + 'type' => strtolower($dao['Type']), + 'notnull' => (bool)$dao['Null'], + 'default' => $dao['Default'], + ); + } + + public function listDaoToSchema($listDAO) { + $list = array(); + + foreach ($listDAO as $dao) { + $list[] = $this->daoToSchema($dao); + } + + return $list; + } +} diff --git a/app/Models/DatabaseDAOSQLite.php b/app/Models/DatabaseDAOSQLite.php new file mode 100644 index 000000000..7f53f967d --- /dev/null +++ b/app/Models/DatabaseDAOSQLite.php @@ -0,0 +1,48 @@ +bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + $tables = array( + 'category' => false, + 'feed' => false, + 'entry' => false, + ); + foreach ($res as $value) { + $tables[$value['name']] = true; + } + + return count(array_keys($tables, true, true)) == count($tables); + } + + public function getSchema($table) { + $sql = 'PRAGMA table_info(' . $table . ')'; + $stm = $this->bd->prepare($sql); + $stm->execute(); + + return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC)); + } + + public function entryIsCorrect() { + return $this->checkTable('entry', array( + 'id', 'guid', 'title', 'author', 'content', 'link', 'date', 'is_read', + 'is_favorite', 'id_feed', 'tags' + )); + } + + public function daoToSchema($dao) { + return array( + 'name' => $dao['name'], + 'type' => strtolower($dao['type']), + 'notnull' => $dao['notnull'] === '1' ? true : false, + 'default' => $dao['dflt_value'], + ); + } +} diff --git a/app/Models/Factory.php b/app/Models/Factory.php index 93f4552f7..91cb84998 100644 --- a/app/Models/Factory.php +++ b/app/Models/Factory.php @@ -29,4 +29,13 @@ class FreshRSS_Factory { } } + public static function createDatabaseDAO($username = null) { + $db = Minz_Configuration::dataBase(); + if ($db['type'] === 'sqlite') { + return new FreshRSS_DatabaseDAOSQLite($username); + } else { + return new FreshRSS_DatabaseDAO($username); + } + } + } diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php index 16cb3a3b8..cf0159199 100644 --- a/app/SQL/install.sql.mysql.php +++ b/app/SQL/install.sql.mysql.php @@ -57,5 +57,3 @@ INSERT IGNORE INTO `%1$scategory` (id, name) VALUES(1, "%2$s"); '); define('SQL_DROP_TABLES', 'DROP TABLES %1$sentry, %1$sfeed, %1$scategory'); - -define('SQL_SHOW_TABLES', 'SHOW tables;'); diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php index 7988ada04..30bca2810 100644 --- a/app/SQL/install.sql.sqlite.php +++ b/app/SQL/install.sql.sqlite.php @@ -55,5 +55,3 @@ $SQL_CREATE_TABLES = array( ); define('SQL_DROP_TABLES', 'DROP TABLES %1$sentry, %1$sfeed, %1$scategory'); - -define('SQL_SHOW_TABLES', 'SELECT name FROM sqlite_master WHERE type="table"'); diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index 20446c877..32dc19a4e 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -31,7 +31,12 @@
  • -
  • +
  • + +
  • +
  • diff --git a/app/layout/header.phtml b/app/layout/header.phtml index e848ac4eb..506cec175 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -68,6 +68,7 @@ if (Minz_Configuration::canLogIn()) {
  • +
  • diff --git a/app/views/update/checkInstall.phtml b/app/views/update/checkInstall.phtml new file mode 100644 index 000000000..32058714e --- /dev/null +++ b/app/views/update/checkInstall.phtml @@ -0,0 +1,30 @@ +partial('aside_configure'); ?> + +
    + + +

    + + status_php as $key => $status) { ?> +

    + +

    + + +

    + + status_files as $key => $status) { ?> +

    + +

    + + +

    + + status_database as $key => $status) { ?> +

    + +

    + + +
    diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 2f9a2ea45..dbed207d0 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -245,3 +245,65 @@ function is_referer_from_same_domain() { } return (isset($host['port']) ? $host['port'] : 0) === (isset($referer['port']) ? $referer['port'] : 0); } + + +/** + * + */ +function check_install_php() { + return array( + 'php' => version_compare(PHP_VERSION, '5.2.1') >= 0, + 'minz' => file_exists(LIB_PATH . '/Minz'), + 'curl' => extension_loaded('curl'), + 'pdo_mysql' => extension_loaded('pdo_mysql'), + 'pdo_sqlite' => extension_loaded('pdo_sqlite'), + 'pdo' => extension_loaded('pdo_mysql') || extension_loaded('pdo_sqlite'), + 'pcre' => extension_loaded('pcre'), + 'ctype' => extension_loaded('ctype'), + 'dom' => class_exists('DOMDocument'), + 'json' => extension_loaded('json'), + 'zip' => extension_loaded('zip'), + ); +} + + +/** + * + */ +function check_install_files() { + return array( + 'data' => DATA_PATH && is_writable(DATA_PATH), + 'cache' => CACHE_PATH && is_writable(CACHE_PATH), + 'logs' => LOG_PATH && is_writable(LOG_PATH), + 'favicons' => is_writable(DATA_PATH . '/favicons'), + 'persona' => is_writable(DATA_PATH . '/persona'), + 'tokens' => is_writable(DATA_PATH . '/tokens'), + ); +} + + +/** + * + */ +function check_install_database() { + $status = array( + 'connection' => true, + 'tables' => false, + 'categories' => false, + 'feeds' => false, + 'entries' => false, + ); + + try { + $dbDAO = FreshRSS_Factory::createDatabaseDAO(); + + $status['tables'] = $dbDAO->tablesAreCorrect(); + $status['categories'] = $dbDAO->categoryIsCorrect(); + $status['feeds'] = $dbDAO->feedIsCorrect(); + $status['entries'] = $dbDAO->entryIsCorrect(); + } catch(Minz_PDOConnectionException $e) { + $status['connection'] = false; + } + + return $status; +} -- cgit v1.2.3 From 61a2f9387f2d7f681040c1641c7601fa3002c8f8 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 20 Oct 2014 12:42:46 +0200 Subject: Fix i18n (french and english) --- app/i18n/en.php | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- app/i18n/fr.php | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- lib/lib_rss.php | 6 ++++++ 3 files changed, 112 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/app/i18n/en.php b/app/i18n/en.php index 283d28dc6..b24af38c1 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -9,6 +9,53 @@ 'add_category' => 'Add a category', 'add_query' => 'Add a query', 'add_rss_feed' => 'Add a RSS feed', + 'admin.check_install.cache.nok' => 'Check permissions on ./data/cache directory. HTTP server must have rights to write into', + 'admin.check_install.cache.ok' => 'Permissions on cache directory are good.', + 'admin.check_install.categories.nok' => 'Category table is bad configured.', + 'admin.check_install.categories.ok' => 'Category table is ok.', + 'admin.check_install.connection.nok' => 'Connection to the database cannot being established.', + 'admin.check_install.connection.ok' => 'Connection to the database is ok.', + 'admin.check_install.ctype.nok' => 'You lack a required library for character type checking (php-ctype).', + 'admin.check_install.ctype.ok' => 'You have the required library for character type checking (ctype).', + 'admin.check_install.curl.nok' => 'You lack cURL (php5-curl package).', + 'admin.check_install.curl.ok' => 'You have version %s of cURL.', + 'admin.check_install.data.nok' => 'Check permissions on ./data directory. HTTP server must have rights to write into', + 'admin.check_install.data.ok' => 'Permissions on data directory are good.', + 'admin.check_install.database' => 'Database installation', + 'admin.check_install.dom.nok' => 'You lack a required library to browse the DOM (php-xml package).', + 'admin.check_install.dom.ok' => 'You have the required library to browse the DOM.', + 'admin.check_install.entries.nok' => 'Entry table is bad configured.', + 'admin.check_install.entries.ok' => 'Entry table is ok.', + 'admin.check_install.favicons.nok' => 'Check permissions on ./data/favicons directory. HTTP server must have rights to write into', + 'admin.check_install.favicons.ok' => 'Permissions on favicons directory are good.', + 'admin.check_install.feeds.nok' => 'Feed table is bad configured.', + 'admin.check_install.feeds.ok' => 'Feed table is ok.', + 'admin.check_install.files' => 'File installation', + 'admin.check_install.json.nok' => 'You lack JSON (php5-json package).', + 'admin.check_install.json.ok' => 'You have version %s of JSON.', + 'admin.check_install.logs.nok' => 'Check permissions on ./data/logs directory. HTTP server must have rights to write into', + 'admin.check_install.logs.ok' => 'Permissions on logs directory are good.', + 'admin.check_install.minz.nok' => 'You lack the Minz framework.', + 'admin.check_install.minz.ok' => 'You have the Minz framework.', + 'admin.check_install.pcre.nok' => 'You lack a required library for regular expressions (php-pcre).', + 'admin.check_install.pcre.ok' => 'You have the required library for regular expressions (PCRE).', + 'admin.check_install.pdo.nok' => 'You lack PDO or one of the supported drivers (pdo_mysql, pdo_sqlite).', + 'admin.check_install.pdo.ok' => 'You have PDO and at least one of the supported drivers (pdo_mysql, pdo_sqlite).', + 'admin.check_install.pdo_mysql.nok' => 'You lack PDO for MySQL (it\'s ok if PDO is good).', + 'admin.check_install.pdo_mysql.ok' => 'You have PDO for MySQL.', + 'admin.check_install.pdo_sqlite.nok' => 'You lack PDO for SQLite (it\'s ok if PDO is good).', + 'admin.check_install.pdo_sqlite.ok' => 'You have PDO for SQLite.', + 'admin.check_install.persona.nok' => 'Check permissions on ./data/persona directory. HTTP server must have rights to write into', + 'admin.check_install.persona.ok' => 'Permissions on Mozilla Persona directory are good.', + 'admin.check_install.php' => 'PHP installation', + 'admin.check_install.php.nok' => 'Your PHP version is %s but FreshRSS requires at least version %s.', + 'admin.check_install.php.ok' => 'Your PHP version is %s, which is compatible with FreshRSS.', + 'admin.check_install.tables.nok' => 'There is one or more lacking tables in the database.', + 'admin.check_install.tables.ok' => 'Tables are existing in the database.', + 'admin.check_install.tokens.nok' => 'Check permissions on ./data/tokens directory. HTTP server must have rights to write into', + 'admin.check_install.tokens.ok' => 'Permissions on tokens directory are good.', + 'admin.check_install.zip.nok' => 'You lack ZIP extension (php5-zip package).', + 'admin.check_install.zip.ok' => 'You have version %s of ZIP extension.', 'admin.users.articles_and_size' => '%s articles (%s)', 'administration' => 'Manage', 'advanced' => 'Advanced', @@ -118,8 +165,8 @@ 'current_user' => 'Current user', 'damn' => 'Damn!', 'data_is_ok' => 'Permissions on data directory are good', - 'dec' => 'dec', 'Dec' => '\\D\\e\\c\\e\\m\\b\\e\\r', + 'dec' => 'dec', 'december' => 'Dec', 'default_category' => 'Uncategorized', 'default_user' => 'Username of the default user (maximum 16 alphanumeric characters)', @@ -186,8 +233,10 @@ 'g+' => 'Google+', 'gen.menu.admin' => 'Administration', 'gen.menu.authentication' => 'Authentication', + 'gen.menu.check_install' => 'Installation checking', 'gen.menu.manage_users' => 'Manage users', 'gen.menu.profil' => 'Profil', + 'gen.title.check_install' => 'Installation checking', 'general_configuration' => 'General configuration', 'general_conf_is_ok' => 'General configuration has been saved.', 'github_or_email' => 'on Github or by mail', @@ -219,8 +268,8 @@ 'javascript_for_shortcuts' => 'JavaScript must be enabled in order to use shortcuts', 'javascript_is_better' => 'FreshRSS is more pleasant with JavaScript enabled', 'javascript_should_be_activated' => 'JavaScript must be enabled', - 'jul' => 'jul', 'Jul' => '\\J\\u\\l\\y', + 'jul' => 'jul', 'july' => 'Jul', 'jump_next' => 'jump to next unread sibling (feed or category)', 'jun' => 'jun', @@ -297,8 +346,8 @@ 'number_feeds' => '%d feeds', 'n_entries_deleted' => '%d articles have been deleted', 'n_feeds_actualized' => '%d feeds have been updated', - 'Oct' => '\\O\\c\\t\\o\\b\\e\\r', 'oct' => 'oct', + 'Oct' => '\\O\\c\\t\\o\\b\\e\\r', 'october' => 'Oct', 'ok' => 'Ok!', 'older_first' => 'Oldest first', @@ -375,8 +424,8 @@ 'seconds_(0_means_no_timeout)' => 'seconds (0 means no timeout)', 'see_on_website' => 'See on original website', 'see_website' => 'See website', - 'sep' => 'sep', 'Sep' => '\\S\\e\\p\\t\\e\\m\\b\\e\\r', + 'sep' => 'sep', 'september' => 'Sep', 'shaarli' => 'Shaarli', 'share' => 'Share', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 0517544d5..22494274c 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -9,6 +9,53 @@ 'add_category' => 'Ajouter une catégorie', 'add_query' => 'Créer un filtre', 'add_rss_feed' => 'Ajouter un flux RSS', + 'admin.check_install.cache.nok' => 'Veuillez vérifier les droits sur le répertoire ./data/cache. Le serveur HTTP doit être capable d’écrire dedans', + 'admin.check_install.cache.ok' => 'Les droits sur le répertoire de cache sont bons.', + 'admin.check_install.categories.nok' => 'La table category est mal configurée.', + 'admin.check_install.categories.ok' => 'La table category est bien configurée.', + 'admin.check_install.connection.nok' => 'La connexion à la base de données est impossible.', + 'admin.check_install.connection.ok' => 'La connexion à la base de données est bonne.', + 'admin.check_install.ctype.nok' => 'Il manque une librairie pour la vérification des types de caractères (php-ctype).', + 'admin.check_install.ctype.ok' => 'Vous disposez du nécessaire pour la vérification des types de caractères (ctype).', + 'admin.check_install.curl.nok' => 'Vous ne disposez pas de cURL (paquet php5-curl).', + 'admin.check_install.curl.ok' => 'Vous disposez de cURL dans sa version %s.', + 'admin.check_install.data.nok' => 'Veuillez vérifier les droits sur le répertoire ./data. Le serveur HTTP doit être capable d’écrire dedans', + 'admin.check_install.data.ok' => 'Les droits sur le répertoire de data sont bons.', + 'admin.check_install.database' => 'Installation de la base de données', + 'admin.check_install.dom.nok' => 'Il manque une librairie pour parcourir le DOM (paquet php-xml).', + 'admin.check_install.dom.ok' => 'Vous disposez du nécessaire pour parcourir le DOM.', + 'admin.check_install.entries.nok' => 'La table entry est mal configurée.', + 'admin.check_install.entries.ok' => 'La table entry est bien configurée.', + 'admin.check_install.favicons.nok' => 'Veuillez vérifier les droits sur le répertoire ./data/favicons. Le serveur HTTP doit être capable d’écrire dedans', + 'admin.check_install.favicons.ok' => 'Les droits sur le répertoire des favicons sont bons.', + 'admin.check_install.feeds.nok' => 'La table feed est mal configurée.', + 'admin.check_install.feeds.ok' => 'La table feed est bien configurée.', + 'admin.check_install.files' => 'Installation des fichiers', + 'admin.check_install.json.nok' => 'Vous ne disposez pas de JSON (paquet php5-json).', + 'admin.check_install.json.ok' => 'Vous disposez de JSON dans sa version %s.', + 'admin.check_install.logs.nok' => 'Veuillez vérifier les droits sur le répertoire ./data/logs. Le serveur HTTP doit être capable d’écrire dedans', + 'admin.check_install.logs.ok' => 'Les droits sur le répertoire des logs sont bons.', + 'admin.check_install.minz.nok' => 'Vous ne disposez pas de la librairie Minz.', + 'admin.check_install.minz.ok' => 'Vous disposez du framework Minz', + 'admin.check_install.pcre.nok' => 'Il manque une librairie pour les expressions régulières (php-pcre).', + 'admin.check_install.pcre.ok' => 'Vous disposez du nécessaire pour les expressions régulières (PCRE).', + 'admin.check_install.pdo.nok' => 'Vous ne disposez pas de PDO ou d’un des drivers supportés (pdo_mysql, pdo_sqlite).', + 'admin.check_install.pdo.ok' => 'Vous disposez de PDO et d’au moins un des drivers supportés (pdo_mysql, pdo_sqlite).', + 'admin.check_install.pdo_mysql.nok' => 'Vous ne possédez pas PDO pour MySQL (ok si PDO est bon).', + 'admin.check_install.pdo_mysql.ok' => 'Vous possédez PDO pour MySQL.', + 'admin.check_install.pdo_sqlite.nok' => 'Vous ne possédez pas PDO pour SQLite (ok si PDO est bon).', + 'admin.check_install.pdo_sqlite.ok' => 'Vous possédez PDO pour SQLite.', + 'admin.check_install.persona.nok' => 'Veuillez vérifier les droits sur le répertoire ./data/persona. Le serveur HTTP doit être capable d’écrire dedans', + 'admin.check_install.persona.ok' => 'Les droits sur le répertoire de Mozilla Persona sont bons.', + 'admin.check_install.php' => 'Installation de PHP', + 'admin.check_install.php.nok' => 'Votre version de PHP est la %s mais FreshRSS requiert au moins la version %s.', + 'admin.check_install.php.ok' => 'Votre version de PHP est la %s, qui est compatible avec FreshRSS.', + 'admin.check_install.tables.nok' => 'Il manque une ou plusieurs tables en base de données.', + 'admin.check_install.tables.ok' => 'Les tables sont bien présentes en base de données.', + 'admin.check_install.tokens.nok' => 'Veuillez vérifier les droits sur le répertoire ./data/tokens. Le serveur HTTP doit être capable d’écrire dedans', + 'admin.check_install.tokens.ok' => 'Les droits sur le répertoire des tokens sont bons.', + 'admin.check_install.zip.nok' => 'Vous ne disposez pas de l\'extension ZIP (paquet php5-zip).', + 'admin.check_install.zip.ok' => 'Vous disposez de l\'extension ZIP dans sa version %s.', 'admin.users.articles_and_size' => '%s articles (%s)', 'administration' => 'Gérer', 'advanced' => 'Avancé', @@ -118,8 +165,8 @@ 'current_user' => 'Utilisateur actuel', 'damn' => 'Arf !', 'data_is_ok' => 'Les droits sur le répertoire de data sont bons', - 'dec' => 'déc.', 'Dec' => '\\d\\é\\c\\e\\m\\b\\r\\e', + 'dec' => 'déc.', 'december' => 'décembre', 'default_category' => 'Sans catégorie', 'default_user' => 'Nom de l’utilisateur par défaut (16 caractères alphanumériques maximum)', @@ -186,8 +233,10 @@ 'g+' => 'Google+', 'gen.menu.admin' => 'Administration', 'gen.menu.authentication' => 'Authentification', + 'gen.menu.check_install' => 'Vérification de l\'installation', 'gen.menu.manage_users' => 'Gestion des utilisateurs', 'gen.menu.profil' => 'Profil', + 'gen.title.check_install' => 'Vérification de l\'installation', 'general_configuration' => 'Configuration générale', 'general_conf_is_ok' => 'La configuration générale a été enregistrée.', 'github_or_email' => 'sur Github ou par courriel', @@ -219,8 +268,8 @@ 'javascript_for_shortcuts' => 'Le JavaScript doit être activé pour pouvoir profiter des raccourcis.', 'javascript_is_better' => 'FreshRSS est plus agréable à utiliser avec JavaScript activé', 'javascript_should_be_activated' => 'Le JavaScript doit être activé.', - 'jul' => 'jui.', 'Jul' => '\\j\\u\\i\\l\\l\\e\\t', + 'jul' => 'jui.', 'july' => 'juillet', 'jump_next' => 'sauter au prochain voisin non lu (flux ou catégorie)', 'jun' => 'juin', @@ -297,8 +346,8 @@ 'number_feeds' => '%d flux', 'n_entries_deleted' => '%d articles ont été supprimés.', 'n_feeds_actualized' => '%d flux ont été mis à jour.', - 'Oct' => '\\o\\c\\t\\o\\b\\r\\e', 'oct' => 'oct.', + 'Oct' => '\\o\\c\\t\\o\\b\\r\\e', 'october' => 'octobre', 'ok' => 'Ok !', 'older_first' => 'Plus anciens en premier', @@ -375,8 +424,8 @@ 'seconds_(0_means_no_timeout)' => 'secondes (0 signifie aucun timeout ) ', 'see_on_website' => 'Voir sur le site d’origine', 'see_website' => 'Voir le site', - 'sep' => 'sep.', 'Sep' => '\\s\\e\\p\\t\\e\\m\\b\\r\\e', + 'sep' => 'sep.', 'september' => 'septembre', 'shaarli' => 'Shaarli', 'share' => 'Partager', diff --git a/lib/lib_rss.php b/lib/lib_rss.php index dbed207d0..9abdf18ce 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -248,7 +248,9 @@ function is_referer_from_same_domain() { /** + * Check PHP and its extensions are well-installed. * + * @return array of tested values. */ function check_install_php() { return array( @@ -268,7 +270,9 @@ function check_install_php() { /** + * Check different data files and directories exist. * + * @return array of tested values. */ function check_install_files() { return array( @@ -283,7 +287,9 @@ function check_install_files() { /** + * Check database is well-installed. * + * @return array of tested values. */ function check_install_database() { $status = array( -- cgit v1.2.3 From 8a7bab3a55442f85553ab1d897084e89c10f7e05 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 20 Oct 2014 19:35:22 +0200 Subject: Refactoring of indexController Global view has been moved to a different action (all is not working) See https://github.com/marienfressinaud/FreshRSS/issues/634 and https://github.com/marienfressinaud/FreshRSS/issues/655 --- app/Controllers/errorController.php | 2 +- app/Controllers/indexController.php | 33 +++++++++++++++----- app/layout/nav_menu.phtml | 2 +- app/views/helpers/view/global_view.phtml | 53 -------------------------------- app/views/index/global.phtml | 46 +++++++++++++++++++++++++++ app/views/index/index.phtml | 2 -- lib/lib_rss.php | 4 +++ p/scripts/global_view.js | 3 +- 8 files changed, 79 insertions(+), 66 deletions(-) delete mode 100644 app/views/helpers/view/global_view.phtml create mode 100644 app/views/index/global.phtml (limited to 'lib') diff --git a/app/Controllers/errorController.php b/app/Controllers/errorController.php index 76ab930e0..6c080bea8 100644 --- a/app/Controllers/errorController.php +++ b/app/Controllers/errorController.php @@ -37,7 +37,7 @@ class FreshRSS_error_Controller extends Minz_ActionController { if ($this->view->errorMessage == '') { switch($code_int) { case 403: - $this->view->errorMessage = _t('forbidden_access'); + $this->view->errorMessage = _t('access_denied'); break; case 404: default: diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index f994e257c..e1ce71b28 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -1,5 +1,8 @@ array(_t('access_denied'))) - ); + Minz_Error::error(403); return; } elseif ($output !== 'rss') { // "hard" redirection is not required, just ask dispatcher to @@ -201,17 +201,34 @@ class FreshRSS_index_Controller extends Minz_ActionController { return false; } } + + /** + * This action displays the global view of FreshRSS. + */ + public function globalAction() { + if (!FreshRSS_Auth::hasAccess() && !Minz_Configuration::allowAnonymous()) { + Minz_Error::error(403); + } + + Minz_View::appendScript(Minz_Url::display('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); + + $catDAO = new FreshRSS_CategoryDAO(); + $this->view->categories = $catDAO->listCategories(); + } + /** + * This action displays the about page of FreshRSS. + */ public function aboutAction() { Minz_View::prependTitle(_t('about') . ' · '); } + /** + * This action displays logs of FreshRSS for the current user. + */ public function logsAction() { if (!FreshRSS_Auth::hasAccess()) { - Minz_Error::error( - 403, - array('error' => array(_t('access_denied'))) - ); + Minz_Error::error(403); } Minz_View::prependTitle(_t('logs') . ' · '); diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index bb9468ab1..1a26422df 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -240,7 +240,7 @@ - + diff --git a/app/views/helpers/view/global_view.phtml b/app/views/helpers/view/global_view.phtml deleted file mode 100644 index ae8af820d..000000000 --- a/app/views/helpers/view/global_view.phtml +++ /dev/null @@ -1,53 +0,0 @@ -partial('nav_menu'); ?> - -entries)) { ?> -
    - 'index', 'a' => 'index', 'params' => array()); - if (FreshRSS_Context::$conf->view_mode !== 'normal') { - $arUrl['params']['output'] = 'normal'; - } - $p = Minz_Request::param('state', ''); - if (($p != '') && (FreshRSS_Context::$conf->default_view !== $p)) { - $arUrl['params']['state'] = $p; - } - - foreach ($this->cat_aside as $cat) { - $feeds = $cat->feeds(); - if (!empty($feeds)) { -?> -
    - - -
    - -
    - -
    -
    display_posts ? '' : ' class="hide_posts"'; ?>> - -
    - - -
    -

    -

    -
    - diff --git a/app/views/index/global.phtml b/app/views/index/global.phtml new file mode 100644 index 000000000..a72e431df --- /dev/null +++ b/app/views/index/global.phtml @@ -0,0 +1,46 @@ +partial('nav_menu'); ?> + +
    + 'index', + 'a' => 'index', + 'params' => array( + 'state' => FreshRSS_Context::$state + ) + ); + + foreach ($this->categories as $cat) { + $feeds = $cat->feeds(); + $url_base['params']['get'] = 'c_' . $cat->id(); + + if (!empty($feeds)) { +?> +
    + + +
      + nbNotRead(); + $error = $feed->inError() ? 'error ' : ''; + $empty = $feed->nbEntries() === 0 ? 'empty ' : ''; + $url_base['params']['get'] = 'f_' . $feed->id(); + ?> +
    • + ✇ + name(); ?> +
    • + +
    +
    + +
    + +
    +
    display_posts ? '' : ' class="hide_posts"'; ?>> + +
    diff --git a/app/views/index/index.phtml b/app/views/index/index.phtml index a59063557..8b93461dd 100644 --- a/app/views/index/index.phtml +++ b/app/views/index/index.phtml @@ -7,8 +7,6 @@ if (FreshRSS_Auth::hasAccess() || Minz_Configuration::allowAnonymous()) { $this->renderHelper('view/normal_view'); } elseif ($output === 'reader') { $this->renderHelper('view/reader_view'); - } elseif ($output === 'global') { - $this->renderHelper('view/global_view'); } elseif ($output === 'rss') { $this->renderHelper('view/rss_view'); } else { diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 9abdf18ce..80eb206d2 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -60,6 +60,10 @@ function formatNumber($n, $precision = 0) { return str_replace(' ', ' ', //Espace insécable //TODO: remplacer par une espace _fine_ insécable number_format($n, $precision, '.', ' ')); //number_format does not seem to be Unicode-compatible } +function format_number($n, $precision = 0) { + // TODO: coding style, prefer THIS function. Remove formatNumber. + return formatNumber($n, $precision); +} function formatBytes($bytes, $precision = 2, $system = 'IEC') { if ($system === 'IEC') { diff --git a/p/scripts/global_view.js b/p/scripts/global_view.js index 6e1f61066..8c2e6c1a2 100644 --- a/p/scripts/global_view.js +++ b/p/scripts/global_view.js @@ -50,7 +50,8 @@ function init_close_panel() { } function init_global_view() { - $("#stream .box-category a").click(function () { + // TODO: should be based on generic classes. + $(".box a").click(function () { var link = $(this).attr("href"); load_panel(link); -- cgit v1.2.3 From 80cffa6de51771cd80995fb1c4f1e04ee868eb45 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 21 Oct 2014 16:46:36 +0200 Subject: Views are in dedicated actions + improve Context - Seperate normal, global and rss outputs in dedicated actions (NOT WORKING YET!) - Rewrite aside_flux and nav_menu to use Context object - Improve Context object See https://github.com/marienfressinaud/FreshRSS/issues/634 --- app/Controllers/indexController.php | 97 ++++++++------- app/FreshRSS.php | 10 -- app/Models/Context.php | 65 +++++++++- app/layout/aside_flux.phtml | 103 ++++++---------- app/layout/nav_menu.phtml | 205 +++++++------------------------ app/views/helpers/view/normal_view.phtml | 191 ---------------------------- app/views/helpers/view/rss_view.phtml | 29 ----- app/views/index/global.phtml | 4 +- app/views/index/normal.phtml | 191 ++++++++++++++++++++++++++++ app/views/index/rss.phtml | 29 +++++ lib/Minz/Request.php | 7 ++ 11 files changed, 425 insertions(+), 506 deletions(-) delete mode 100644 app/views/helpers/view/normal_view.phtml delete mode 100755 app/views/helpers/view/rss_view.phtml create mode 100644 app/views/index/normal.phtml create mode 100755 app/views/index/rss.phtml (limited to 'lib') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index e1ce71b28..d348ea1d0 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -7,47 +7,17 @@ class FreshRSS_index_Controller extends Minz_ActionController { private $nb_not_read_cat = 0; public function indexAction() { - $output = Minz_Request::param('output'); - $token = FreshRSS_Context::$conf->token; - - // check if user is logged in - if (!FreshRSS_Auth::hasAccess() && !Minz_Configuration::allowAnonymous()) { - $token_param = Minz_Request::param('token', ''); - $token_is_ok = ($token != '' && $token === $token_param); - if ($output === 'rss' && !$token_is_ok) { - Minz_Error::error(403); - return; - } elseif ($output !== 'rss') { - // "hard" redirection is not required, just ask dispatcher to - // forward to the login form without 302 redirection - Minz_Request::forward(array('c' => 'auth', 'a' => 'login')); - return; - } - } + // TODO: update the context with information from request. + // TODO: then, in dedicated action, get corresponding entries - $params = Minz_Request::params(); - if (isset($params['search'])) { - $params['search'] = urlencode($params['search']); - } - - $this->view->url = array( + $prefered_output = FreshRSS_Context::$conf->view_mode; + Minz_Request::forward(array( 'c' => 'index', - 'a' => 'index', - 'params' => $params - ); + 'a' => $prefered_output + )); - if ($output === 'rss') { - // no layout for RSS output - $this->view->_useLayout(false); - header('Content-Type: application/rss+xml; charset=utf-8'); - } elseif ($output === 'global') { - Minz_View::appendScript(Minz_Url::display('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); - } + return; - $catDAO = new FreshRSS_CategoryDAO(); - $entryDAO = FreshRSS_Factory::createEntryDao(); - - $this->view->cat_aside = $catDAO->listCategories(); $this->view->nb_favorites = $entryDAO->countUnreadReadFavorites(); $this->view->nb_not_read = FreshRSS_CategoryDAO::CountUnreads($this->view->cat_aside, 1); $this->view->currentName = ''; @@ -60,10 +30,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $getId = substr($get, 2); if (!$this->checkAndProcessType($getType, $getId)) { Minz_Log::debug('Not found [' . $getType . '][' . $getId . ']'); - Minz_Error::error( - 404, - array('error' => array(_t('page_not_found'))) - ); + Minz_Error::error(404); return; } @@ -144,10 +111,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $this->view->entries = $entries; } catch (FreshRSS_EntriesGetter_Exception $e) { Minz_Log::notice($e->getMessage()); - Minz_Error::error( - 404, - array('error' => array(_t('page_not_found'))) - ); + Minz_Error::error(404); } } @@ -202,20 +166,59 @@ class FreshRSS_index_Controller extends Minz_ActionController { } } + /** + * This action displays the normal view of FreshRSS. + */ + public function normalAction() { + if (!FreshRSS_Auth::hasAccess() && !Minz_Configuration::allowAnonymous()) { + Minz_Request::forward(array('c' => 'auth', 'a' => 'login')); + return; + } + + $catDAO = new FreshRSS_CategoryDAO(); + $entryDAO = FreshRSS_Factory::createEntryDao(); + + $this->view->categories = $catDAO->listCategories(); + + } + /** * This action displays the global view of FreshRSS. */ public function globalAction() { if (!FreshRSS_Auth::hasAccess() && !Minz_Configuration::allowAnonymous()) { - Minz_Error::error(403); + Minz_Request::forward(array('c' => 'auth', 'a' => 'login')); + return; } Minz_View::appendScript(Minz_Url::display('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); $catDAO = new FreshRSS_CategoryDAO(); $this->view->categories = $catDAO->listCategories(); + + Minz_View::prependTitle(_t('gen.title.global_view') . ' · '); } - + + /** + * This action displays the RSS feed of FreshRSS. + */ + public function rssAction() { + $token = FreshRSS_Context::$conf->token; + $token_param = Minz_Request::param('token', ''); + $token_is_ok = ($token != '' && $token === $token_param); + + // Check if user has access. + if (!FreshRSS_Auth::hasAccess() && + !Minz_Configuration::allowAnonymous() && + !$token_is_ok) { + Minz_Error::error(403); + } + + // No layout for RSS output. + $this->view->_useLayout(false); + header('Content-Type: application/rss+xml; charset=utf-8'); + } + /** * This action displays the about page of FreshRSS. */ diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 752b14e31..b997433bf 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -26,21 +26,11 @@ class FreshRSS extends Minz_FrontController { // Load context and configuration. FreshRSS_Context::init(); - $this->loadParamsView(); $this->loadStylesAndScripts(); $this->loadNotifications(); $this->loadExtensions(); } - private function loadParamsView() { - // TODO: outputs should be different actions. - $output = Minz_Request::param('output', ''); - if (($output === '') || ($output !== 'normal' && $output !== 'rss' && $output !== 'reader' && $output !== 'global')) { - $output = FreshRSS_Context::$conf->view_mode; - Minz_Request::_param('output', $output); - } - } - private function loadStylesAndScripts() { $theme = FreshRSS_Themes::load(FreshRSS_Context::$conf->theme); if ($theme) { diff --git a/app/Models/Context.php b/app/Models/Context.php index d984fece7..b85179652 100644 --- a/app/Models/Context.php +++ b/app/Models/Context.php @@ -6,7 +6,22 @@ */ class FreshRSS_Context { public static $conf = null; + + public static $total_unread = 0; + public static $total_starred = array( + 'all' => 0, + 'read' => 0, + 'unread' => 0, + ); + public static $state = 0; + public static $current_get = array( + 'all' => false, + 'starred' => false, + 'feed' => false, + 'category' => false, + ); + public static $order = 'DESC'; public static function init() { // Init configuration. @@ -23,10 +38,56 @@ class FreshRSS_Context { Minz_Translate::init(); // Get the current state. - self::$state = self::$conf->default_view; + // self::$state = self::$conf->default_view; } - public static function stateEnabled($state) { + public static function isStateEnabled($state) { return self::$state & $state; } + + public static function getRevertState($state) { + if (self::$state & $state) { + return self::$state & ~$state; + } else { + return self::$state | $state; + } + } + + public static function currentGet() { + if (self::$current_get['all']) { + return 'a'; + } elseif (self::$current_get['starred']) { + return 's'; + } elseif (self::$current_get['feed']) { + return 'f_' . self::$current_get['feed']; + } elseif (self::$current_get['category']) { + return 'c_' . self::$current_get['category']; + } + } + + public static function isCurrentGet($get) { + $type = $get[0]; + $id = substr($get, 2); + + switch($type) { + case 'a': + return self::$current_get['all']; + case 's': + return self::$current_get['starred']; + case 'f': + return self::$current_get['feed'] === $id; + case 'c': + return self::$current_get['category'] === $id; + default: + return false; + } + } + + public static function nextStep() { + // TODO: fix this method. + return array( + 'get' => 'a', + 'idMax' => (time() - 1) . '000000' + ); + } } diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index 114ccbf56..e572e9d48 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -1,82 +1,53 @@ -
    +
    -
      - - + +
      + + +
      + + + + + -
    • -
      - - -
      +
        +
      • + +
      • + +
      • +
      • - -
      • - 'index', 'a' => 'index', 'params' => array()); - if (FreshRSS_Context::$conf->view_mode !== Minz_Request::param('output', 'normal')) { - $arUrl['params']['output'] = 'normal'; - } + foreach ($this->categories as $cat) { + $feeds = $cat->feeds(); + if (!empty($feeds)) { ?> -
      • - -
      • +
      • + name(); ?> -
      • - +
      • - cat_aside as $cat) { - $feeds = $cat->feeds(); - if (!empty($feeds)) { - $c_active = false; - $c_show = false; - if ($this->get_c == $cat->id()) { - $c_active = true; - if (!FreshRSS_Context::$conf->display_categories || $this->get_f) { - $c_show = true; - } } - ?>
      • >
          id(); - $nbEntries = $feed->nbEntries(); - $f_active = ($this->get_f == $feed_id); - ?>
        • ✇ name(); ?>
      • + ?>
      -
    "> - + diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index 6577e0109..02f2014ee 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -1,6 +1,7 @@ "use strict"; mark_when; $mail = Minz_Session::param('mail', false); $auto_actualize = Minz_Session::param('actualize_feeds', false); @@ -29,7 +30,7 @@ echo 'var context={', 'does_lazyload:', FreshRSS_Context::$conf->lazyload ? 'true' : 'false', ',', 'sticky_post:', FreshRSS_Context::isStickyPostEnabled() ? 'true' : 'false', ',', 'html5_notif_timeout:', FreshRSS_Context::$conf->html5_notif_timeout, ',', - 'auth_type:"', Minz_Configuration::authType(), '",', + 'auth_type:"', $conf->general['auth_type'], '",', 'current_user_mail:', $mail ? ('"' . $mail . '"') : 'null', ',', 'current_view:"', Minz_Request::param('output', 'normal'), '"', "},\n"; diff --git a/data/config.default.php b/data/config.default.php new file mode 100644 index 000000000..a69d8050b --- /dev/null +++ b/data/config.default.php @@ -0,0 +1,32 @@ + array( + 'environment' => 'production', + 'salt' => '', + 'base_url' => '', + 'language' => 'en', + 'title' => 'FreshRSS', + 'default_user' => '_', + 'allow_anonymous' => false, + 'allow_anonymous_refresh' => false, + 'auth_type' => 'none', + 'api_enabled' => false, + 'unsafe_autologin_enabled' => false, + ), + 'limits' => array( + 'cache_duration' => 800, + 'timeout' => 10, + 'max_inactivity' => PHP_INT_MAX, + 'max_feeds' => 16384, + 'max_categories' => 16384, + ), + 'db' => array( + 'type' => 'sqlite', + 'host' => '', + 'user' => '', + 'password' => '', + 'base' => '', + 'prefix' => '', + ), +); diff --git a/data/users/_/config.default.php b/data/users/_/config.default.php new file mode 100644 index 000000000..56d54b293 --- /dev/null +++ b/data/users/_/config.default.php @@ -0,0 +1,66 @@ + 'en', + 'old_entries' => 3, + 'keep_history_default' => 0, + 'ttl_default' => 3600, + 'mail_login' => '', + 'token' => '', + 'passwordHash' => '', + 'apiPasswordHash' => '', + 'posts_per_page' => 20, + 'view_mode' => 'normal', + 'default_view' => 'adaptive', + 'default_state' => FreshRSS_Entry::STATE_NOT_READ, + 'auto_load_more' => true, + 'display_posts' => false, + 'display_categories' => false, + 'hide_read_feeds' => true, + 'onread_jump_next' => true, + 'lazyload' => true, + 'sticky_post' => true, + 'reading_confirm' => false, + 'auto_remove_article' => false, + 'sort_order' => 'DESC', + 'anon_access' => false, + 'mark_when' => array ( + 'article' => true, + 'site' => true, + 'scroll' => false, + 'reception' => false, + ), + 'theme' => 'Origine', + 'content_width' => 'thin', + 'shortcuts' => array ( + 'mark_read' => 'r', + 'mark_favorite' => 'f', + 'go_website' => 'space', + 'next_entry' => 'j', + 'prev_entry' => 'k', + 'first_entry' => 'home', + 'last_entry' => 'end', + 'collapse_entry' => 'c', + 'load_more' => 'm', + 'auto_share' => 's', + 'focus_search' => 'a', + 'user_filter' => 'u', + 'help' => 'f1', + 'close_dropdown' => 'escape', + ), + 'topline_read' => true, + 'topline_favorite' => true, + 'topline_date' => true, + 'topline_link' => true, + 'bottomline_read' => true, + 'bottomline_favorite' => true, + 'bottomline_sharing' => true, + 'bottomline_tags' => true, + 'bottomline_date' => true, + 'bottomline_link' => true, + 'sharing' => array ( + ), + 'queries' => array ( + ), + 'html5_notif_timeout' => 0, +); diff --git a/lib/Minz/BadConfigurationException.php b/lib/Minz/BadConfigurationException.php deleted file mode 100644 index a7b77d687..000000000 --- a/lib/Minz/BadConfigurationException.php +++ /dev/null @@ -1,9 +0,0 @@ - -*/ /** - * La classe Configuration permet de gérer la configuration de l'application + * Manage configuration for the application. */ class Minz_Configuration { - const CONF_PATH_NAME = '/config.php'; - /** - * VERSION est la version actuelle de MINZ + * The list of configurations. */ - const VERSION = '1.3.1.freshrss'; // version spéciale FreshRSS + private static $config_list = array(); /** - * valeurs possibles pour l'"environment" - * SILENT rend l'application muette (pas de log) - * PRODUCTION est recommandée pour une appli en production - * (log les erreurs critiques) - * DEVELOPMENT log toutes les erreurs + * Add a new configuration to the list of configuration. + * + * @param $namespace the name of the current configuration + * @param $config_filename the filename of the configuration + * @param $default_filename a filename containing default values for the configuration + * @throws Minz_ConfigurationNamespaceException if the namespace already exists. */ - const SILENT = 0; - const PRODUCTION = 1; - const DEVELOPMENT = 2; + public static function register($namespace, $config_filename, $default_filename = null) { + if (isset(self::$config_list[$namespace])) { + throw new Minz_ConfigurationNamespaceException( + $namespace . ' namespace already exists' + ); + } + + self::$config_list[$namespace] = new Minz_Configuration( + $namespace, $config_filename, $default_filename + ); + } /** - * définition des variables de configuration - * $salt une chaîne de caractères aléatoires (obligatoire) - * $environment gère le niveau d'affichage pour log et erreurs - * $base_url le chemin de base pour accéder à l'application - * $title le nom de l'application - * $language la langue par défaut de l'application - * $db paramètres pour la base de données (tableau) - * - host le serveur de la base - * - user nom d'utilisateur - * - password mot de passe de l'utilisateur - * - base le nom de la base de données + * Parse a file and return its data. + * + * If the file does not contain a valid PHP code returning an array, an + * empty array is returned anyway. + * + * @param $filename the name of the file to parse. + * @return an array of values + * @throws Minz_FileNotExistException if the file does not exist. */ - private static $salt = ''; - private static $environment = Minz_Configuration::PRODUCTION; - private static $base_url = ''; - private static $title = ''; - private static $language = 'en'; - private static $default_user = ''; - private static $allow_anonymous = false; - private static $allow_anonymous_refresh = false; - private static $auth_type = 'none'; - private static $api_enabled = false; - private static $unsafe_autologin_enabled = false; - - private static $db = array ( - 'type' => 'mysql', - 'host' => '', - 'user' => '', - 'password' => '', - 'base' => '', - 'prefix' => '', - ); - - const MAX_SMALL_INT = 16384; - private static $limits = array( - 'cache_duration' => 800, //SimplePie cache duration in seconds - 'timeout' => 10, //SimplePie timeout in seconds - 'max_inactivity' => PHP_INT_MAX, //Time in seconds after which a user who has not used the account is considered inactive (no auto-refresh of feeds). - 'max_feeds' => Minz_Configuration::MAX_SMALL_INT, - 'max_categories' => Minz_Configuration::MAX_SMALL_INT, - ); + public static function parseFile($filename) { + if (!file_exists($filename)) { + throw new Minz_FileNotExistException($filename); + } - /* - * Getteurs - */ - public static function salt () { - return self::$salt; + $data = @include($filename); + if (is_array($data)) { + return $data; + } else { + return array(); + } } - public static function environment ($str = false) { - $env = self::$environment; - if ($str) { - switch (self::$environment) { - case self::SILENT: - $env = 'silent'; - break; - case self::DEVELOPMENT: - $env = 'development'; - break; - case self::PRODUCTION: - default: - $env = 'production'; - } + /** + * Return the configuration related to a given namespace. + * + * @param $namespace the name of the configuration to get. + * @return a Minz_Configuration object + * @throws Minz_ConfigurationNamespaceException if the namespace does not exist. + */ + public static function get($namespace) { + if (!isset(self::$config_list[$namespace])) { + throw new Minz_ConfigurationNamespaceException( + $namespace . ' namespace does not exist' + ); } - return $env; - } - public static function baseUrl () { - return self::$base_url; - } - public static function title () { - return self::$title; - } - public static function language () { - return self::$language; - } - public static function dataBase () { - return self::$db; - } - public static function limits() { - return self::$limits; - } - public static function defaultUser () { - return self::$default_user; - } - public static function allowAnonymous() { - return self::$allow_anonymous; - } - public static function allowAnonymousRefresh() { - return self::$allow_anonymous_refresh; - } - public static function authType() { - return self::$auth_type; - } - public static function needsLogin() { - return self::$auth_type !== 'none'; - } - public static function canLogIn() { - return self::$auth_type === 'form' || self::$auth_type === 'persona'; - } - public static function apiEnabled() { - return self::$api_enabled; - } - public static function unsafeAutologinEnabled() { - return self::$unsafe_autologin_enabled; + return self::$config_list[$namespace]; } - public static function _allowAnonymous($allow = false) { - self::$allow_anonymous = ((bool)$allow) && self::canLogIn(); - } - public static function _allowAnonymousRefresh($allow = false) { - self::$allow_anonymous_refresh = ((bool)$allow) && self::allowAnonymous(); - } - public static function _authType($value) { - $value = strtolower($value); - switch ($value) { - case 'form': - case 'http_auth': - case 'persona': - case 'none': - self::$auth_type = $value; - break; - } - self::_allowAnonymous(self::$allow_anonymous); - } + /** + * The namespace of the current configuration. + */ + private $namespace = ''; - public static function _enableApi($value = false) { - self::$api_enabled = (bool)$value; - } - public static function _enableAutologin($value = false) { - self::$unsafe_autologin_enabled = (bool)$value; - } + /** + * The filename for the current configuration. + */ + private $config_filename = ''; + + /** + * The filename for the current default values, null by default. + */ + private $default_filename = null; + + /** + * The configuration values, an empty array by default. + */ + private $data = array(); + + /** + * The default values, an empty array by default. + */ + private $data_default = array(); /** - * Initialise les variables de configuration - * @exception Minz_FileNotExistException si le CONF_PATH_NAME n'existe pas - * @exception Minz_BadConfigurationException si CONF_PATH_NAME mal formaté + * Create a new Minz_Configuration object. + * + * @param $namespace the name of the current configuration. + * @param $config_filename the file containing configuration values. + * @param $default_filename the file containing default values, null by default. */ - public static function init () { + private function __construct($namespace, $config_filename, $default_filename = null) { + $this->namespace = $namespace; + $this->config_filename = $config_filename; + try { - self::parseFile (); - self::setReporting (); + $this->data = self::parseFile($this->config_filename); } catch (Minz_FileNotExistException $e) { - throw $e; - } catch (Minz_BadConfigurationException $e) { - throw $e; + if (is_null($default_filename)) { + throw $e; + } } - } - public static function writeFile() { - $ini_array = array( - 'general' => array( - 'environment' => self::environment(true), - 'salt' => self::$salt, - 'base_url' => self::$base_url, - 'title' => self::$title, - 'default_user' => self::$default_user, - 'allow_anonymous' => self::$allow_anonymous, - 'allow_anonymous_refresh' => self::$allow_anonymous_refresh, - 'auth_type' => self::$auth_type, - 'api_enabled' => self::$api_enabled, - 'unsafe_autologin_enabled' => self::$unsafe_autologin_enabled, - ), - 'limits' => self::$limits, - 'db' => self::$db, - ); - @rename(DATA_PATH . self::CONF_PATH_NAME, DATA_PATH . self::CONF_PATH_NAME . '.bak.php'); - $result = file_put_contents(DATA_PATH . self::CONF_PATH_NAME, "default_filename = $default_filename; + if (!is_null($this->default_filename)) { + $this->data_default = self::parseFile($this->default_filename); } - return (bool)$result; } /** - * Parse un fichier de configuration - * @exception Minz_PermissionDeniedException si le CONF_PATH_NAME n'est pas accessible - * @exception Minz_BadConfigurationException si CONF_PATH_NAME mal formaté + * Return the value of the given param. + * + * @param $key the name of the param. + * @param $default default value to return if key does not exist. + * @return the value corresponding to the key. + * @throws Minz_ConfigurationParamException if the param does not exist */ - private static function parseFile () { - $ini_array = include(DATA_PATH . self::CONF_PATH_NAME); - - if (!is_array($ini_array)) { - throw new Minz_PermissionDeniedException ( - DATA_PATH . self::CONF_PATH_NAME, - Minz_Exception::ERROR + public function param($key, $default = null) { + if (isset($this->data[$key])) { + return $this->data[$key]; + } elseif (!is_null($default)) { + return $default; + } elseif (isset($this->data_default[$key])) { + return $this->data_default[$key]; + } else { + throw new Minz_ConfigurationParamException( + $key . ' param does not exist' ); } + } - // [general] est obligatoire - if (!isset ($ini_array['general'])) { - throw new Minz_BadConfigurationException ( - '[general]', - Minz_Exception::ERROR - ); - } - $general = $ini_array['general']; + /** + * A wrapper for param(). + */ + public function __get($key) { + return $this->param($key); + } - // salt est obligatoire - if (!isset ($general['salt'])) { - if (isset($general['sel_application'])) { //v0.6 - $general['salt'] = $general['sel_application']; - } else { - throw new Minz_BadConfigurationException ( - 'salt', - Minz_Exception::ERROR - ); - } + /** + * Set or remove a param. + * + * @param $key the param name to set. + * @param $value the value to set. If null, the key is removed from the configuration. + */ + public function _param($key, $value = null) { + if (isset($this->data[$key]) && is_null($value)) { + unset($this->data[$key]); + } else { + $this->data[$key] = $value; } - self::$salt = $general['salt']; - - if (isset ($general['environment'])) { - switch ($general['environment']) { - case 'silent': - self::$environment = Minz_Configuration::SILENT; - break; - case 'development': - self::$environment = Minz_Configuration::DEVELOPMENT; - break; - case 'production': - self::$environment = Minz_Configuration::PRODUCTION; - break; - default: - if ($general['environment'] >= 0 && - $general['environment'] <= 2) { - // fallback 0.7-beta - self::$environment = $general['environment']; - } else { - throw new Minz_BadConfigurationException ( - 'environment', - Minz_Exception::ERROR - ); - } - } + } - } - if (isset ($general['base_url'])) { - self::$base_url = $general['base_url']; - } + /** + * A wrapper for _param(). + */ + public function __set($key, $value) { + $this->_param($key, $value); + } - if (isset ($general['title'])) { - self::$title = $general['title']; - } - if (isset ($general['language'])) { - self::$language = $general['language']; - } - if (isset ($general['default_user'])) { - self::$default_user = $general['default_user']; - } - if (isset ($general['auth_type'])) { - self::_authType($general['auth_type']); - } - if (isset ($general['allow_anonymous'])) { - self::$allow_anonymous = ( - ((bool)($general['allow_anonymous'])) && - ($general['allow_anonymous'] !== 'no') - ); - } - if (isset ($general['allow_anonymous_refresh'])) { - self::$allow_anonymous_refresh = ( - ((bool)($general['allow_anonymous_refresh'])) && - ($general['allow_anonymous_refresh'] !== 'no') - ); - } - if (isset ($general['api_enabled'])) { - self::$api_enabled = ( - ((bool)($general['api_enabled'])) && - ($general['api_enabled'] !== 'no') - ); - } - if (isset ($general['unsafe_autologin_enabled'])) { - self::$unsafe_autologin_enabled = ( - ((bool)($general['unsafe_autologin_enabled'])) && - ($general['unsafe_autologin_enabled'] !== 'no') - ); - } + /** + * Save the current configuration in the configuration file. + */ + public function save() { + $back_filename = $this->config_filename . '.bak.php'; + @rename($this->config_filename, $back_filename); - if (isset($ini_array['limits'])) { - $limits = $ini_array['limits']; - if (isset($limits['cache_duration'])) { - $v = intval($limits['cache_duration']); - if ($v > 0) { - self::$limits['cache_duration'] = $v; - } - } - if (isset($limits['timeout'])) { - $v = intval($limits['timeout']); - if ($v > 0) { - self::$limits['timeout'] = $v; - } - } - if (isset($limits['max_inactivity'])) { - $v = intval($limits['max_inactivity']); - if ($v > 0) { - self::$limits['max_inactivity'] = $v; - } - } - if (isset($limits['max_feeds'])) { - $v = intval($limits['max_feeds']); - if ($v > 0 && $v < Minz_Configuration::MAX_SMALL_INT) { - self::$limits['max_feeds'] = $v; - } - } - if (isset($limits['max_categories'])) { - $v = intval($limits['max_categories']); - if ($v > 0 && $v < Minz_Configuration::MAX_SMALL_INT) { - self::$limits['max_categories'] = $v; - } - } + if (file_put_contents($this->config_filename, + "data, true) . ';', + LOCK_EX) === false) { + return false; } - // Base de données - if (isset ($ini_array['db'])) { - $db = $ini_array['db']; - if (empty($db['type'])) { - throw new Minz_BadConfigurationException ( - 'type', - Minz_Exception::ERROR - ); - } - switch ($db['type']) { - case 'mysql': - if (empty($db['host'])) { - throw new Minz_BadConfigurationException ( - 'host', - Minz_Exception::ERROR - ); - } - if (empty($db['user'])) { - throw new Minz_BadConfigurationException ( - 'user', - Minz_Exception::ERROR - ); - } - if (!isset($db['password'])) { - throw new Minz_BadConfigurationException ( - 'password', - Minz_Exception::ERROR - ); - } - if (empty($db['base'])) { - throw new Minz_BadConfigurationException ( - 'base', - Minz_Exception::ERROR - ); - } - self::$db['host'] = $db['host']; - self::$db['user'] = $db['user']; - self::$db['password'] = $db['password']; - self::$db['base'] = $db['base']; - if (isset($db['prefix'])) { - self::$db['prefix'] = $db['prefix']; - } - break; - case 'sqlite': - self::$db['host'] = ''; - self::$db['user'] = ''; - self::$db['password'] = ''; - self::$db['base'] = ''; - self::$db['prefix'] = ''; - break; - default: - throw new Minz_BadConfigurationException ( - 'type', - Minz_Exception::ERROR - ); - break; - } - self::$db['type'] = $db['type']; + // Clear PHP 5.5+ cache for include + if (function_exists('opcache_invalidate')) { + opcache_invalidate($this->config_filename); } - } - private static function setReporting() { - switch (self::$environment) { - case self::PRODUCTION: - error_reporting(E_ALL); - ini_set('display_errors','Off'); - ini_set('log_errors', 'On'); - break; - case self::DEVELOPMENT: - error_reporting(E_ALL); - ini_set('display_errors','On'); - ini_set('log_errors', 'On'); - break; - case self::SILENT: - error_reporting(0); - break; - } + return true; } } diff --git a/lib/Minz/ConfigurationException.php b/lib/Minz/ConfigurationException.php new file mode 100644 index 000000000..f294c3341 --- /dev/null +++ b/lib/Minz/ConfigurationException.php @@ -0,0 +1,8 @@ + en fonction de l'environment */ private static function processLogs ($logs) { - $env = Minz_Configuration::environment (); + $conf = Minz_Configuration::get('system'); + $env = $conf->general['environment']; $logs_ok = array (); $error = array (); $warning = array (); @@ -98,10 +99,10 @@ class Minz_Error { $notice = $logs['notice']; } - if ($env == Minz_Configuration::PRODUCTION) { + if ($env == 'production') { $logs_ok = $error; } - if ($env == Minz_Configuration::DEVELOPMENT) { + if ($env == 'development') { $logs_ok = array_merge ($error, $warning, $notice); } diff --git a/lib/Minz/FrontController.php b/lib/Minz/FrontController.php index 3dac1e438..974cf4260 100644 --- a/lib/Minz/FrontController.php +++ b/lib/Minz/FrontController.php @@ -31,9 +31,12 @@ class Minz_FrontController { */ public function __construct () { try { - Minz_Configuration::init (); + Minz_Configuration::register('system', + DATA_PATH . '/config.php', + DATA_PATH . '/config.default.php'); + $this->setReporting(); - Minz_Request::init (); + Minz_Request::init(); $url = $this->buildUrl(); $url['params'] = array_merge ( @@ -110,4 +113,23 @@ class Minz_FrontController { } exit ('### Application problem ###
    '."\n".$txt); } + + private function setReporting() { + $conf = Minz_Configuration::get('system'); + switch($conf->general['environment']) { + case 'production': + error_reporting(E_ALL); + ini_set('display_errors','Off'); + ini_set('log_errors', 'On'); + break; + case 'development': + error_reporting(E_ALL); + ini_set('display_errors','On'); + ini_set('log_errors', 'On'); + break; + case 'silent': + error_reporting(0); + break; + } + } } diff --git a/lib/Minz/Log.php b/lib/Minz/Log.php index d19edc1dc..2063efe7e 100644 --- a/lib/Minz/Log.php +++ b/lib/Minz/Log.php @@ -31,10 +31,15 @@ class Minz_Log { * @param $file_name fichier de log */ public static function record ($information, $level, $file_name = null) { - $env = Minz_Configuration::environment (); + try { + $conf = Minz_Configuration::get('system'); + $env = $conf->general['environment']; + } catch (Minz_ConfigurationException $e) { + $env = 'production'; + } - if (! ($env === Minz_Configuration::SILENT - || ($env === Minz_Configuration::PRODUCTION + if (! ($env === 'silent' + || ($env === 'production' && ($level >= Minz_Log::NOTICE)))) { if ($file_name === null) { $file_name = join_path(USERS_PATH, Minz_Session::param('currentUser', '_'), 'log.txt'); diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index 118d89ad2..ac7a1bed7 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -44,7 +44,8 @@ class Minz_ModelPdo { return; } - $db = Minz_Configuration::dataBase(); + $conf = Minz_Configuration::get('system'); + $db = $conf->db; if ($currentUser === null) { $currentUser = Minz_Session::param('currentUser', '_'); diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 4b97a3caf..5f2f6a858 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -96,7 +96,8 @@ class Minz_Request { * @return la base de l'url */ public static function getBaseUrl() { - $defaultBaseUrl = Minz_Configuration::baseUrl(); + $conf = Minz_Configuration::get('system'); + $defaultBaseUrl = $conf->general['base_url']; if (!empty($defaultBaseUrl)) { return $defaultBaseUrl; } elseif (isset($_SERVER['REQUEST_URI'])) { diff --git a/lib/Minz/Translate.php b/lib/Minz/Translate.php index e7efb8665..7525e95cc 100644 --- a/lib/Minz/Translate.php +++ b/lib/Minz/Translate.php @@ -28,7 +28,8 @@ class Minz_Translate { * Load $lang_name and $lang_path based on configuration and selected language. */ public static function init() { - $l = Minz_Configuration::language(); + $conf = Minz_Configuration::get('system'); + $l = $conf->general['language']; self::$lang_name = Minz_Session::param('language', $l); self::$lang_path = APP_PATH . '/i18n/' . self::$lang_name . '/'; } diff --git a/lib/Minz/View.php b/lib/Minz/View.php index b40448491..24ad630d0 100644 --- a/lib/Minz/View.php +++ b/lib/Minz/View.php @@ -28,7 +28,9 @@ class Minz_View { public function __construct () { $this->change_view(Minz_Request::controllerName(), Minz_Request::actionName()); - self::$title = Minz_Configuration::title (); + + $conf = Minz_Configuration::get('system'); + self::$title = $conf->general['title']; } /** diff --git a/p/i/index.php b/p/i/index.php index 009d56bc3..d3fc0b37c 100755 --- a/p/i/index.php +++ b/p/i/index.php @@ -33,7 +33,7 @@ if (file_exists(DATA_PATH . '/do-install.txt')) { $currentUser = Minz_Session::param('currentUser', ''); $dateLastModification = $currentUser === '' ? time() : max( @filemtime(join_path(USERS_PATH, $currentUser, 'log.txt')), - @filemtime(join_path(DATA_PATH . 'config.php')) + @filemtime(join_path(DATA_PATH, 'config.php')) ); if (httpConditional($dateLastModification, 0, 0, false, PHP_COMPRESSION, true)) { exit(); //No need to send anything -- cgit v1.2.3 From d27efeec04c7c41cf0f52bc7f89879e66f2e44a9 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 6 Jan 2015 17:38:31 +0100 Subject: Fix Controllers to use the correct config system See https://github.com/FreshRSS/FreshRSS/issues/730 --- app/Controllers/feedController.php | 5 +++-- app/Controllers/importExportController.php | 4 ++-- app/Controllers/indexController.php | 9 ++++++--- app/Controllers/javascriptController.php | 3 ++- app/Controllers/userController.php | 10 ++++++---- lib/lib_rss.php | 3 ++- 6 files changed, 21 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 92a1e3bf8..df1e559bc 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -18,8 +18,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $token_param = Minz_Request::param('token', ''); $token_is_ok = ($token != '' && $token == $token_param); $action = Minz_Request::actionName(); + $allow_anonymous_refresh = FreshRSS_Context::$system_conf->general['allow_anonymous_refresh']; if ($action !== 'actualize' || - !(Minz_Configuration::allowAnonymousRefresh() || $token_is_ok)) { + !($allow_anonymous_refresh || $token_is_ok)) { Minz_Error::error(403); } } @@ -65,7 +66,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { 'params' => array(), ); - $limits = Minz_Configuration::limits(); + $limits = FreshRSS_Context::$system_conf->limits; $this->view->feeds = $feedDAO->listFeeds(); if (count($this->view->feeds) >= $limits['max_feeds']) { Minz_Request::bad(_t('feedback.sub.feed.over_max', $limits['max_feeds']), diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 334f33d6a..4ce24719e 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -174,7 +174,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $nb_feeds = count($this->feedDAO->listFeeds()); $nb_cats = count($this->catDAO->listCategories(false)); - $limits = Minz_Configuration::limits(); + $limits = FreshRSS_Context::$system_conf->limits; foreach ($opml_elements as $elt) { $is_error = false; @@ -323,7 +323,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $article_to_feed = array(); $nb_feeds = count($this->feedDAO->listFeeds()); - $limits = Minz_Configuration::limits(); + $limits = FreshRSS_Context::$system_conf->limits; // First, we check feeds of articles are in DB (and add them if needed). foreach ($article_object['items'] as $item) { diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 14f3f4f4b..d948504cc 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -20,7 +20,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { * This action displays the normal view of FreshRSS. */ public function normalAction() { - if (!FreshRSS_Auth::hasAccess() && !Minz_Configuration::allowAnonymous()) { + $allow_anonymous = FreshRSS_Context::$system_conf->general['allow_anonymous']; + if (!FreshRSS_Auth::hasAccess() && !$allow_anonymous) { Minz_Request::forward(array('c' => 'auth', 'a' => 'login')); return; } @@ -82,7 +83,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { * This action displays the global view of FreshRSS. */ public function globalAction() { - if (!FreshRSS_Auth::hasAccess() && !Minz_Configuration::allowAnonymous()) { + $allow_anonymous = FreshRSS_Context::$system_conf->general['allow_anonymous']; + if (!FreshRSS_Auth::hasAccess() && !$allow_anonymous) { Minz_Request::forward(array('c' => 'auth', 'a' => 'login')); return; } @@ -109,13 +111,14 @@ class FreshRSS_index_Controller extends Minz_ActionController { * This action displays the RSS feed of FreshRSS. */ public function rssAction() { + $allow_anonymous = FreshRSS_Context::$system_conf->general['allow_anonymous']; $token = FreshRSS_Context::$user_conf->token; $token_param = Minz_Request::param('token', ''); $token_is_ok = ($token != '' && $token === $token_param); // Check if user has access. if (!FreshRSS_Auth::hasAccess() && - !Minz_Configuration::allowAnonymous() && + !$allow_anonymous && !$token_is_ok) { Minz_Error::error(403); } diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php index b178801d4..dd9aa6189 100755 --- a/app/Controllers/javascriptController.php +++ b/app/Controllers/javascriptController.php @@ -28,11 +28,12 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { $user = isset($_GET['user']) ? $_GET['user'] : ''; if (ctype_alnum($user)) { try { + $salt = FreshRSS_Context::$system_conf->general['salt']; $conf = new FreshRSS_Configuration($user); $s = $conf->passwordHash; if (strlen($s) >= 60) { $this->view->salt1 = substr($s, 0, 29); //CRYPT_BLOWFISH Salt: "$2a$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". - $this->view->nonce = sha1(Minz_Configuration::salt() . uniqid(mt_rand(), true)); + $this->view->nonce = sha1($salt . uniqid(mt_rand(), true)); Minz_Session::_param('nonce', $this->view->nonce); return; //Success } diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 58181bfb0..be2ae943e 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -105,7 +105,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { public function createAction() { if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { - $db = Minz_Configuration::dataBase(); + $db = FreshRSS_Context::$system_conf->db; require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); $new_user_language = Minz_Request::param('new_user_language', FreshRSS_Context::$user_conf->language); @@ -118,7 +118,8 @@ class FreshRSS_user_Controller extends Minz_ActionController { $ok = ($new_user_name != '') && ctype_alnum($new_user_name); if ($ok) { - $ok &= (strcasecmp($new_user_name, Minz_Configuration::defaultUser()) !== 0); //It is forbidden to alter the default user + $default_user = FreshRSS_Context::$system_conf->general['default_user']; + $ok &= (strcasecmp($new_user_name, $default_user) !== 0); //It is forbidden to alter the default user $ok &= !in_array(strtoupper($new_user_name), array_map('strtoupper', listUsers())); //Not an existing user, case-insensitive @@ -179,7 +180,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { public function deleteAction() { if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { - $db = Minz_Configuration::dataBase(); + $db = FreshRSS_Context::$system_conf->db; require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); $username = Minz_Request::param('username'); @@ -187,7 +188,8 @@ class FreshRSS_user_Controller extends Minz_ActionController { $user_data = join_path(DATA_PATH, 'users', $username); if ($ok) { - $ok &= (strcasecmp($username, Minz_Configuration::defaultUser()) !== 0); //It is forbidden to delete the default user + $default_user = FreshRSS_Context::$system_conf->general['default_user']; + $ok &= (strcasecmp($username, $default_user) !== 0); //It is forbidden to delete the default user } if ($ok) { $ok &= is_dir($user_data); diff --git a/lib/lib_rss.php b/lib/lib_rss.php index d450ec858..3a929631e 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -119,7 +119,8 @@ function html_only_entity_decode($text) { } function customSimplePie() { - $limits = Minz_Configuration::limits(); + $system_conf = Minz_Configuration::get('system'); + $limits = $system_conf->limits; $simplePie = new SimplePie(); $simplePie->set_useragent(_t('gen.freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION); $simplePie->set_cache_location(CACHE_PATH); -- cgit v1.2.3 From d3a93ea2905ae50a2365d293f9f3ef3e51bf5f30 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 6 Jan 2015 18:53:36 +0100 Subject: BREAKING FEATURE: Remove general in config General attribute has been removed from system config. Now subattributes (e.g. environment, salt, title, etc.) are directly accessible. YOU HAVE TO FIX YOUR ./data/config.php file! - Remove the general array - Values inside this array must be kept - To see what it must look like, please have a look to ./data/config.default.php (but keep your values!!). See https://github.com/FreshRSS/FreshRSS/issues/730 --- app/Controllers/authController.php | 28 +++++++++++++--------------- app/Controllers/feedController.php | 2 +- app/Controllers/indexController.php | 6 +++--- app/Controllers/javascriptController.php | 2 +- app/Controllers/userController.php | 4 ++-- app/FreshRSS.php | 2 +- app/Models/Auth.php | 14 +++++++------- app/Models/Feed.php | 4 ++-- app/layout/header.phtml | 6 ++---- app/layout/layout.phtml | 2 +- app/views/helpers/javascript_vars.phtml | 3 +-- data/config.default.php | 24 +++++++++++------------- lib/Minz/Error.php | 2 +- lib/Minz/FrontController.php | 2 +- lib/Minz/Log.php | 2 +- lib/Minz/Request.php | 2 +- lib/Minz/Translate.php | 2 +- lib/Minz/View.php | 2 +- 18 files changed, 51 insertions(+), 58 deletions(-) (limited to 'lib') diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php index 3a1ad4605..4ae9ff7fb 100644 --- a/app/Controllers/authController.php +++ b/app/Controllers/authController.php @@ -27,7 +27,6 @@ class FreshRSS_auth_Controller extends Minz_ActionController { if (Minz_Request::isPost()) { $ok = true; - $general = FreshRSS_Context::$system_conf->general; $current_token = FreshRSS_Context::$user_conf->token; $token = Minz_Request::param('token', $current_token); FreshRSS_Context::$user_conf->_token($token); @@ -40,20 +39,19 @@ class FreshRSS_auth_Controller extends Minz_ActionController { $auth_type = Minz_Request::param('auth_type', 'none'); $unsafe_autologin = Minz_Request::param('unsafe_autologin', false); $api_enabled = Minz_Request::param('api_enabled', false); - if ($anon != $general['allow_anonymous'] || - $auth_type != $general['auth_type'] || - $anon_refresh != $general['allow_anonymous_refresh'] || - $unsafe_autologin != $general['unsafe_autologin_enabled'] || - $api_enabled != $general['api_enabled']) { + if ($anon != FreshRSS_Context::$system_conf->allow_anonymous || + $auth_type != FreshRSS_Context::$system_conf->auth_type || + $anon_refresh != FreshRSS_Context::$system_conf->allow_anonymous_refresh || + $unsafe_autologin != FreshRSS_Context::$system_conf->unsafe_autologin_enabled || + $api_enabled != FreshRSS_Context::$system_conf->api_enabled) { // TODO: test values from form - $general['auth_type'] = $auth_type; - $general['allow_anonymous'] = $anon; - $general['allow_anonymous_refresh'] = $anon_refresh; - $general['unsafe_autologin_enabled'] = $unsafe_autologin; - $general['api_enabled'] = $api_enabled; + FreshRSS_Context::$system_conf->auth_type = $auth_type; + FreshRSS_Context::$system_conf->allow_anonymous = $anon; + FreshRSS_Context::$system_conf->allow_anonymous_refresh = $anon_refresh; + FreshRSS_Context::$system_conf->unsafe_autologin_enabled = $unsafe_autologin; + FreshRSS_Context::$system_conf->api_enabled = $api_enabled; - $system_conf->general = $general; $ok &= $system_conf->save(); } @@ -80,7 +78,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController { Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } - $auth_type = FreshRSS_Context::$system_conf->general['auth_type']; + $auth_type = FreshRSS_Context::$system_conf->auth_type; switch ($auth_type) { case 'form': Minz_Request::forward(array('c' => 'auth', 'a' => 'formLogin')); @@ -160,7 +158,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController { Minz_Request::bad(_t('feedback.auth.login.invalid'), array('c' => 'auth', 'a' => 'login')); } - } elseif (FreshRSS_Context::$system_conf->general['unsafe_autologin_enabled']) { + } elseif (FreshRSS_Context::$system_conf->unsafe_autologin_enabled) { $username = Minz_Request::param('u', ''); $password = Minz_Request::param('p', ''); Minz_Request::_param('p'); @@ -301,7 +299,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController { $this->view->no_form = false; // Enable changement of auth only if Persona! - if (FreshRSS_Context::$system_conf->general['auth_type'] != 'persona') { + if (FreshRSS_Context::$system_conf->auth_type != 'persona') { $this->view->message = array( 'status' => 'bad', 'title' => _t('gen.short.damn'), diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index df1e559bc..c22669361 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -18,7 +18,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $token_param = Minz_Request::param('token', ''); $token_is_ok = ($token != '' && $token == $token_param); $action = Minz_Request::actionName(); - $allow_anonymous_refresh = FreshRSS_Context::$system_conf->general['allow_anonymous_refresh']; + $allow_anonymous_refresh = FreshRSS_Context::$system_conf->allow_anonymous_refresh; if ($action !== 'actualize' || !($allow_anonymous_refresh || $token_is_ok)) { Minz_Error::error(403); diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index d948504cc..c53d3223e 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -20,7 +20,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { * This action displays the normal view of FreshRSS. */ public function normalAction() { - $allow_anonymous = FreshRSS_Context::$system_conf->general['allow_anonymous']; + $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous; if (!FreshRSS_Auth::hasAccess() && !$allow_anonymous) { Minz_Request::forward(array('c' => 'auth', 'a' => 'login')); return; @@ -83,7 +83,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { * This action displays the global view of FreshRSS. */ public function globalAction() { - $allow_anonymous = FreshRSS_Context::$system_conf->general['allow_anonymous']; + $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous; if (!FreshRSS_Auth::hasAccess() && !$allow_anonymous) { Minz_Request::forward(array('c' => 'auth', 'a' => 'login')); return; @@ -111,7 +111,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { * This action displays the RSS feed of FreshRSS. */ public function rssAction() { - $allow_anonymous = FreshRSS_Context::$system_conf->general['allow_anonymous']; + $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous; $token = FreshRSS_Context::$user_conf->token; $token_param = Minz_Request::param('token', ''); $token_is_ok = ($token != '' && $token === $token_param); diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php index dd9aa6189..acd3fef69 100755 --- a/app/Controllers/javascriptController.php +++ b/app/Controllers/javascriptController.php @@ -28,7 +28,7 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { $user = isset($_GET['user']) ? $_GET['user'] : ''; if (ctype_alnum($user)) { try { - $salt = FreshRSS_Context::$system_conf->general['salt']; + $salt = FreshRSS_Context::$system_conf->salt; $conf = new FreshRSS_Configuration($user); $s = $conf->passwordHash; if (strlen($s) >= 60) { diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index be2ae943e..bfc2dfb3b 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -118,7 +118,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $ok = ($new_user_name != '') && ctype_alnum($new_user_name); if ($ok) { - $default_user = FreshRSS_Context::$system_conf->general['default_user']; + $default_user = FreshRSS_Context::$system_conf->default_user; $ok &= (strcasecmp($new_user_name, $default_user) !== 0); //It is forbidden to alter the default user $ok &= !in_array(strtoupper($new_user_name), array_map('strtoupper', listUsers())); //Not an existing user, case-insensitive @@ -188,7 +188,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $user_data = join_path(DATA_PATH, 'users', $username); if ($ok) { - $default_user = FreshRSS_Context::$system_conf->general['default_user']; + $default_user = FreshRSS_Context::$system_conf->default_user; $ok &= (strcasecmp($username, $default_user) !== 0); //It is forbidden to delete the default user } if ($ok) { diff --git a/app/FreshRSS.php b/app/FreshRSS.php index b22bfdb4b..a53174394 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -62,7 +62,7 @@ class FreshRSS extends Minz_FrontController { Minz_View::appendScript(Minz_Url::display('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); Minz_View::appendScript(Minz_Url::display('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js'))); - if (FreshRSS_Context::$system_conf->general['auth_type'] === 'persona') { + if (FreshRSS_Context::$system_conf->auth_type === 'persona') { // TODO move it in a plugin // Needed for login AND logout with Persona. Minz_View::appendScript('https://login.persona.org/include.js'); diff --git a/app/Models/Auth.php b/app/Models/Auth.php index 84b4e3721..05ec61d0e 100644 --- a/app/Models/Auth.php +++ b/app/Models/Auth.php @@ -17,7 +17,7 @@ class FreshRSS_Auth { $current_user = Minz_Session::param('currentUser', ''); if ($current_user === '') { $conf = Minz_Configuration::get('system'); - $current_user = $conf->general['default_user']; + $current_user = $conf->default_user; Minz_Session::_param('currentUser', $current_user); } @@ -42,7 +42,7 @@ class FreshRSS_Auth { */ private static function accessControl() { $conf = Minz_Configuration::get('system'); - $auth_type = $conf->general['auth_type']; + $auth_type = $conf->auth_type; switch ($auth_type) { case 'form': $credentials = FreshRSS_FormAuth::getCredentialsFromCookie(); @@ -84,7 +84,7 @@ class FreshRSS_Auth { public static function giveAccess() { $user_conf = Minz_Configuration::get('user'); $system_conf = Minz_Configuration::get('system'); - $auth_type = $system_conf->general['auth_type']; + $auth_type = $system_conf->auth_type; switch ($auth_type) { case 'form': @@ -115,7 +115,7 @@ class FreshRSS_Auth { */ public static function hasAccess($scope = 'general') { $conf = Minz_Configuration::get('system'); - $default_user = $conf->general['default_user']; + $default_user = $conf->default_user; $ok = self::$login_ok; switch ($scope) { case 'general': @@ -136,9 +136,9 @@ class FreshRSS_Auth { Minz_Session::_param('loginOk'); self::$login_ok = false; $conf = Minz_Configuration::get('system'); - Minz_Session::_param('currentUser', $conf->general['default_user']); + Minz_Session::_param('currentUser', $conf->default_user); - switch ($conf->general['auth_type']) { + switch ($conf->auth_type) { case 'form': Minz_Session::_param('passwordHash'); FreshRSS_FormAuth::deleteCookie(); @@ -160,7 +160,7 @@ class FreshRSS_Auth { */ public static function accessNeedLogin() { $conf = Minz_Configuration::get('system'); - $auth_type = $conf->general['auth_type']; + $auth_type = $conf->auth_type; return $auth_type === 'form' || $auth_type === 'persona'; } } diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 071eafdf6..86cbb783e 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -39,9 +39,9 @@ class FreshRSS_Feed extends Minz_Model { } public function hash() { - $conf = Minz_Configuration::get('system'); if ($this->hash === null) { - $this->hash = hash('crc32b', $conf->general['salt'] . $this->url); + $salt = FreshRSS_Context::$system_conf->salt; + $this->hash = hash('crc32b', $salt . $this->url); } return $this->hash; } diff --git a/app/layout/header.phtml b/app/layout/header.phtml index 2f16b5f63..97e24a1d9 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -1,7 +1,5 @@
     
    conf->topline_read ? ' checked="checked"' : ''; ?> /> conf->topline_favorite ? ' checked="checked"' : ''; ?> /> conf->topline_date ? ' checked="checked"' : ''; ?> /> conf->topline_link ? ' checked="checked"' : ''; ?> />
    conf->bottomline_read ? ' checked="checked"' : ''; ?> /> conf->bottomline_favorite ? ' checked="checked"' : ''; ?> /> conf->bottomline_sharing ? ' checked="checked"' : ''; ?> />