From ccb56bcbf3ef69228ae4147a76cf3059f519bbf3 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 11 Sep 2016 11:24:32 +0200 Subject: Simplify SQL in statistics Reduce the use of product-specific date functions. Improve performances. Remove redundant functions. --- app/Controllers/statsController.php | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php index 4a597ae7d..5d1dee72c 100644 --- a/app/Controllers/statsController.php +++ b/app/Controllers/statsController.php @@ -18,6 +18,27 @@ class FreshRSS_stats_Controller extends Minz_ActionController { Minz_View::prependTitle(_t('admin.stats.title') . ' · '); } + private function convertToSerie($data) { + $serie = array(); + + foreach ($data as $key => $value) { + $serie[] = array($key, $value); + } + + return $serie; + } + + private function convertToPieSerie($data) { + $serie = array(); + + foreach ($data as $value) { + $value['data'] = array(array(0, (int) $value['data'])); + $serie[] = $value; + } + + return $serie; + } + /** * This action handles the statistic main page. * @@ -33,10 +54,11 @@ class FreshRSS_stats_Controller extends Minz_ActionController { $statsDAO = FreshRSS_Factory::createStatsDAO(); Minz_View::appendScript(Minz_Url::display('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js'))); $this->view->repartition = $statsDAO->calculateEntryRepartition(); - $this->view->count = $statsDAO->calculateEntryCount(); - $this->view->average = $statsDAO->calculateEntryAverage(); - $this->view->feedByCategory = $statsDAO->calculateFeedByCategory(); - $this->view->entryByCategory = $statsDAO->calculateEntryByCategory(); + $entryCount = $statsDAO->calculateEntryCount(); + $this->view->count = $this->convertToSerie($entryCount); + $this->view->average = round(array_sum(array_values($entryCount)) / count($entryCount), 2); + $this->view->feedByCategory = $this->convertToPieSerie($statsDAO->calculateFeedByCategory()); + $this->view->entryByCategory = $this->convertToPieSerie($statsDAO->calculateEntryByCategory()); $this->view->topFeed = $statsDAO->calculateTopFeed(); } @@ -118,11 +140,11 @@ class FreshRSS_stats_Controller extends Minz_ActionController { $this->view->days = $statsDAO->getDays(); $this->view->months = $statsDAO->getMonths(); $this->view->repartition = $statsDAO->calculateEntryRepartitionPerFeed($id); - $this->view->repartitionHour = $statsDAO->calculateEntryRepartitionPerFeedPerHour($id); + $this->view->repartitionHour = $this->convertToSerie($statsDAO->calculateEntryRepartitionPerFeedPerHour($id)); $this->view->averageHour = $statsDAO->calculateEntryAveragePerFeedPerHour($id); - $this->view->repartitionDayOfWeek = $statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id); + $this->view->repartitionDayOfWeek = $this->convertToSerie($statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id)); $this->view->averageDayOfWeek = $statsDAO->calculateEntryAveragePerFeedPerDayOfWeek($id); - $this->view->repartitionMonth = $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id); + $this->view->repartitionMonth = $this->convertToSerie($statsDAO->calculateEntryRepartitionPerFeedPerMonth($id)); $this->view->averageMonth = $statsDAO->calculateEntryAveragePerFeedPerMonth($id); } } -- cgit v1.2.3 From 2a5aa34ad2796df00fa09de41361c20764ccdfa8 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 11 Sep 2016 15:06:33 +0200 Subject: Better control of number of entries per page or RSS feed https://github.com/FreshRSS/FreshRSS/issues/1249 * Since X hours: `https://freshrss.example/i/?a=rss&hours=3` * Explicit number: `https://freshrss.example/i/?a=rss&nb=10` * Limited by `min_posts_per_rss` and `max_posts_per_rss` in user config --- CHANGELOG.md | 4 ++++ app/Controllers/indexController.php | 39 +++++++++++++++++++++++++++++++------ app/Models/Context.php | 1 + app/layout/layout.phtml | 3 +++ app/layout/nav_menu.phtml | 3 +++ app/views/auth/index.phtml | 2 +- data/users/_/config.default.php | 3 +++ 7 files changed, 48 insertions(+), 7 deletions(-) (limited to 'app/Controllers') diff --git a/CHANGELOG.md b/CHANGELOG.md index b3c46bdb6..f82f68fff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ ## 2016-XX-XX FreshRSS 1.6.0-dev * Features + * Better control of number of entries per page or RSS feed [#1249](https://github.com/FreshRSS/FreshRSS/issues/1249) + * Since X hours: `https://freshrss.example/i/?a=rss&hours=3` + * Explicit number: `https://freshrss.example/i/?a=rss&nb=10` + * Limited by `min_posts_per_rss` and `max_posts_per_rss` in user config * Support custom ports `localhost:3306` for database servers [#1241](https://github.com/FreshRSS/FreshRSS/issues/1241) * Security * Prevent `` attacks with `window.opener` [#1245](https://github.com/FreshRSS/FreshRSS/issues/1245) diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 2332d225d..4e2717920 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -34,7 +34,9 @@ class FreshRSS_index_Controller extends Minz_ActionController { $this->view->callbackBeforeContent = function($view) { try { + FreshRSS_Context::$number++; //+1 for pagination $entries = FreshRSS_index_Controller::listEntriesByContext(); + FreshRSS_Context::$number--; $nb_entries = count($entries); if ($nb_entries > FreshRSS_Context::$number) { @@ -154,6 +156,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { * - order (default: conf->sort_order) * - nb (default: conf->posts_per_page) * - next (default: empty string) + * - hours (default: 0) */ private function updateContext() { // Update number of read / unread variables. @@ -180,10 +183,14 @@ class FreshRSS_index_Controller extends Minz_ActionController { FreshRSS_Context::$order = Minz_Request::param( 'order', FreshRSS_Context::$user_conf->sort_order ); - FreshRSS_Context::$number = Minz_Request::param( - 'nb', FreshRSS_Context::$user_conf->posts_per_page - ); + FreshRSS_Context::$number = intval(Minz_Request::param('nb', FreshRSS_Context::$user_conf->posts_per_page)); + if (FreshRSS_Context::$number > FreshRSS_Context::$user_conf->max_posts_per_rss) { + FreshRSS_Context::$number = max( + FreshRSS_Context::$user_conf->max_posts_per_rss, + FreshRSS_Context::$user_conf->posts_per_page); + } FreshRSS_Context::$first_id = Minz_Request::param('next', ''); + FreshRSS_Context::$sinceHours = intval(Minz_Request::param('hours', 0)); } /** @@ -201,11 +208,31 @@ class FreshRSS_index_Controller extends Minz_ActionController { $id = ''; } - return $entryDAO->listWhere( + $limit = FreshRSS_Context::$number; + + $date_min = 0; + if (FreshRSS_Context::$sinceHours) { + $date_min = time() - (FreshRSS_Context::$sinceHours * 3600); + $limit = FreshRSS_Context::$user_conf->max_posts_per_rss; + } + + $entries = $entryDAO->listWhere( $type, $id, FreshRSS_Context::$state, FreshRSS_Context::$order, - FreshRSS_Context::$number + 1, FreshRSS_Context::$first_id, - FreshRSS_Context::$search + $limit, FreshRSS_Context::$first_id, + FreshRSS_Context::$search, $date_min ); + + if (FreshRSS_Context::$sinceHours && (count($entries) < FreshRSS_Context::$user_conf->min_posts_per_rss)) { + $date_min = 0; + $limit = FreshRSS_Context::$user_conf->min_posts_per_rss; + $entries = $entryDAO->listWhere( + $type, $id, FreshRSS_Context::$state, FreshRSS_Context::$order, + $limit, FreshRSS_Context::$first_id, + FreshRSS_Context::$search, $date_min + ); + } + + return $entries; } /** diff --git a/app/Models/Context.php b/app/Models/Context.php index 2a58bd4ba..33e65a619 100644 --- a/app/Models/Context.php +++ b/app/Models/Context.php @@ -35,6 +35,7 @@ class FreshRSS_Context { public static $first_id = ''; public static $next_id = ''; public static $id_max = ''; + public static $sinceHours = 0; /** * Initialize the context. diff --git a/app/layout/layout.phtml b/app/layout/layout.phtml index 4d9ad5458..1f11e0af1 100644 --- a/app/layout/layout.phtml +++ b/app/layout/layout.phtml @@ -42,6 +42,9 @@ } if (isset($this->rss_title)) { $url_rss = $url_base; $url_rss['a'] = 'rss'; + if (FreshRSS_Context::$user_conf->since_hours_posts_per_rss) { + $url_rss['params']['hours'] = FreshRSS_Context::$user_conf->since_hours_posts_per_rss; + } ?> allow_robots) { ?> diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index d77c1abf9..c02232242 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -151,6 +151,9 @@ if (FreshRSS_Context::$user_conf->token) { $url_output['params']['token'] = FreshRSS_Context::$user_conf->token; } + if (FreshRSS_Context::$user_conf->since_hours_posts_per_rss) { + $url_output['params']['hours'] = FreshRSS_Context::$user_conf->since_hours_posts_per_rss; + } ?> diff --git a/app/views/auth/index.phtml b/app/views/auth/index.phtml index 74e692ec5..7195bab5d 100644 --- a/app/views/auth/index.phtml +++ b/app/views/auth/index.phtml @@ -60,7 +60,7 @@ data-leave-validation=""/> - array('output' => 'rss', 'token' => $token)), 'html', true); ?> + array('output' => 'rss', 'token' => $token, 'hours' => FreshRSS_Context::$user_conf->since_hours_posts_per_rss)), 'html', true); ?> diff --git a/data/users/_/config.default.php b/data/users/_/config.default.php index 4a3403453..2c56d5f45 100644 --- a/data/users/_/config.default.php +++ b/data/users/_/config.default.php @@ -9,6 +9,9 @@ return array ( 'passwordHash' => '', 'apiPasswordHash' => '', 'posts_per_page' => 20, + 'since_hours_posts_per_rss' => 168, + 'min_posts_per_rss' => 2, + 'max_posts_per_rss' => 400, 'view_mode' => 'normal', 'default_view' => 'adaptive', 'default_state' => FreshRSS_Entry::STATE_NOT_READ, -- cgit v1.2.3 From 44f22ab8b4c46befab98440f69a05725928bed75 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 24 Sep 2016 17:36:33 +0200 Subject: API move feed to another category https://github.com/jangernert/FeedReader/issues/59 https://github.com/FreshRSS/FreshRSS/issues/443 --- app/Controllers/feedController.php | 14 +------------- app/Models/FeedDAO.php | 13 +++++++++++++ p/api/greader.php | 7 +++++-- 3 files changed, 19 insertions(+), 15 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index ffda1450d..c64b67a80 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -511,21 +511,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feed_id = Minz_Request::param('f_id'); $cat_id = Minz_Request::param('c_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(); - } - $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))) { + if ($feedDAO->moveFeed($feed_id, $cat_id)) { // TODO: return something useful } else { Minz_Log::warning('Cannot move feed `' . $feed_id . '` ' . diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 475d39286..2fd2c6194 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -136,6 +136,19 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } + public function moveFeed($feed_id, $cat_id) { + if ($cat_id <= 0) { + // If category was not given get the default one. + $catDAO = new FreshRSS_CategoryDAO(); + $catDAO->checkDefault(); + $def_cat = $catDAO->getDefault(); + $cat_id = $def_cat->id(); + } + $feed = $this->searchById($feed_id); + return $feed && ($feed->category() == $cat_id || + $this->updateFeed($feed_id, array('category' => $cat_id))); + } + public function deleteFeed($id) { $sql = 'DELETE FROM `' . $this->prefix . 'feed` WHERE id=?'; $stm = $this->bd->prepare($sql); diff --git a/p/api/greader.php b/p/api/greader.php index 3da1156c2..426f3fe44 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -337,14 +337,17 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = ' break; case 'edit': if ($feedId > 0) { - //TODO + if ($feedDAO->moveFeed($feed_id, $cat_id)) { + exit('OK'); + } else { + badRequest(); + } } break; } } } notImplemented(); - exit('OK'); } function unreadCount() { //http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#unread-count -- cgit v1.2.3 From d6b4186040011aad16a173a1ceaa5a5084025470 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 24 Sep 2016 19:26:40 +0200 Subject: API implement delete feed + refactor move feed --- app/Controllers/feedController.php | 45 ++++++++++++++++++++++++++++--------- app/Models/FeedDAO.php | 13 ----------- p/api/greader.php | 46 +++++++++++++++++--------------------- 3 files changed, 54 insertions(+), 50 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index c64b67a80..0ff27e0da 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -492,6 +492,23 @@ class FreshRSS_feed_Controller extends Minz_ActionController { return $updated_feeds; } + public static function moveFeed($feed_id, $cat_id) { + if ($feed_id <= 0) { + return false; + } + if ($cat_id <= 0) { + // If category was not given get the default one. + $catDAO = new FreshRSS_CategoryDAO(); + $catDAO->checkDefault(); + $def_cat = $catDAO->getDefault(); + $cat_id = $def_cat->id(); + } + $feedDAO = FreshRSS_Factory::createFeedDao(); + $feed = $feedDAO->searchById($feed_id); + return $feed && ($feed->category() == $cat_id || + $feedDAO->updateFeed($feed_id, array('category' => $cat_id))); + } + /** * This action changes the category of a feed. * @@ -511,9 +528,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feed_id = Minz_Request::param('f_id'); $cat_id = Minz_Request::param('c_id'); - $feedDAO = FreshRSS_Factory::createFeedDao(); - if ($feedDAO->moveFeed($feed_id, $cat_id)) { + if (self::moveFeed($feed_id, $cat_id)) { // TODO: return something useful } else { Minz_Log::warning('Cannot move feed `' . $feed_id . '` ' . @@ -522,6 +538,21 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } + public static function deleteFeed($feed_id) { + $feedDAO = FreshRSS_Factory::createFeedDao(); + if ($feedDAO->deleteFeed($feed_id)) { + // TODO: Delete old favicon + + // Remove related queries + FreshRSS_Context::$user_conf->queries = remove_query_by_get( + 'f_' . $feed_id, FreshRSS_Context::$user_conf->queries); + FreshRSS_Context::$user_conf->save(); + + return true; + } + return false; + } + /** * This action deletes a feed. * @@ -540,21 +571,13 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if (!$redirect_url) { $redirect_url = array('c' => 'subscription', 'a' => 'index'); } - if (!Minz_Request::isPost()) { Minz_Request::forward($redirect_url, true); } $id = Minz_Request::param('id'); - $feedDAO = FreshRSS_Factory::createFeedDao(); - if ($feedDAO->deleteFeed($id)) { - // TODO: Delete old favicon - - // Remove related queries - FreshRSS_Context::$user_conf->queries = remove_query_by_get( - 'f_' . $id, FreshRSS_Context::$user_conf->queries); - FreshRSS_Context::$user_conf->save(); + if (self::deleteFeed($id)) { Minz_Request::good(_t('feedback.sub.feed.deleted'), $redirect_url); } else { Minz_Request::bad(_t('feedback.sub.feed.error'), $redirect_url); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 2fd2c6194..475d39286 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -136,19 +136,6 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } - public function moveFeed($feed_id, $cat_id) { - if ($cat_id <= 0) { - // If category was not given get the default one. - $catDAO = new FreshRSS_CategoryDAO(); - $catDAO->checkDefault(); - $def_cat = $catDAO->getDefault(); - $cat_id = $def_cat->id(); - } - $feed = $this->searchById($feed_id); - return $feed && ($feed->category() == $cat_id || - $this->updateFeed($feed_id, array('category' => $cat_id))); - } - public function deleteFeed($id) { $sql = 'DELETE FROM `' . $this->prefix . 'feed` WHERE id=?'; $stm = $this->bd->prepare($sql); diff --git a/p/api/greader.php b/p/api/greader.php index 7094fb381..9b07e0729 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -153,13 +153,12 @@ function authorizationToUser() { if (count($headerAuthX) === 2) { $user = $headerAuthX[0]; if (ctype_alnum($user)) { - $conf = get_user_configuration($user); - if (is_null($conf)) { + FreshRSS_Context::$user_conf = get_user_configuration($user); + if (FreshRSS_Context::$user_conf == null) { Minz_Log::warning('Invalid API user ' . $user . ': configuration cannot be found.'); unauthorized(); } - global $system_conf; - if ($headerAuthX[1] === sha1($system_conf->salt . $user . $conf->apiPasswordHash)) { + if ($headerAuthX[1] === sha1(FreshRSS_Context::$system_conf->salt . $user . FreshRSS_Context::$user_conf->apiPasswordHash)) { return $user; } else { logMe('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1]); @@ -181,16 +180,15 @@ function clientLogin($email, $pass) { //http://web.archive.org/web/2013060409104 include_once(LIB_PATH . '/password_compat.php'); } - $conf = get_user_configuration($email); - if (is_null($conf)) { + FreshRSS_Context::$user_conf = get_user_configuration($email); + if (FreshRSS_Context::$user_conf == null) { Minz_Log::warning('Invalid API user ' . $email . ': configuration cannot be found.'); unauthorized(); } - if ($conf->apiPasswordHash != '' && password_verify($pass, $conf->apiPasswordHash)) { + if (FreshRSS_Context::$user_conf->apiPasswordHash != '' && password_verify($pass, FreshRSS_Context::$user_conf->apiPasswordHash)) { header('Content-Type: text/plain; charset=UTF-8'); - global $system_conf; - $auth = $email . '/' . sha1($system_conf->salt . $email . $conf->apiPasswordHash); + $auth = $email . '/' . sha1(FreshRSS_Context::$system_conf->salt . $email . FreshRSS_Context::$user_conf->apiPasswordHash); echo 'SID=', $auth, "\n", 'Auth=', $auth, "\n"; exit(); @@ -209,8 +207,7 @@ function token($conf) { //https://github.com/ericmann/gReader-Library/blob/master/greader.class.php $user = Minz_Session::param('currentUser', '_'); //logMe('token('. $user . ")"); //TODO: Implement real token that expires - global $system_conf; - $token = str_pad(sha1($system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z'); //Must have 57 characters + $token = str_pad(sha1(FreshRSS_Context::$system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z'); //Must have 57 characters echo $token, "\n"; exit(); } @@ -219,8 +216,7 @@ function checkToken($conf, $token) { //http://code.google.com/p/google-reader-api/wiki/ActionToken $user = Minz_Session::param('currentUser', '_'); //logMe('checkToken(' . $token . ")"); - global $system_conf; - if ($token === str_pad(sha1($system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z')) { + if ($token === str_pad(sha1(FreshRSS_Context::$system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z')) { return true; } unauthorized(); @@ -261,8 +257,7 @@ function subscriptionList() { $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_ASSOC); - global $system_conf; - $salt = $system_conf->salt; + $salt = FreshRSS_Context::$system_conf->salt; $subscriptions = array(); foreach ($res as $line) { @@ -296,7 +291,6 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = ' case 'edit': break; default: - logMe("Bad action: $action"); badRequest(); } $addCatId = 0; @@ -336,14 +330,14 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = ' } break; case 'unsubscribe': - if ($feedId > 0) { - //TODO + if ($feedId > 0 && FreshRSS_feed_Controller::deleteFeed($feedId)) { + exit('OK'); } else { badRequest(); } break; case 'edit': - if ($feedId > 0 && $feedDAO->moveFeed($feedId, $addCatId)) { + if ($feedId > 0 && FreshRSS_feed_Controller::moveFeed($feedId, $addCatId)) { exit('OK'); } badRequest(); @@ -620,17 +614,17 @@ $pathInfos = explode('/', $pathInfo); Minz_Configuration::register('system', DATA_PATH . '/config.php', DATA_PATH . '/config.default.php'); -$system_conf = Minz_Configuration::get('system'); -if (!$system_conf->api_enabled) { +FreshRSS_Context::$system_conf = Minz_Configuration::get('system'); +if (!FreshRSS_Context::$system_conf->api_enabled) { serviceUnavailable(); } Minz_Session::init('FreshRSS'); $user = authorizationToUser(); -$conf = null; +FreshRSS_Context::$user_conf = null; if ($user !== '') { - $conf = get_user_configuration($user); + FreshRSS_Context::$user_conf = get_user_configuration($user); } //logMe('User => ' . $user); @@ -722,7 +716,7 @@ elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfo 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($conf, $token); + checkToken(FreshRSS_Context::$user_conf, $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 @@ -730,7 +724,7 @@ elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfo break; case 'mark-all-as-read': $token = isset($_POST['T']) ? trim($_POST['T']) : ''; - checkToken($conf, $token); + checkToken(FreshRSS_Context::$user_conf, $token); $streamId = $_POST['s']; //StreamId $ts = isset($_POST['ts']) ? $_POST['ts'] : '0'; //Older than timestamp in nanoseconds if (!ctype_digit($ts)) { @@ -739,7 +733,7 @@ elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfo markAllAsRead($streamId, $ts); break; case 'token': - token($conf); + token(FreshRSS_Context::$user_conf); break; } } elseif ($pathInfos[1] === 'check' && $pathInfos[2] === 'compatibility') { -- cgit v1.2.3 From 6a812b0d31df275d9b8b211a90628400ee097644 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 24 Sep 2016 19:54:46 +0200 Subject: API rename feed --- app/Controllers/feedController.php | 12 +++++++++--- p/api/greader.php | 19 ++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 0ff27e0da..0364424f5 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -492,6 +492,14 @@ class FreshRSS_feed_Controller extends Minz_ActionController { return $updated_feeds; } + public static function renameFeed($feed_id, $feed_name) { + if ($feed_id <= 0 || $feed_name == '') { + return false; + } + $feedDAO = FreshRSS_Factory::createFeedDao(); + return $feedDAO->updateFeed($feed_id, array('name' => $feed_name)); + } + public static function moveFeed($feed_id, $cat_id) { if ($feed_id <= 0) { return false; @@ -504,9 +512,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $cat_id = $def_cat->id(); } $feedDAO = FreshRSS_Factory::createFeedDao(); - $feed = $feedDAO->searchById($feed_id); - return $feed && ($feed->category() == $cat_id || - $feedDAO->updateFeed($feed_id, array('category' => $cat_id))); + return $feedDAO->updateFeed($feed_id, array('category' => $cat_id)); } /** diff --git a/p/api/greader.php b/p/api/greader.php index 9b07e0729..131eb65c5 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -325,27 +325,32 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = ' case 'subscribe': if ($feedId <= 0) { //TODO + notImplemented(); } else { badRequest(); } break; case 'unsubscribe': - if ($feedId > 0 && FreshRSS_feed_Controller::deleteFeed($feedId)) { - exit('OK'); - } else { + if (!($feedId > 0 && FreshRSS_feed_Controller::deleteFeed($feedId))) { badRequest(); } break; case 'edit': - if ($feedId > 0 && FreshRSS_feed_Controller::moveFeed($feedId, $addCatId)) { - exit('OK'); + if ($feedId > 0) { + if ($addCatId > 0) { + FreshRSS_feed_Controller::moveFeed($feedId, $addCatId); + } + if ($title != '') { + FreshRSS_feed_Controller::renameFeed($feedId, $title); + } + } else { + badRequest(); } - badRequest(); break; } } } - notImplemented(); + exit('OK'); } function unreadCount() { //http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#unread-count -- cgit v1.2.3 From 0a79d4085b18c5607438f8ebd56543508e7db3a8 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 24 Sep 2016 20:43:52 +0200 Subject: Refactor controller for actualize feed --- app/Controllers/feedController.php | 46 +++++++++++++++++++++----------------- lib/lib_rss.php | 2 +- p/api/pshb.php | 7 ++---- 3 files changed, 28 insertions(+), 27 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 0364424f5..25d504480 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -261,33 +261,18 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - /** - * This action actualizes entries from one or several feeds. - * - * Parameters are: - * - id (default: false): Feed ID - * - url (default: false): Feed URL - * - force (default: false) - * If id and url are 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($simplePiePush = null) { + public static function actualizeFeed($feed_id, $feed_url, $force, $simplePiePush = null) { @set_time_limit(300); $feedDAO = FreshRSS_Factory::createFeedDao(); $entryDAO = FreshRSS_Factory::createEntryDao(); - Minz_Session::_param('actualize_feeds', false); - $id = Minz_Request::param('id'); - $url = Minz_Request::param('url'); - $force = Minz_Request::param('force'); - // Create a list of feeds to actualize. - // If id is set and valid, corresponding feed is added to the list but + // If feed_id is set and valid, corresponding feed is added to the list but // alone in order to automatize further process. $feeds = array(); - if ($id || $url) { - $feed = $id ? $feedDAO->searchById($id) : $feedDAO->searchByUrl($url); + if ($feed_id > 0 || $feed_url) { + $feed = $feed_id > 0 ? $feedDAO->searchById($feed_id) : $feedDAO->searchByUrl($feed_url); if ($feed) { $feeds[] = $feed; } @@ -309,7 +294,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $url = $feed->url(); //For detection of HTTP 301 $pubSubHubbubEnabled = $pubsubhubbubEnabledGeneral && $feed->pubSubHubbubEnabled(); - if ((!$simplePiePush) && (!$id) && $pubSubHubbubEnabled && ($feed->lastUpdate() > $pshbMinAge)) { + if ((!$simplePiePush) && (!$feed_id) && $pubSubHubbubEnabled && ($feed->lastUpdate() > $pshbMinAge)) { //$text = 'Skip pull of feed using PubSubHubbub: ' . $url; //Minz_Log::debug($text); //file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND); @@ -464,6 +449,26 @@ class FreshRSS_feed_Controller extends Minz_ActionController { break; } } + return array($updated_feeds, reset($feeds)); + } + + /** + * This action actualizes entries from one or several feeds. + * + * Parameters are: + * - id (default: false): Feed ID + * - url (default: false): Feed URL + * - force (default: false) + * If id and url are 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() { + Minz_Session::_param('actualize_feeds', false); + $id = Minz_Request::param('id'); + $url = Minz_Request::param('url'); + $force = Minz_Request::param('force'); + + list($updated_feeds, $feed) = self::actualizeFeed($id, $url, $force); if (Minz_Request::param('ajax')) { // Most of the time, ajax request is for only one feed. But since @@ -479,7 +484,6 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } else { // Redirect to the main page with correct notification. if ($updated_feeds === 1) { - $feed = reset($feeds); Minz_Request::good(_t('feedback.sub.feed.actualized', $feed->name()), array( 'params' => array('get' => 'f_' . $feed->id()) )); diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 82ddced2c..a4cd8e782 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -165,7 +165,7 @@ function customSimplePie() { $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_useragent('FreshRSS/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION); $simplePie->set_syslog($system_conf->simplepie_syslog_enabled); $simplePie->set_cache_location(CACHE_PATH); $simplePie->set_cache_duration($limits['cache_duration']); diff --git a/p/api/pshb.php b/p/api/pshb.php index 650767114..94a0068ed 100644 --- a/p/api/pshb.php +++ b/p/api/pshb.php @@ -88,9 +88,6 @@ if ($ORIGINAL_INPUT == '') { Minz_Configuration::register('system', DATA_PATH . '/config.php', DATA_PATH . '/config.default.php'); $system_conf = Minz_Configuration::get('system'); $system_conf->auth_type = 'none'; // avoid necessity to be logged in (not saved!) -Minz_Translate::init('en'); -Minz_Request::_param('ajax', true); -$feedController = new FreshRSS_feed_Controller(); $simplePie = customSimplePie(); $simplePie->set_raw_data($ORIGINAL_INPUT); @@ -106,7 +103,6 @@ if ($self !== base64url_decode($canonical64)) { //die('Self URL does not match registered canonical URL!'); $self = base64url_decode($canonical64); } -Minz_Request::_param('url', $self); $nb = 0; foreach ($users as $userFilename) { @@ -121,7 +117,8 @@ foreach ($users as $userFilename) { join_path(USERS_PATH, $username, 'config.php'), join_path(USERS_PATH, '_', 'config.default.php')); FreshRSS_Context::init(); - if ($feedController->actualizeAction($simplePie) > 0) { + list($updated_feeds, $feed) = FreshRSS_feed_Controller::actualizeFeed(0, $self, false, $simplePie); + if ($updated_feeds > 0) { $nb++; } } catch (Exception $e) { -- cgit v1.2.3 From 324c83348ca44f3c13f4e0efeea25bbc96ed3b05 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 24 Sep 2016 23:29:49 +0200 Subject: Refactor controller add feed --- app/Controllers/feedController.php | 181 +++++++++++--------------- app/Exceptions/AlreadySubscribedException.php | 14 ++ app/Exceptions/FeedNotAddedException.php | 14 ++ app/Models/Feed.php | 4 +- 4 files changed, 105 insertions(+), 108 deletions(-) create mode 100644 app/Exceptions/AlreadySubscribedException.php create mode 100644 app/Exceptions/FeedNotAddedException.php (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 25d504480..c6adc64cb 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -26,6 +26,63 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } + public static function addFeed($url, $cat_id = 0, $new_cat_name = '', $http_auth = '') { + @set_time_limit(300); + + $catDAO = new FreshRSS_CategoryDAO(); + + $cat = null; + if ($cat_id > 0) { + $cat = $catDAO->searchById($cat_id); + } + if ($cat == null && $new_cat_name != '') { + $cat = $catDAO->addCategory(array('name' => $new_cat_name)); + } + if ($cat == null) { + $catDAO->checkDefault(); + $def_cat = $catDAO->getDefault(); + $cat = $def_cat->id(); + } + + $feed = new FreshRSS_Feed($url); //Throws FreshRSS_BadUrl_Exception + $feed->_httpAuth($http_auth); + $feed->load(true); //Throws FreshRSS_Feed_Exception, Minz_FileNotExistException + $feed->_category($cat_id); + + $feedDAO = FreshRSS_Factory::createFeedDao(); + if ($feedDAO->searchByUrl($feed->url())) { + throw new FreshRSS_AlreadySubscribed_Exception($url, $feed->name()); + } + + // Call the extension hook + $feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed); + if ($feed === null) { + throw new FreshRSS_FeedNotAdded_Exception($url, $feed->name()); + } + + $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. + throw new FreshRSS_FeedNotAdded_Exception($url, $feed->name()); + } + $feed->_id($id); + + // Ok, feed has been added in database. Now we have to refresh entries. + self::actualizeFeed($id, $url, false, null, true); + + return $feed; + } + /** * This action subscribes to a feed. * @@ -59,7 +116,6 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } $feedDAO = FreshRSS_Factory::createFeedDao(); - $this->catDAO = new FreshRSS_CategoryDAO(); $url_redirect = array( 'c' => 'subscription', 'a' => 'index', @@ -74,26 +130,13 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } if (Minz_Request::isPost()) { - @set_time_limit(300); - $cat = Minz_Request::param('category'); + $new_cat_name = ''; 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; - } else { - $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(); + $new_cat_name = isset($new_cat['name']) ? $new_cat['name'] : ''; } // HTTP information are useful if feed is protected behind a @@ -105,103 +148,24 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $http_auth = $user . ':' . $pass; } - $transaction_started = false; try { - $feed = new FreshRSS_Feed($url); + $feed = self::addFeed($url, $cat, $new_cat_name, $http_auth); } catch (FreshRSS_BadUrl_Exception $e) { // Given url was not a valid url! Minz_Log::warning($e->getMessage()); Minz_Request::bad(_t('feedback.sub.feed.invalid_url', $url), $url_redirect); - } - - $feed->_httpAuth($http_auth); - - try { - $feed->load(true); } catch (FreshRSS_Feed_Exception $e) { // Something went bad (timeout, server not found, etc.) Minz_Log::warning($e->getMessage()); - Minz_Request::bad( - _t('feedback.sub.feed.internal_problem', _url('index', 'logs')), - $url_redirect - ); + Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect); } catch (Minz_FileNotExistException $e) { // Cache directory doesn't exist! Minz_Log::error($e->getMessage()); - Minz_Request::bad( - _t('feedback.sub.feed.internal_problem', _url('index', 'logs')), - $url_redirect - ); - } - - if ($feedDAO->searchByUrl($feed->url())) { - Minz_Request::bad( - _t('feedback.sub.feed.already_subscribed', $feed->name()), - $url_redirect - ); - } - - $feed->_category($cat); - - // Call the extension hook - $name = $feed->name(); - $feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed); - if ($feed === null) { - Minz_Request::bad(_t('feedback.sub.feed.not_added', $name), $url_redirect); - } - - $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('feedback.sub.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(); - //$feed->pubSubHubbubPrepare(); //TODO: prepare PubSubHubbub already when adding the feed - - $is_read = FreshRSS_Context::$user_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 = FreshRSS_Context::$user_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. - $feedDAO->beginTransaction(); - foreach ($entries as $entry) { - // Entries are added without any verification. - $entry->_feed($feed->id()); - $entry->_id(min(time(), $entry->date(true)) . uSecString()); - $entry->_isRead($is_read); - - $entry = Minz_ExtensionManager::callHook('entry_before_insert', $entry); - if ($entry === null) { - // An extension has returned a null value, there is nothing to insert. - continue; - } - - $values = $entry->toArray(); - $entryDAO->addEntry($values); - } - $feedDAO->updateLastUpdate($feed->id()); - if ($feedDAO->inTransaction()) { - $feedDAO->commit(); + Minz_Request::bad(_t('feedback.sub.feed.internal_problem', _url('index', 'logs')), $url_redirect); + } catch (FreshRSS_AlreadySubscribed_Exception $e) { + Minz_Request::bad(_t('feedback.sub.feed.already_subscribed', $e->feedName()), $url_redirect); + } catch (FreshRSS_FeedNotAdded_Exception $e) { + Minz_Request::bad(_t('feedback.sub.feed.not_added', $e->feedName()), $url_redirect); } // Entries are in DB, we redirect to feed configuration page. @@ -211,6 +175,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { // GET request: we must ask confirmation to user before adding feed. Minz_View::prependTitle(_t('sub.feed.title_add') . ' · '); + $this->catDAO = new FreshRSS_CategoryDAO(); $this->view->categories = $this->catDAO->listCategories(false); $this->view->feed = new FreshRSS_Feed($url); try { @@ -261,7 +226,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - public static function actualizeFeed($feed_id, $feed_url, $force, $simplePiePush = null) { + public static function actualizeFeed($feed_id, $feed_url, $force, $simplePiePush = null, $isNewFeed = false) { @set_time_limit(300); $feedDAO = FreshRSS_Factory::createFeedDao(); @@ -310,7 +275,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if ($simplePiePush) { $feed->loadEntries($simplePiePush); //Used by PubSubHubbub } else { - $feed->load(false); + $feed->load(false, $isNewFeed); } } catch (FreshRSS_Feed_Exception $e) { Minz_Log::warning($e->getMessage()); @@ -320,7 +285,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } $feed_history = $feed->keepHistory(); - if ($feed_history == -2) { + if ($isNewFeed) { + $feed_history = -1; //∞ + } elseif ($feed_history == -2) { // TODO: -2 must be a constant! // -2 means we take the default value from configuration $feed_history = FreshRSS_Context::$user_conf->keep_history_default; @@ -360,7 +327,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { // This entry should not be added considering configuration and date. $oldGuids[] = $entry->guid(); } else { - if ($entry_date < $date_min) { + if ($isNewFeed) { + $id = min(time(), $entry_date) . uSecString(); + } elseif ($entry_date < $date_min) { $id = min(time(), $entry_date) . uSecString(); $entry->_isRead(true); //Old article that was not in database. Probably an error, so mark as read } else { diff --git a/app/Exceptions/AlreadySubscribedException.php b/app/Exceptions/AlreadySubscribedException.php new file mode 100644 index 000000000..f85d51300 --- /dev/null +++ b/app/Exceptions/AlreadySubscribedException.php @@ -0,0 +1,14 @@ +$feedName = $feedName; + } + + public function feedName() { + return $this->feedName(); + } +} diff --git a/app/Exceptions/FeedNotAddedException.php b/app/Exceptions/FeedNotAddedException.php new file mode 100644 index 000000000..02a8bc6be --- /dev/null +++ b/app/Exceptions/FeedNotAddedException.php @@ -0,0 +1,14 @@ +$feedName = $feedName; + } + + public function feedName() { + return $this->feedName(); + } +} diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 4403a23c4..658b9ccca 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -216,7 +216,7 @@ class FreshRSS_Feed extends Minz_Model { $this->nbEntries = intval($value); } - public function load($loadDetails = false) { + public function load($loadDetails = false, $noCache = false) { if ($this->url !== null) { if (CACHE_PATH === false) { throw new Minz_FileNotExistException( @@ -268,7 +268,7 @@ class FreshRSS_Feed extends Minz_Model { $this->_url($clean_url); } - if (($mtime === true) || ($mtime > $this->lastUpdate)) { + if (($mtime === true) || ($mtime > $this->lastUpdate) || $noCache) { //Minz_Log::debug('FreshRSS no cache ' . $mtime . ' > ' . $this->lastUpdate . ' for ' . $clean_url); $this->loadEntries($feed); // et on charge les articles du flux } else { -- cgit v1.2.3 From 71b98a0ffc41ea869e8c49c2889cda5b3e113030 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 25 Sep 2016 00:14:36 +0200 Subject: API add feed --- app/Controllers/feedController.php | 10 +++++----- p/api/greader.php | 20 ++++++++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index c6adc64cb..faf670e6e 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -26,7 +26,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - public static function addFeed($url, $cat_id = 0, $new_cat_name = '', $http_auth = '') { + public static function addFeed($url, $title = '', $cat_id = 0, $new_cat_name = '', $http_auth = '') { @set_time_limit(300); $catDAO = new FreshRSS_CategoryDAO(); @@ -40,9 +40,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } if ($cat == null) { $catDAO->checkDefault(); - $def_cat = $catDAO->getDefault(); - $cat = $def_cat->id(); + $cat = $catDAO->getDefault(); } + $cat_id = $cat->id(); $feed = new FreshRSS_Feed($url); //Throws FreshRSS_BadUrl_Exception $feed->_httpAuth($http_auth); @@ -63,7 +63,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $values = array( 'url' => $feed->url(), 'category' => $feed->category(), - 'name' => $feed->name(), + 'name' => $title != '' ? $title : $feed->name(), 'website' => $feed->website(), 'description' => $feed->description(), 'lastUpdate' => time(), @@ -149,7 +149,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } try { - $feed = self::addFeed($url, $cat, $new_cat_name, $http_auth); + $feed = self::addFeed($url, '', $cat, $new_cat_name, $http_auth); } catch (FreshRSS_BadUrl_Exception $e) { // Given url was not a valid url! Minz_Log::warning($e->getMessage()); diff --git a/p/api/greader.php b/p/api/greader.php index 131eb65c5..dc8dd92bc 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -298,18 +298,22 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = ' if ($add != '' || $remove != '') { $categoryDAO = new FreshRSS_CategoryDAO(); } + $c_name = ''; if ($add != '' && strpos($add, 'user/-/label/') === 0) { //user/-/label/Example - $c_name = basename($add); + $c_name = substr($add, 13); $cat = $categoryDAO->searchByName($c_name); $addCatId = $cat == null ? -1 : $cat->id(); } else if ($remove != '' && strpos($remove, 'user/-/label/')) { $addCatId = 1; //Default category } + if ($addCatId <= 0 && $c_name = '') { + $addCatId = 1; //Default category + } $feedDAO = FreshRSS_Factory::createFeedDao(); for ($i = count($streamNames) - 1; $i >= 0; $i--) { $streamName = $streamNames[$i]; //feed/http://example.net/sample.xml ; feed/338 if (strpos($streamName, 'feed/') === 0) { - $streamName = basename($streamName); + $streamName = substr($streamName, 5); $feedId = 0; if (ctype_digit($streamName)) { if ($action === 'subscribe') { @@ -324,11 +328,15 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = ' switch ($action) { case 'subscribe': if ($feedId <= 0) { - //TODO - notImplemented(); - } else { - badRequest(); + $http_auth = ''; //TODO + try { + $feed = FreshRSS_feed_Controller::addFeed($streamName, $title, $addCatId, $c_name, $http_auth); + continue; + } catch (Exception $e) { + logMe("subscriptionEdit error subscribe: " . $e->getMessage()); + } } + badRequest(); break; case 'unsubscribe': if (!($feedId > 0 && FreshRSS_feed_Controller::deleteFeed($feedId))) { -- cgit v1.2.3 From 9291748c4745ff8f4be2beaa2998869fd26e907e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 26 Sep 2016 10:44:44 +0200 Subject: API implement user-info and fix edits https://github.com/FreshRSS/FreshRSS/issues/1254 https://github.com/jangernert/FeedReader/issues/59#issuecomment-249491580 --- app/Controllers/feedController.php | 20 +++++++++++----- app/Models/CategoryDAO.php | 3 +++ data/users/_/config.default.php | 1 + p/api/greader.php | 47 ++++++++++++++++++++++++++++++-------- 4 files changed, 55 insertions(+), 16 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index faf670e6e..ca7a818c6 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -473,17 +473,25 @@ class FreshRSS_feed_Controller extends Minz_ActionController { return $feedDAO->updateFeed($feed_id, array('name' => $feed_name)); } - public static function moveFeed($feed_id, $cat_id) { + public static function moveFeed($feed_id, $cat_id, $new_cat_name = '') { if ($feed_id <= 0) { return false; } - if ($cat_id <= 0) { - // If category was not given get the default one. - $catDAO = new FreshRSS_CategoryDAO(); + + $catDAO = new FreshRSS_CategoryDAO(); + if ($cat_id > 0) { + $cat = $catDAO->searchById($cat_id); + $cat_id = $cat == null ? 0 : $cat->id(); + } + if ($cat_id <= 1 && $new_cat_name != '') { + $cat_id = $catDAO->addCategory(array('name' => $new_cat_name)); + } + if ($cat_id <= 1) { $catDAO->checkDefault(); - $def_cat = $catDAO->getDefault(); - $cat_id = $def_cat->id(); + $cat = $catDAO->getDefault(); + $cat_id = $cat->id(); } + $feedDAO = FreshRSS_Factory::createFeedDao(); return $feedDAO->updateFeed($feed_id, array('category' => $cat_id)); } diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index fc431553e..c103163a1 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -50,6 +50,9 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } public function deleteCategory($id) { + if ($id <= 1) { + return false; + } $sql = 'DELETE FROM `' . $this->prefix . 'category` WHERE id=?'; $stm = $this->bd->prepare($sql); diff --git a/data/users/_/config.default.php b/data/users/_/config.default.php index 2c56d5f45..6e1d497bc 100644 --- a/data/users/_/config.default.php +++ b/data/users/_/config.default.php @@ -5,6 +5,7 @@ return array ( 'old_entries' => 3, 'keep_history_default' => 0, 'ttl_default' => 3600, + 'mail_login' => '', 'token' => '', 'passwordHash' => '', 'apiPasswordHash' => '', diff --git a/p/api/greader.php b/p/api/greader.php index e2d7dc039..8b1e0ffb1 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -222,6 +222,17 @@ function checkToken($conf, $token) { unauthorized(); } +function userInfo() { //https://github.com/theoldreader/api#user-info + //logMe("userInfo()"); + $user = Minz_Session::param('currentUser', '_'); + exit(json_encode(array( + 'userId' => $user, + 'userName' => $user, + 'userProfileId' => $user, + 'userEmail' => FreshRSS_Context::$user_conf->mail_login, + ))); +} + function tagList() { //logMe("tagList()"); header('Content-Type: application/json; charset=UTF-8'); @@ -299,14 +310,24 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = ' $categoryDAO = new FreshRSS_CategoryDAO(); } $c_name = ''; - if ($add != '' && strpos($add, 'user/-/label/') === 0) { //user/-/label/Example - $c_name = substr($add, 13); + if ($add != '' && strpos($add, 'user/') === 0) { //user/-/label/Example ; user/username/label/Example + if (strpos($add, 'user/-/label/') === 0) { + $c_name = substr($add, 13); + } else { + $user = Minz_Session::param('currentUser', '_'); + $prefix = 'user/' . $user . '/label/'; + if (strpos($add, $prefix) === 0) { + $c_name = substr($add, strlen($prefix)); + } else { + $c_name = ''; + } + } $cat = $categoryDAO->searchByName($c_name); $addCatId = $cat == null ? -1 : $cat->id(); } else if ($remove != '' && strpos($remove, 'user/-/label/')) { $addCatId = 1; //Default category } - if ($addCatId <= 0 && $c_name = '') { + if ($addCatId <= 0 && $c_name == '') { $addCatId = 1; //Default category } $feedDAO = FreshRSS_Factory::createFeedDao(); @@ -345,9 +366,7 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = ' break; case 'edit': if ($feedId > 0) { - if ($addCatId > 0) { - FreshRSS_feed_Controller::moveFeed($feedId, $addCatId); - } + FreshRSS_feed_Controller::moveFeed($feedId, $addCatId, $c_name); if ($title != '') { FreshRSS_feed_Controller::renameFeed($feedId, $title); } @@ -633,8 +652,8 @@ function renameTag($s, $dest) { badRequest(); } -function disableTag($s, $dest) { - //logMe("renameTag()"); +function disableTag($s) { + //logMe("disableTag($s)"); if ($s != '' && strpos($s, 'user/-/label/') === 0) { $s = substr($s, 13); $categoryDAO = new FreshRSS_CategoryDAO(); @@ -642,6 +661,9 @@ function disableTag($s, $dest) { if ($cat != null) { $feedDAO = FreshRSS_Factory::createFeedDao(); $feedDAO->changeCategory($cat->id(), 0); + if ($cat->id() > 1) { + $categoryDAO->deleteCategory($cat->id()); + } exit('OK'); } } @@ -798,8 +820,10 @@ elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfo case 'disable-tag': //https://github.com/theoldreader/api $token = isset($_POST['T']) ? trim($_POST['T']) : ''; checkToken(FreshRSS_Context::$user_conf, $token); - $s = isset($_POST['s']) ? $_POST['s'] : ''; //user/-/label/Folder - disableTag($s); + $s_s = multiplePosts('s'); + foreach ($s_s as $s) { + disableTag($s); //user/-/label/Folder + } break; case 'mark-all-as-read': $token = isset($_POST['T']) ? trim($_POST['T']) : ''; @@ -814,6 +838,9 @@ elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfo case 'token': token(FreshRSS_Context::$user_conf); break; + case 'user-info': + userInfo(); + break; } } elseif ($pathInfos[1] === 'check' && $pathInfos[2] === 'compatibility') { checkCompatibility(); -- cgit v1.2.3 From d5ca360ca967d327a184429a2e37aed196add7a0 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 26 Sep 2016 15:20:02 +0200 Subject: API fix feed rename https://github.com/FreshRSS/FreshRSS/issues/1254 https://github.com/jangernert/FeedReader/issues/59#issuecomment-249558202 --- app/Controllers/feedController.php | 2 +- p/api/greader.php | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index ca7a818c6..c2a22aeb3 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -474,7 +474,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } public static function moveFeed($feed_id, $cat_id, $new_cat_name = '') { - if ($feed_id <= 0) { + if ($feed_id <= 0 || ($cat_id <= 0 && $new_cat_name == '')) { return false; } diff --git a/p/api/greader.php b/p/api/greader.php index 8b1e0ffb1..4965ffd3b 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -323,13 +323,10 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = ' } } $cat = $categoryDAO->searchByName($c_name); - $addCatId = $cat == null ? -1 : $cat->id(); + $addCatId = $cat == null ? 0 : $cat->id(); } else if ($remove != '' && strpos($remove, 'user/-/label/')) { $addCatId = 1; //Default category } - if ($addCatId <= 0 && $c_name == '') { - $addCatId = 1; //Default category - } $feedDAO = FreshRSS_Factory::createFeedDao(); for ($i = count($streamNames) - 1; $i >= 0; $i--) { $streamName = $streamNames[$i]; //feed/http://example.net/sample.xml ; feed/338 @@ -366,7 +363,9 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = ' break; case 'edit': if ($feedId > 0) { - FreshRSS_feed_Controller::moveFeed($feedId, $addCatId, $c_name); + if ($addCatId > 0 || $c_name != '') { + FreshRSS_feed_Controller::moveFeed($feedId, $addCatId, $c_name); + } if ($title != '') { FreshRSS_feed_Controller::renameFeed($feedId, $title); } -- cgit v1.2.3 From c4b43316c756c4e8f5b35128e6ab40bc4f724337 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 1 Oct 2016 20:22:34 +0200 Subject: Export files with date --- app/Controllers/importExportController.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 60e467255..9dfc479f0 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -469,20 +469,21 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $export_opml = Minz_Request::param('export_opml', false); $export_starred = Minz_Request::param('export_starred', false); $export_feeds = Minz_Request::param('export_feeds', array()); + $day = date('Y-m-d'); $export_files = array(); if ($export_opml) { - $export_files['feeds.opml'] = $this->generateOpml(); + $export_files["feeds_${day}.opml.xml"] = $this->generateOpml(); } if ($export_starred) { - $export_files['starred.json'] = $this->generateEntries('starred'); + $export_files["starred_${day}.json"] = $this->generateEntries('starred'); } foreach ($export_feeds as $feed_id) { $feed = $this->feedDAO->searchById($feed_id); if ($feed) { - $filename = 'feed_' . $feed->category() . '_' + $filename = "feed_${day}_" . $feed->category() . '_' . $feed->id() . '.json'; $export_files[$filename] = $this->generateEntries('feed', $feed); } @@ -579,7 +580,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $zip->close(); header('Content-Type: application/zip'); header('Content-Length: ' . filesize($zip_file)); - header('Content-Disposition: attachment; filename="freshrss_export.zip"'); + $day = date('Y-m-d'); + header('Content-Disposition: attachment; filename="freshrss_' . $day . '_export.zip"'); readfile($zip_file); unlink($zip_file); } @@ -599,9 +601,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $content_type = ''; if ($type === 'opml') { - $content_type = "text/opml"; + $content_type = 'application/xml'; } elseif ($type === 'json_feed' || $type === 'json_starred') { - $content_type = "text/json"; + $content_type = 'application/json'; } header('Content-Type: ' . $content_type . '; charset=utf-8'); -- cgit v1.2.3 From f81c441920d2de087099c85f0119e823d15225c4 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 4 Oct 2016 21:06:37 +0200 Subject: Fix bug language option for new user https://github.com/FreshRSS/FreshRSS/issues/1273 --- app/Controllers/userController.php | 4 ++-- app/Models/UserDAO.php | 41 +++++++++++++++++++++++--------------- app/views/user/manage.phtml | 4 ++-- 3 files changed, 29 insertions(+), 20 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 0521bc008..c259ffde9 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -121,7 +121,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $new_user_language = Minz_Request::param('new_user_language', FreshRSS_Context::$user_conf->language); $languages = Minz_Translate::availableLanguages(); - if (!isset($languages[$new_user_language])) { + if (!in_array($new_user_language, $languages)) { $new_user_language = FreshRSS_Context::$user_conf->language; } @@ -165,7 +165,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { } if ($ok) { $userDAO = new FreshRSS_UserDAO(); - $ok &= $userDAO->createUser($new_user_name); + $ok &= $userDAO->createUser($new_user_name, $new_user_language); } invalidateHttpCache(); diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index e35be848c..597182693 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -1,35 +1,44 @@ db; require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); $userPDO = new Minz_ModelPdo($username); - $ok = false; - $bd_prefix_user = $db['prefix'] . $username . '_'; - if (defined('SQL_CREATE_TABLES')) { //E.g. MySQL - $sql = sprintf(SQL_CREATE_TABLES, $bd_prefix_user, _t('gen.short.default_category')); - $stm = $userPDO->bd->prepare($sql); - $ok = $stm && $stm->execute(); - } 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, $bd_prefix_user, _t('gen.short.default_category')); - $stm = $userPDO->bd->prepare($sql); - $ok &= ($stm && $stm->execute()); + $currentLanguage = Minz_Translate::language(); + + try { + Minz_Translate::reset($new_user_language); + $ok = false; + $bd_prefix_user = $db['prefix'] . $username . '_'; + if (defined('SQL_CREATE_TABLES')) { //E.g. MySQL + $sql = sprintf(SQL_CREATE_TABLES, $bd_prefix_user, _t('gen.short.default_category')); + $stm = $userPDO->bd->prepare($sql); + $ok = $stm && $stm->execute(); + } 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, $bd_prefix_user, _t('gen.short.default_category')); + $stm = $userPDO->bd->prepare($sql); + $ok &= ($stm && $stm->execute()); + } } } + } catch (Exception $e) { + Minz_Log::error('Error while creating user: ' . $e->getMessage()); } + Minz_Translate::reset($currentLanguage); + if ($ok) { return true; } else { $info = empty($stm) ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error : ' . $info[2]); + Minz_Log::error('SQL error: ' . $info[2]); return false; } } diff --git a/app/views/user/manage.phtml b/app/views/user/manage.phtml index aab3aa4c4..a32247d14 100644 --- a/app/views/user/manage.phtml +++ b/app/views/user/manage.phtml @@ -3,7 +3,7 @@
-
+ @@ -30,7 +30,7 @@
- +
-- cgit v1.2.3 From 05cabe99ae48a187a77e0246dfffc60f2434b5e5 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 5 Oct 2016 00:39:54 +0200 Subject: Take better advantage of other users refresh --- app/Controllers/feedController.php | 18 ++++++++++++++++-- app/Models/EntryDAO.php | 7 +++++-- app/Models/Feed.php | 4 ++++ app/Models/FeedDAO.php | 8 ++++---- 4 files changed, 29 insertions(+), 8 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index c2a22aeb3..15f373833 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -242,7 +242,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feeds[] = $feed; } } else { - $feeds = $feedDAO->listFeedsOrderUpdate(FreshRSS_Context::$user_conf->ttl_default); + $feeds = $feedDAO->listFeedsOrderUpdate(-1); } // Calculate date of oldest entries we accept in DB. @@ -266,6 +266,20 @@ class FreshRSS_feed_Controller extends Minz_ActionController { continue; //When PubSubHubbub is used, do not pull refresh so often } + $mtime = 0; + $ttl = $feed->ttl(); + if ($ttl == -1) { + continue; //Feed refresh is disabled + } + if (feed->lastUpdate() < time() - ($ttl == -2 ? FreshRSS_Context::$user_conf->ttl_default : $ttl)) { + //Too early to refresh from source, but check if the feed was already updated by another user + $mtime = feed->cacheModifiedTime(); + if (feed->lastUpdate() >= $mtime + 10) { + continue; //Nothing newer from other users + } + Minz_Log::debug($feed->url() . ' was recently updated by another user'); + } + if (!$feed->lock()) { Minz_Log::notice('Feed already being actualized: ' . $feed->url()); continue; @@ -358,7 +372,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $entryDAO->addEntry($entry->toArray()); } } - $entryDAO->updateLastSeen($feed->id(), $oldGuids); + $entryDAO->updateLastSeen($feed->id(), $oldGuids, $mtime); } if ($feed_history >= 0 && rand(0, 30) === 1) { diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 63565e73a..43ac21f9e 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -707,13 +707,16 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } - public function updateLastSeen($id_feed, $guids) { + public function updateLastSeen($id_feed, $guids, $mtime = 0) { if (count($guids) < 1) { return 0; } $sql = 'UPDATE `' . $this->prefix . 'entry` SET `lastSeen`=? WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)'; $stm = $this->bd->prepare($sql); - $values = array(time(), $id_feed); + if ($mtime <= 0) { + $mtime = time(); + } + $values = array($mtime, $id_feed); $values = array_merge($values, $guids); if ($stm && $stm->execute($values)) { return $stm->rowCount(); diff --git a/app/Models/Feed.php b/app/Models/Feed.php index f2f345662..f435620c8 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -340,6 +340,10 @@ class FreshRSS_Feed extends Minz_Model { $this->entries = $entries; } + function cacheModifiedTime() { + return @filemtime(CACHE_PATH . '/' . md5($this->url) . '.spc'); + } + function lock() { $this->lockPath = TMP_PATH . '/' . $this->hash() . '.freshrss.lock'; if (file_exists($this->lockPath) && ((time() - @filemtime($this->lockPath)) > 3600)) { diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 6e6d8857b..ad3cb0c2e 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -222,13 +222,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return $feedCategoryNames; } + /** + * Use $defaultCacheDuration == -1 to return all feeds, without filtering them by TTL. + */ public function listFeedsOrderUpdate($defaultCacheDuration = 3600) { - if ($defaultCacheDuration < 0) { - $defaultCacheDuration = 2147483647; - } $sql = 'SELECT id, url, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, keep_history, ttl ' . 'FROM `' . $this->prefix . 'feed` ' - . 'WHERE ttl <> -1 AND `lastUpdate` < (' . (time() + 60) . '-(CASE WHEN ttl=-2 THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) ' + . ($defaultCacheDuration < 0 ? '' : '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); if (!($stm && $stm->execute())) { -- cgit v1.2.3 From 9e34d7275eb936e1a08b872aca7adbce5a5674c5 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 5 Oct 2016 00:44:14 +0200 Subject: Typo --- app/Controllers/feedController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 15f373833..d85a53446 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -271,10 +271,10 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if ($ttl == -1) { continue; //Feed refresh is disabled } - if (feed->lastUpdate() < time() - ($ttl == -2 ? FreshRSS_Context::$user_conf->ttl_default : $ttl)) { - //Too early to refresh from source, but check if the feed was already updated by another user - $mtime = feed->cacheModifiedTime(); - if (feed->lastUpdate() >= $mtime + 10) { + if ($feed->lastUpdate() < time() + 60 - ($ttl == -2 ? FreshRSS_Context::$user_conf->ttl_default : $ttl)) { + //Too early to refresh from source, but check whether the feed was updated by another user + $mtime = $feed->cacheModifiedTime(); + if ($feed->lastUpdate() >= $mtime + 10) { continue; //Nothing newer from other users } Minz_Log::debug($feed->url() . ' was recently updated by another user'); -- cgit v1.2.3 From fe65eec5bbdaee37177e3673e31e241b1f1b938d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 5 Oct 2016 17:48:24 +0200 Subject: Better multiuser update --- app/Controllers/feedController.php | 9 +++++---- app/Models/FeedDAO.php | 10 +++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index d85a53446..dadc8aa83 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -271,13 +271,14 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if ($ttl == -1) { continue; //Feed refresh is disabled } - if ($feed->lastUpdate() < time() + 60 - ($ttl == -2 ? FreshRSS_Context::$user_conf->ttl_default : $ttl)) { + if ($feed->lastUpdate() + 10 >= time() - ($ttl == -2 ? FreshRSS_Context::$user_conf->ttl_default : $ttl)) { //Too early to refresh from source, but check whether the feed was updated by another user $mtime = $feed->cacheModifiedTime(); - if ($feed->lastUpdate() >= $mtime + 10) { + if ($feed->lastUpdate() + 10 >= $mtime) { continue; //Nothing newer from other users } - Minz_Log::debug($feed->url() . ' was recently updated by another user'); + //Minz_Log::debug($feed->url() . ' was updated at ' . date('c', $mtime) . ' by another user'); + //Will take advantage of the newer cache } if (!$feed->lock()) { @@ -391,7 +392,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - $feedDAO->updateLastUpdate($feed->id(), 0, $entryDAO->inTransaction()); + $feedDAO->updateLastUpdate($feed->id(), false, $entryDAO->inTransaction(), $mtime); if ($entryDAO->inTransaction()) { $entryDAO->commit(); } diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index ad3cb0c2e..c680d270c 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -82,7 +82,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } - public function updateLastUpdate($id, $inError = 0, $updateCache = true) { + public function updateLastUpdate($id, $inError = false, $updateCache = true, $mtime = 0) { 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=`' . $this->prefix . 'feed`.id),' @@ -95,9 +95,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . 'WHERE id=?'; } + if ($mtime <= 0) { + $mtime = time(); + } + $values = array( - time(), - $inError, + $mtime, + $inError ? 1 : 0, $id, ); -- cgit v1.2.3 From ca4dcca5b20c3e2e6da1293746cd46674d97c710 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 7 Oct 2016 21:46:48 +0200 Subject: PSHB bugs Introduced by https://github.com/FreshRSS/FreshRSS/pull/1280 --- app/Controllers/feedController.php | 3 ++- p/api/pshb.php | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index dadc8aa83..8751d2fff 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -271,7 +271,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if ($ttl == -1) { continue; //Feed refresh is disabled } - if ($feed->lastUpdate() + 10 >= time() - ($ttl == -2 ? FreshRSS_Context::$user_conf->ttl_default : $ttl)) { + if ((!$simplePiePush) && (!$feed_id) && + ($feed->lastUpdate() + 10 >= time() - ($ttl == -2 ? FreshRSS_Context::$user_conf->ttl_default : $ttl))) { //Too early to refresh from source, but check whether the feed was updated by another user $mtime = $feed->cacheModifiedTime(); if ($feed->lastUpdate() + 10 >= $mtime) { diff --git a/p/api/pshb.php b/p/api/pshb.php index 94a0068ed..e9b66b167 100644 --- a/p/api/pshb.php +++ b/p/api/pshb.php @@ -116,6 +116,7 @@ foreach ($users as $userFilename) { Minz_Configuration::register('user', join_path(USERS_PATH, $username, 'config.php'), join_path(USERS_PATH, '_', 'config.default.php')); + new Minz_ModelPdo($username); //TODO: FIXME: Quick-fix while waiting for a better FreshRSS() constructor/init FreshRSS_Context::init(); list($updated_feeds, $feed) = FreshRSS_feed_Controller::actualizeFeed(0, $self, false, $simplePie); if ($updated_feeds > 0) { -- cgit v1.2.3 From cbb6c26db79b5a74e588110d2cf8fc8cf9137ab8 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 10 Oct 2016 22:07:58 +0200 Subject: Fix share POST https://github.com/FreshRSS/FreshRSS/issues/1289 --- app/Controllers/configureController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 147a2fe06..e73f106a6 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -139,7 +139,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { */ public function sharingAction() { if (Minz_Request::isPost()) { - $params = Minz_Request::fetchGET(); + $params = Minz_Request::fetchPOST(); FreshRSS_Context::$user_conf->sharing = $params['share']; FreshRSS_Context::$user_conf->save(); invalidateHttpCache(); -- cgit v1.2.3 From a0127d980c37b69c48db6982733fac60eaf8601c Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 15 Oct 2016 01:27:39 +0200 Subject: Fix bug JSON import duplicates https://github.com/FreshRSS/FreshRSS/issues/1312 --- app/Controllers/importExportController.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'app/Controllers') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 9dfc479f0..c2e68f465 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -360,6 +360,14 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } } + $newGuids = array(); + foreach ($article_object['items'] as $item) { + $newGuids[] = $item['id']; + } + // For this feed, check existing GUIDs already in database. + $existingHashForGuids = $entryDAO->listHashForFeedGuids($feed->id(), $newGuids); + unset($newGuids); + // Then, articles are imported. $this->entryDAO->beginTransaction(); foreach ($article_object['items'] as $item) { @@ -395,7 +403,11 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } $values = $entry->toArray(); - $id = $this->entryDAO->addEntry($values); + if (isset($existingHashForGuids[$entry->guid()])) { + $id = $this->entryDAO->updateEntry($values); + } else { + $id = $this->entryDAO->addEntry($values); + } if (!$error && ($id === false)) { $error = true; -- cgit v1.2.3 From 50f1e027e3e3a77abf367f9d4559b51573b1b295 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 15 Oct 2016 14:18:23 +0200 Subject: Fix import bug --- app/Controllers/importExportController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/Controllers') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index c2e68f465..d36c57deb 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -365,7 +365,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $newGuids[] = $item['id']; } // For this feed, check existing GUIDs already in database. - $existingHashForGuids = $entryDAO->listHashForFeedGuids($feed->id(), $newGuids); + $existingHashForGuids = $this->entryDAO->listHashForFeedGuids($feed->id(), $newGuids); unset($newGuids); // Then, articles are imported. -- cgit v1.2.3 From 56f66f4b6168276ca449d18a440b6958c6817088 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 15 Oct 2016 14:58:40 +0200 Subject: Remove superflous category request A category request was systematically done, although it is not always needed. --- app/Controllers/indexController.php | 5 +++++ app/Models/Context.php | 15 +++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 4e2717920..5ca147ff3 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -159,6 +159,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { * - hours (default: 0) */ private function updateContext() { + if (empty(FreshRSS_Context::$categories)) { + $catDAO = new FreshRSS_CategoryDAO(); + FreshRSS_Context::$categories = $catDAO->listCategories(); + } + // Update number of read / unread variables. $entryDAO = FreshRSS_Factory::createEntryDao(); FreshRSS_Context::$total_starred = $entryDAO->countUnreadReadFavorites(); diff --git a/app/Models/Context.php b/app/Models/Context.php index 889ddab1e..fe4fa6281 100644 --- a/app/Models/Context.php +++ b/app/Models/Context.php @@ -48,9 +48,6 @@ class FreshRSS_Context { // Init configuration. self::$system_conf = Minz_Configuration::get('system'); self::$user_conf = Minz_Configuration::get('user'); - - $catDAO = new FreshRSS_CategoryDAO(); - self::$categories = $catDAO->listCategories(); } /** @@ -142,6 +139,11 @@ class FreshRSS_Context { $id = substr($get, 2); $nb_unread = 0; + if (empty(self::$categories)) { + $catDAO = new FreshRSS_CategoryDAO(); + self::$categories = $catDAO->listCategories(); + } + switch($type) { case 'a': self::$current_get['all'] = true; @@ -203,11 +205,16 @@ class FreshRSS_Context { /** * Set the value of $next_get attribute. */ - public static function _nextGet() { + private static function _nextGet() { $get = self::currentGet(); // By default, $next_get == $get self::$next_get = $get; + if (empty(self::$categories)) { + $catDAO = new FreshRSS_CategoryDAO(); + self::$categories = $catDAO->listCategories(); + } + if (self::$user_conf->onread_jump_next && strlen($get) > 2) { $another_unread_id = ''; $found_current_get = false; -- cgit v1.2.3 From 1893fc61e0e576519f878267fd877247445d1055 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 20 Oct 2016 01:19:59 +0200 Subject: guid and urls should not contain low/high characters It looks like SimplePie does not always filter everything Having a character not in latin1 would create MySQL collate errors --- app/Controllers/feedController.php | 4 +++- app/Controllers/importExportController.php | 4 +++- app/Models/EntryDAO.php | 4 ++++ app/Models/FeedDAO.php | 10 ++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 8751d2fff..d4a6c9955 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -314,7 +314,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if (count($entries) > 0) { $newGuids = array(); foreach ($entries as $entry) { - $newGuids[] = $entry->guid(); + $guid = $entry->guid(); + $guid = filter_var($guid, FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + $newGuids[] = $guid; } // For this feed, check existing GUIDs already in database. $existingHashForGuids = $entryDAO->listHashForFeedGuids($feed->id(), $newGuids); diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index d36c57deb..e380323c4 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -362,7 +362,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $newGuids = array(); foreach ($article_object['items'] as $item) { - $newGuids[] = $item['id']; + $guid = $item['id']; + $guid = filter_var($guid, FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + $newGuids[] = $guid; } // For this feed, check existing GUIDs already in database. $existingHashForGuids = $this->entryDAO->listHashForFeedGuids($feed->id(), $newGuids); diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 3959cb191..466e6f5a3 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -123,6 +123,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } $this->addEntryPrepared->bindParam(':id', $valuesTmp['id']); $valuesTmp['guid'] = substr($valuesTmp['guid'], 0, 760); + $valuesTmp['guid'] = filter_var($valuesTmp['guid'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); $this->addEntryPrepared->bindParam(':guid', $valuesTmp['guid']); $valuesTmp['title'] = substr($valuesTmp['title'], 0, 255); $this->addEntryPrepared->bindParam(':title', $valuesTmp['title']); @@ -130,6 +131,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $this->addEntryPrepared->bindParam(':author', $valuesTmp['author']); $this->addEntryPrepared->bindParam(':content', $valuesTmp['content']); $valuesTmp['link'] = substr($valuesTmp['link'], 0, 1023); + $valuesTmp['link'] = filter_var($valuesTmp['link'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); $this->addEntryPrepared->bindParam(':link', $valuesTmp['link']); $this->addEntryPrepared->bindParam(':date', $valuesTmp['date'], PDO::PARAM_INT); $valuesTmp['lastSeen'] = time(); @@ -190,6 +192,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $this->updateEntryPrepared->bindParam(':author', $valuesTmp['author']); $this->updateEntryPrepared->bindParam(':content', $valuesTmp['content']); $valuesTmp['link'] = substr($valuesTmp['link'], 0, 1023); + $valuesTmp['link'] = filter_var($valuesTmp['link'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); $this->updateEntryPrepared->bindParam(':link', $valuesTmp['link']); $this->updateEntryPrepared->bindParam(':date', $valuesTmp['date'], PDO::PARAM_INT); $valuesTmp['lastSeen'] = time(); @@ -689,6 +692,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if (count($guids) < 1) { return array(); } + $guids = array_unique($guids); $sql = 'SELECT guid, ' . $this->sqlHexEncode('hash') . ' AS hex_hash FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)'; $stm = $this->bd->prepare($sql); $values = array($id_feed); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index c680d270c..33e19d750 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -5,6 +5,9 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $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); + $valuesTmp['url'] = filter_var($valuesTmp['url'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + $valuesTmp['website'] = filter_var($valuesTmp['website'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + $values = array( substr($valuesTmp['url'], 0, 511), $valuesTmp['category'], @@ -55,6 +58,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function updateFeed($id, $valuesTmp) { + if (isset($valuesTmp['url'])) { + $valuesTmp['url'] = filter_var($valuesTmp['url'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + } + if (isset($valuesTmp['website'])) { + $valuesTmp['website'] = filter_var($valuesTmp['website'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + } + $set = ''; foreach ($valuesTmp as $key => $v) { $set .= $key . '=?, '; -- cgit v1.2.3 From 7f2b0439ec4158ee7d78571d60e9bcc995e87cac Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 20 Oct 2016 01:38:23 +0200 Subject: Extract function safe_ascii() --- app/Controllers/feedController.php | 4 +--- app/Controllers/importExportController.php | 4 +--- app/Models/EntryDAO.php | 6 +++--- app/Models/FeedDAO.php | 8 ++++---- lib/lib_rss.php | 3 +++ 5 files changed, 12 insertions(+), 13 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index d4a6c9955..ed3229687 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -314,9 +314,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if (count($entries) > 0) { $newGuids = array(); foreach ($entries as $entry) { - $guid = $entry->guid(); - $guid = filter_var($guid, FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); - $newGuids[] = $guid; + $newGuids[] = safe_ascii($entry->guid()); } // For this feed, check existing GUIDs already in database. $existingHashForGuids = $entryDAO->listHashForFeedGuids($feed->id(), $newGuids); diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index e380323c4..a1f789805 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -362,9 +362,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $newGuids = array(); foreach ($article_object['items'] as $item) { - $guid = $item['id']; - $guid = filter_var($guid, FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); - $newGuids[] = $guid; + $newGuids[] = safe_ascii($item['id']); } // For this feed, check existing GUIDs already in database. $existingHashForGuids = $this->entryDAO->listHashForFeedGuids($feed->id(), $newGuids); diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 466e6f5a3..4c6a9ea20 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -123,7 +123,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } $this->addEntryPrepared->bindParam(':id', $valuesTmp['id']); $valuesTmp['guid'] = substr($valuesTmp['guid'], 0, 760); - $valuesTmp['guid'] = filter_var($valuesTmp['guid'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + $valuesTmp['guid'] = safe_ascii($valuesTmp['guid']); $this->addEntryPrepared->bindParam(':guid', $valuesTmp['guid']); $valuesTmp['title'] = substr($valuesTmp['title'], 0, 255); $this->addEntryPrepared->bindParam(':title', $valuesTmp['title']); @@ -131,7 +131,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $this->addEntryPrepared->bindParam(':author', $valuesTmp['author']); $this->addEntryPrepared->bindParam(':content', $valuesTmp['content']); $valuesTmp['link'] = substr($valuesTmp['link'], 0, 1023); - $valuesTmp['link'] = filter_var($valuesTmp['link'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + $valuesTmp['link'] = safe_ascii($valuesTmp['link']); $this->addEntryPrepared->bindParam(':link', $valuesTmp['link']); $this->addEntryPrepared->bindParam(':date', $valuesTmp['date'], PDO::PARAM_INT); $valuesTmp['lastSeen'] = time(); @@ -192,7 +192,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $this->updateEntryPrepared->bindParam(':author', $valuesTmp['author']); $this->updateEntryPrepared->bindParam(':content', $valuesTmp['content']); $valuesTmp['link'] = substr($valuesTmp['link'], 0, 1023); - $valuesTmp['link'] = filter_var($valuesTmp['link'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + $valuesTmp['link'] = safe_ascii($valuesTmp['link']); $this->updateEntryPrepared->bindParam(':link', $valuesTmp['link']); $this->updateEntryPrepared->bindParam(':date', $valuesTmp['date'], PDO::PARAM_INT); $valuesTmp['lastSeen'] = time(); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 33e19d750..b21f19b66 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -5,8 +5,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $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); - $valuesTmp['url'] = filter_var($valuesTmp['url'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); - $valuesTmp['website'] = filter_var($valuesTmp['website'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + $valuesTmp['url'] = safe_ascii($valuesTmp['url']); + $valuesTmp['website'] = safe_ascii($valuesTmp['website']); $values = array( substr($valuesTmp['url'], 0, 511), @@ -59,10 +59,10 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function updateFeed($id, $valuesTmp) { if (isset($valuesTmp['url'])) { - $valuesTmp['url'] = filter_var($valuesTmp['url'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + $valuesTmp['url'] = safe_ascii($valuesTmp['url']); } if (isset($valuesTmp['website'])) { - $valuesTmp['website'] = filter_var($valuesTmp['website'], FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); + $valuesTmp['website'] = safe_ascii($valuesTmp['website']); } $set = ''; diff --git a/lib/lib_rss.php b/lib/lib_rss.php index b18512484..75046fd54 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -83,6 +83,9 @@ function checkUrl($url) { } } +function safe_ascii($text) { + return filter_var($text, FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH); +} /** * Test if a given server address is publicly accessible. -- cgit v1.2.3 From e1f214e9e2e09a83a9920e33fbf617dfe48fbb7e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 22 Oct 2016 12:58:06 +0200 Subject: CLI list-users and create-user https://github.com/FreshRSS/FreshRSS/issues/1095 https://github.com/FreshRSS/FreshRSS/issues/1090 --- app/Controllers/userController.php | 125 +++++++++++++++++++------------------ app/Models/Context.php | 2 +- app/Models/Feed.php | 2 +- app/actualize_script.php | 4 +- cli/.htaccess | 3 + cli/_cli.php | 39 ++++++++++++ cli/create-user.php | 41 ++++++++++++ cli/index.html | 13 ++++ cli/list-users.php | 14 +++++ lib/lib_rss.php | 9 ++- 10 files changed, 184 insertions(+), 68 deletions(-) create mode 100644 cli/.htaccess create mode 100644 cli/_cli.php create mode 100644 cli/create-user.php create mode 100644 cli/index.html create mode 100644 cli/list-users.php (limited to 'app/Controllers') diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index c259ffde9..f880b951d 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -24,6 +24,16 @@ class FreshRSS_user_Controller extends Minz_ActionController { } } + private static function hashPassword($passwordPlain) { + if (!function_exists('password_hash')) { + include_once(LIB_PATH . '/password_compat.php'); + } + $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST)); + $passwordPlain = ''; + $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js + return $passwordHash == '' ? '' : $passwordHash; + } + /** * This action displays the user profile page. */ @@ -41,12 +51,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { if ($passwordPlain != '') { Minz_Request::_param('newPasswordPlain'); //Discard plain-text password ASAP $_POST['newPasswordPlain'] = ''; - if (!function_exists('password_hash')) { - include_once(LIB_PATH . '/password_compat.php'); - } - $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST)); - $passwordPlain = ''; - $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js + $passwordHash = self::hashPassword($passwordPlain); $ok &= ($passwordHash != ''); FreshRSS_Context::$user_conf->passwordHash = $passwordHash; } @@ -54,12 +59,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $passwordPlain = Minz_Request::param('apiPasswordPlain', '', true); if ($passwordPlain != '') { - if (!function_exists('password_hash')) { - include_once(LIB_PATH . '/password_compat.php'); - } - $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST)); - $passwordPlain = ''; - $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js + $passwordHash = self::hashPassword($passwordPlain); $ok &= ($passwordHash != ''); FreshRSS_Context::$user_conf->apiPasswordHash = $passwordHash; } @@ -99,6 +99,53 @@ class FreshRSS_user_Controller extends Minz_ActionController { $this->view->size_user = $entryDAO->size(); } + public static function createUser($new_user_name, $passwordPlain, $apiPasswordPlain, $userConfig = array()) { + if (!is_array($userConfig)) { + $userConfig = array(); + } + + $ok = ($new_user_name != '') && ctype_alnum($new_user_name); + + if ($ok) { + $languages = Minz_Translate::availableLanguages(); + if (empty($userConfig['language']) || !in_array($userConfig['language'], $languages)) { + $userConfig['language'] = 'en'; + } + + $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 + + $configPath = join_path(DATA_PATH, 'users', $new_user_name, 'config.php'); + $ok &= !file_exists($configPath); + } + if ($ok) { + $passwordHash = ''; + if ($passwordPlain != '') { + $passwordHash = self::hashPassword($passwordPlain); + $ok &= ($passwordHash != ''); + } + + $apiPasswordHash = ''; + if ($apiPasswordPlain != '') { + $apiPasswordHash = self::hashPassword($apiPasswordPlain); + $ok &= ($apiPasswordHash != ''); + } + } + if ($ok) { + mkdir(join_path(DATA_PATH, 'users', $new_user_name)); + $userConfig['passwordHash'] = $passwordHash; + $userConfig['apiPasswordHash'] = $apiPasswordHash; + $ok &= (file_put_contents($configPath, "createUser($new_user_name, $userConfig['language']); + } + return $ok; + } + /** * This action creates a new user. * @@ -116,57 +163,13 @@ class FreshRSS_user_Controller extends Minz_ActionController { FreshRSS_Auth::hasAccess('admin') || !max_registrations_reached() )) { - $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); - $languages = Minz_Translate::availableLanguages(); - if (!in_array($new_user_language, $languages)) { - $new_user_language = FreshRSS_Context::$user_conf->language; - } - $new_user_name = Minz_Request::param('new_user_name'); - $ok = ($new_user_name != '') && ctype_alnum($new_user_name); - - if ($ok) { - $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 + $passwordPlain = Minz_Request::param('new_user_passwordPlain', '', true); + $new_user_language = Minz_Request::param('new_user_language', FreshRSS_Context::$user_conf->language); - $configPath = join_path(DATA_PATH, 'users', $new_user_name, 'config.php'); - $ok &= !file_exists($configPath); - } - if ($ok) { - $passwordPlain = Minz_Request::param('new_user_passwordPlain', '', true); - $passwordHash = ''; - if ($passwordPlain != '') { - Minz_Request::_param('new_user_passwordPlain'); //Discard plain-text password ASAP - $_POST['new_user_passwordPlain'] = ''; - if (!function_exists('password_hash')) { - include_once(LIB_PATH . '/password_compat.php'); - } - $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST)); - $passwordPlain = ''; - $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js - $ok &= ($passwordHash != ''); - } - if (empty($passwordHash)) { - $passwordHash = ''; - } - } - if ($ok) { - mkdir(join_path(DATA_PATH, 'users', $new_user_name)); - $config_array = array( - 'language' => $new_user_language, - 'passwordHash' => $passwordHash, - ); - $ok &= (file_put_contents($configPath, "createUser($new_user_name, $new_user_language); - } + $ok = self::createUser($new_user_name, $passwordPlain, '', array('language' => $new_user_language)); + Minz_Request::_param('new_user_passwordPlain'); //Discard plain-text password ASAP + $_POST['new_user_passwordPlain'] = ''; invalidateHttpCache(); $notif = array( diff --git a/app/Models/Context.php b/app/Models/Context.php index fe4fa6281..fd0e79fc1 100644 --- a/app/Models/Context.php +++ b/app/Models/Context.php @@ -37,7 +37,7 @@ class FreshRSS_Context { public static $id_max = ''; public static $sinceHours = 0; - public static $isCron = false; + public static $isCli = false; /** * Initialize the context. diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 55c2db4d6..97cb1c47e 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -141,7 +141,7 @@ class FreshRSS_Feed extends Minz_Model { if (!file_exists($txt)) { file_put_contents($txt, $url); } - if (FreshRSS_Context::$isCron) { + if (FreshRSS_Context::$isCli) { $ico = $favicons_dir . $this->hash() . '.ico'; $ico_mtime = @filemtime($ico); $txt_mtime = @filemtime($txt); diff --git a/app/actualize_script.php b/app/actualize_script.php index 78712d721..deaa1bf7c 100755 --- a/app/actualize_script.php +++ b/app/actualize_script.php @@ -28,13 +28,13 @@ $app = new FreshRSS(); $system_conf = Minz_Configuration::get('system'); $system_conf->auth_type = 'none'; // avoid necessity to be logged in (not saved!) -FreshRSS_Context::$isCron = true; +FreshRSS_Context::$isCli = true; // Create the list of users to actualize. // Users are processed in a random order but always start with admin $users = listUsers(); shuffle($users); -if ($system_conf->default_user !== ''){ +if ($system_conf->default_user !== '') { array_unshift($users, $system_conf->default_user); $users = array_unique($users); } diff --git a/cli/.htaccess b/cli/.htaccess new file mode 100644 index 000000000..9e768397d --- /dev/null +++ b/cli/.htaccess @@ -0,0 +1,3 @@ +Order Allow,Deny +Deny from all +Satisfy all diff --git a/cli/_cli.php b/cli/_cli.php new file mode 100644 index 000000000..cb6d8ec32 --- /dev/null +++ b/cli/_cli.php @@ -0,0 +1,39 @@ + empty($options['language']) ? '' : $options['language'], + 'token' => empty($options['token']) ? '' : $options['token'], + )); + +invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); + +echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; +exit($ok ? 0 : 1); diff --git a/cli/index.html b/cli/index.html new file mode 100644 index 000000000..85faaa37e --- /dev/null +++ b/cli/index.html @@ -0,0 +1,13 @@ + + + + + +Redirection + + + + +

Redirection

+ + diff --git a/cli/list-users.php b/cli/list-users.php new file mode 100644 index 000000000..cc1cf5269 --- /dev/null +++ b/cli/list-users.php @@ -0,0 +1,14 @@ +#!/usr/bin/php +default_user !== '') { + array_unshift($users, $system_conf->default_user); + $users = array_unique($users); +} + +foreach ($users as $user) { + echo $user, "\n"; +} diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 75046fd54..143b55bee 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -282,9 +282,12 @@ function uSecString() { return str_pad($t['usec'], 6, '0'); } -function invalidateHttpCache() { - Minz_Session::_param('touch', uTimeString()); - return touch(join_path(DATA_PATH, 'users', Minz_Session::param('currentUser', '_'), 'log.txt')); +function invalidateHttpCache($username = '') { + if (($username == '') || (!ctype_alnum($username))) { + Minz_Session::_param('touch', uTimeString()); + $username = Minz_Session::param('currentUser', '_'); + } + return touch(join_path(DATA_PATH, 'users', $username, 'log.txt')); } function listUsers() { -- cgit v1.2.3 From 5b1b43ab57da6a7bc1599c224d47455b2e56d53d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 22 Oct 2016 20:32:16 +0200 Subject: CLI delete user https://github.com/FreshRSS/FreshRSS/issues/1095 --- app/Controllers/userController.php | 39 ++++++++++++++++++++++---------------- cli/delete-user.php | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 16 deletions(-) create mode 100755 cli/delete-user.php (limited to 'app/Controllers') diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index f880b951d..2f04c7a1d 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -186,6 +186,27 @@ class FreshRSS_user_Controller extends Minz_ActionController { Minz_Request::forward($redirect_url, true); } + public static function deleteUser($username) { + $db = FreshRSS_Context::$system_conf->db; + require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); + + $ok = ctype_alnum($username); + if ($ok) { + $default_user = FreshRSS_Context::$system_conf->default_user; + $ok &= (strcasecmp($username, $default_user) !== 0); //It is forbidden to delete the default user + } + $user_data = join_path(DATA_PATH, 'users', $username); + if ($ok) { + $ok &= is_dir($user_data); + } + if ($ok) { + $userDAO = new FreshRSS_UserDAO(); + $ok &= $userDAO->deleteUser($username); + $ok &= recursive_unlink($user_data); + } + return $ok; + } + /** * This action delete an existing user. * @@ -207,16 +228,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { FreshRSS_Auth::hasAccess('admin') || $self_deletion )) { - $db = FreshRSS_Context::$system_conf->db; - require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); - - $ok = ctype_alnum($username); - $user_data = join_path(DATA_PATH, 'users', $username); - - if ($ok) { - $default_user = FreshRSS_Context::$system_conf->default_user; - $ok &= (strcasecmp($username, $default_user) !== 0); //It is forbidden to delete the default user - } + $ok = true; if ($ok && $self_deletion) { // We check the password if it's a self-destruction $nonce = Minz_Session::param('nonce'); @@ -228,12 +240,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { ); } if ($ok) { - $ok &= is_dir($user_data); - } - if ($ok) { - $userDAO = new FreshRSS_UserDAO(); - $ok &= $userDAO->deleteUser($username); - $ok &= recursive_unlink($user_data); + $ok &= self::deleteUser($username); } if ($ok && $self_deletion) { FreshRSS_Auth::removeAccess(); diff --git a/cli/delete-user.php b/cli/delete-user.php new file mode 100755 index 000000000..46332fe34 --- /dev/null +++ b/cli/delete-user.php @@ -0,0 +1,33 @@ +#!/usr/bin/php +default_user) === 0) { + fail('FreshRSS error: default user must not be deleted: “' . $username . '”'); +} + +echo 'FreshRSS deleting user “', $username, "”…\n"; + +$ok = FreshRSS_user_Controller::deleteUser($username); + +invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); + +echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; +exit($ok ? 0 : 1); -- cgit v1.2.3 From 1b8eb6c7e732f1eda4fc8f22e847b363b016f857 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 23 Oct 2016 01:46:14 +0200 Subject: CLI import ZIP/OPML/JSON for user https://github.com/FreshRSS/FreshRSS/issues/1095 https://github.com/FreshRSS/FreshRSS/issues/851 --- CHANGELOG.md | 2 +- README.fr.md | 2 +- README.md | 2 +- app/Controllers/categoryController.php | 5 +- app/Controllers/feedController.php | 6 +- app/Controllers/importExportController.php | 273 ++++++++++++++++++----------- app/Exceptions/ZipException.php | 14 ++ app/Exceptions/ZipMissingException.php | 4 + app/Models/CategoryDAO.php | 11 +- app/i18n/cz/feedback.php | 6 +- app/i18n/cz/sub.php | 4 +- app/i18n/de/feedback.php | 6 +- app/i18n/de/sub.php | 2 +- app/i18n/en/feedback.php | 6 +- app/i18n/en/sub.php | 4 +- app/i18n/fr/feedback.php | 6 +- app/i18n/fr/sub.php | 4 +- app/i18n/it/feedback.php | 6 +- app/i18n/it/sub.php | 4 +- app/i18n/nl/feedback.php | 6 +- app/i18n/nl/sub.php | 4 +- app/i18n/ru/feedback.php | 6 +- app/i18n/ru/sub.php | 4 +- app/i18n/tr/feedback.php | 6 +- app/i18n/tr/sub.php | 4 +- cli/_cli.php | 5 + cli/create-user.php | 17 +- cli/delete-user.php | 3 +- cli/import-for-user.php | 36 ++++ cli/list-users.php | 4 +- lib/Minz/ModelPdo.php | 13 +- 31 files changed, 296 insertions(+), 179 deletions(-) create mode 100644 app/Exceptions/ZipException.php create mode 100644 app/Exceptions/ZipMissingException.php create mode 100644 cli/import-for-user.php (limited to 'app/Controllers') diff --git a/CHANGELOG.md b/CHANGELOG.md index c35b3b578..d24ec0739 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -390,7 +390,7 @@ * Possibility to combine search filters, e.g. `date:2014-05 intitle:FreshRSS intitle:Open great reader #Internet` * Change nav menu with more buttons instead of dropdown menus and add some filters * New system of import / export - * Support OPML, Json (like Google Reader) and Zip archives + * Support OPML, Json (like Google Reader) and ZIP archives * Can export and import articles (specific option for favorites) * Refactor "Origine" theme * Some improvements diff --git a/README.fr.md b/README.fr.md index 3fcedd337..2a2da256d 100644 --- a/README.fr.md +++ b/README.fr.md @@ -34,7 +34,7 @@ Nous sommes une communauté amicale. * Serveur Web Apache2 (recommandé), ou nginx, lighttpd (non testé sur les autres) * PHP 5.3.3+ (PHP 5.4+ recommandé, et PHP 5.5+ pour les performances, et PHP 7+ pour d’encore meilleures performances) * Requis : [DOM](http://php.net/dom), [XML](http://php.net/xml), [PDO_MySQL](http://php.net/pdo-mysql) ou [PDO_SQLite](http://php.net/pdo-sqlite) ou [PDO_PGSQL](http://php.net/pdo-pgsql), [cURL](http://php.net/curl) - * Recommandés : [JSON](http://php.net/json), [GMP](http://php.net/gmp) (pour accès API sur plateformes < 64 bits), [IDN](http://php.net/intl.idn) (pour les noms de domaines internationalisés), [mbstring](http://php.net/mbstring) et/ou [iconv](http://php.net/iconv) (pour conversion d’encodages), [Zip](http://php.net/zip) (pour import/export), [zlib](http://php.net/zlib) (pour les flux compressés) + * Recommandés : [JSON](http://php.net/json), [GMP](http://php.net/gmp) (pour accès API sur plateformes < 64 bits), [IDN](http://php.net/intl.idn) (pour les noms de domaines internationalisés), [mbstring](http://php.net/mbstring) et/ou [iconv](http://php.net/iconv) (pour conversion d’encodages), [ZIP](http://php.net/zip) (pour import/export), [zlib](http://php.net/zlib) (pour les flux compressés) * MySQL 5.5.3+ (recommandé), ou SQLite 3.7.4+, ou PostgreSQL (experimental) * Un navigateur Web récent tel Firefox, Internet Explorer 11 / Edge, Chrome, Opera, Safari. * Fonctionne aussi sur mobile diff --git a/README.md b/README.md index 5c8f586fa..a541d6dea 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ We are a friendly community. * A web server: Apache2 (recommended), nginx, lighttpd (not tested on others) * PHP 5.3.3+ (PHP 5.4+ recommended, and PHP 5.5+ for performance, and PHP 7 for even higher performance) * Required extensions: [DOM](http://php.net/dom), [XML](http://php.net/xml), [PDO_MySQL](http://php.net/pdo-mysql) or [PDO_SQLite](http://php.net/pdo-sqlite) or [PDO_PGSQL](http://php.net/pdo-pgsql), [cURL](http://php.net/curl) - * Recommended extensions: [JSON](http://php.net/json), [GMP](http://php.net/gmp) (for API access on platforms < 64 bits), [IDN](http://php.net/intl.idn) (for Internationalized Domain Names), [mbstring](http://php.net/mbstring) and/or [iconv](http://php.net/iconv) (for charset conversion), [Zip](http://php.net/zip) (for import/export), [zlib](http://php.net/zlib) (for compressed feeds) + * Recommended extensions: [JSON](http://php.net/json), [GMP](http://php.net/gmp) (for API access on platforms < 64 bits), [IDN](http://php.net/intl.idn) (for Internationalized Domain Names), [mbstring](http://php.net/mbstring) and/or [iconv](http://php.net/iconv) (for charset conversion), [ZIP](http://php.net/zip) (for import/export), [zlib](http://php.net/zlib) (for compressed feeds) * MySQL 5.5.3+ (recommended), or SQLite 3.7.4+, or PostgreSQL (experimental) * A recent browser like Firefox, Internet Explorer 11 / Edge, Chrome, Opera, Safari. * Works on mobile diff --git a/app/Controllers/categoryController.php b/app/Controllers/categoryController.php index e65c146de..922f92844 100644 --- a/app/Controllers/categoryController.php +++ b/app/Controllers/categoryController.php @@ -117,7 +117,6 @@ class FreshRSS_category_Controller extends Minz_ActionController { public function deleteAction() { $feedDAO = FreshRSS_Factory::createFeedDao(); $catDAO = new FreshRSS_CategoryDAO(); - $default_category = $catDAO->getDefault(); $url_redirect = array('c' => 'subscription', 'a' => 'index'); if (Minz_Request::isPost()) { @@ -128,11 +127,11 @@ class FreshRSS_category_Controller extends Minz_ActionController { Minz_Request::bad(_t('feedback.sub.category.no_id'), $url_redirect); } - if ($id === $default_category->id()) { + if ($id === FreshRSS_CategoryDAO::defaultCategoryId) { Minz_Request::bad(_t('feedback.sub.category.not_delete_default'), $url_redirect); } - if ($feedDAO->changeCategory($id, $default_category->id()) === false) { + if ($feedDAO->changeCategory($id, FreshRSS_CategoryDAO::defaultCategoryId) === false) { Minz_Request::bad(_t('feedback.sub.category.error'), $url_redirect); } diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index ed3229687..c4115584a 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -40,9 +40,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } if ($cat == null) { $catDAO->checkDefault(); - $cat = $catDAO->getDefault(); } - $cat_id = $cat->id(); + $cat_id = $cat == null ? FreshRSS_CategoryDAO::defaultCategoryId : $cat->id(); $feed = new FreshRSS_Feed($url); //Throws FreshRSS_BadUrl_Exception $feed->_httpAuth($http_auth); @@ -504,8 +503,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } if ($cat_id <= 1) { $catDAO->checkDefault(); - $cat = $catDAO->getDefault(); - $cat_id = $cat->id(); + $cat_id = FreshRSS_CategoryDAO::defaultCategoryId; } $feedDAO = FreshRSS_Factory::createFeedDao(); diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index a1f789805..fbebb2a78 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -29,32 +29,14 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { Minz_View::prependTitle(_t('sub.import_export.title') . ' · '); } - /** - * This action handles import action. - * - * It must be reached by a POST request. - * - * Parameter is: - * - file (default: nothing!) - * Available file types are: zip, json or xml. - */ - public function importAction() { - if (!Minz_Request::isPost()) { - Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); - } - - $file = $_FILES['file']; - $status_file = $file['error']; - - if ($status_file !== 0) { - Minz_Log::warning('File cannot be uploaded. Error code: ' . $status_file); - Minz_Request::bad(_t('feedback.import_export.file_cannot_be_uploaded'), - array('c' => 'importExport', 'a' => 'index')); - } + public function importFile($name, $path, $username = null) { + require_once(LIB_PATH . '/lib_opml.php'); - @set_time_limit(300); + $this->catDAO = new FreshRSS_CategoryDAO($username); + $this->entryDAO = FreshRSS_Factory::createEntryDao($username); + $this->feedDAO = FreshRSS_Factory::createFeedDao($username); - $type_file = $this->guessFileType($file['name']); + $type_file = self::guessFileType($name); $list_files = array( 'opml' => array(), @@ -65,21 +47,17 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // We try to list all files according to their type $list = array(); if ($type_file === 'zip' && extension_loaded('zip')) { - $zip = zip_open($file['tmp_name']); - + $zip = zip_open($path); if (!is_resource($zip)) { // zip_open cannot open file: something is wrong - Minz_Log::warning('Zip archive cannot be imported. Error code: ' . $zip); - Minz_Request::bad(_t('feedback.import_export.zip_error'), - array('c' => 'importExport', 'a' => 'index')); + throw new FreshRSS_Zip_Exception($zip); } - while (($zipfile = zip_read($zip)) !== false) { if (!is_resource($zipfile)) { // zip_entry() can also return an error code! - Minz_Log::warning('Zip file cannot be imported. Error code: ' . $zipfile); + throw new FreshRSS_Zip_Exception($zipfile); } else { - $type_zipfile = $this->guessFileType(zip_entry_name($zipfile)); + $type_zipfile = self::guessFileType(zip_entry_name($zipfile)); if ($type_file !== 'unknown') { $list_files[$type_zipfile][] = zip_entry_read( $zipfile, @@ -88,29 +66,82 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } } } - zip_close($zip); } elseif ($type_file === 'zip') { - // Zip extension is not loaded - Minz_Request::bad(_t('feedback.import_export.no_zip_extension'), - array('c' => 'importExport', 'a' => 'index')); + // ZIP extension is not loaded + throw new FreshRSS_ZipMissing_Exception(); } elseif ($type_file !== 'unknown') { - $list_files[$type_file][] = file_get_contents($file['tmp_name']); + $list_files[$type_file][] = file_get_contents($path); } // Import file contents. // OPML first(so categories and feeds are imported) // Starred articles then so the "favourite" status is already set // And finally all other files. - $error = false; + $ok = true; foreach ($list_files['opml'] as $opml_file) { - $error = $this->importOpml($opml_file); + if (!$this->importOpml($opml_file)) { + $ok = false; + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during OPML import' . "\n"); + } + } } foreach ($list_files['json_starred'] as $article_file) { - $error = $this->importJson($article_file, true); + if (!$this->importJson($article_file, true)) { + $ok = false; + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during JSON stars import' . "\n"); + } + } } foreach ($list_files['json_feed'] as $article_file) { - $error = $this->importJson($article_file); + if (!$this->importJson($article_file)) { + $ok = false; + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during JSON feeds import' . "\n"); + } + } + } + + return $ok; + } + + /** + * This action handles import action. + * + * It must be reached by a POST request. + * + * Parameter is: + * - file (default: nothing!) + * Available file types are: zip, json or xml. + */ + public function importAction() { + if (!Minz_Request::isPost()) { + Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); + } + + $file = $_FILES['file']; + $status_file = $file['error']; + + if ($status_file !== 0) { + Minz_Log::warning('File cannot be uploaded. Error code: ' . $status_file); + Minz_Request::bad(_t('feedback.import_export.file_cannot_be_uploaded'), + array('c' => 'importExport', 'a' => 'index')); + } + + @set_time_limit(300); + + $error = false; + try { + $error = !$this->importFile($file['name'], $file['tmp_name']); + } catch (FreshRSS_ZipMissing_Exception $zme) { + Minz_Request::bad(_t('feedback.import_export.no_zip_extension'), + array('c' => 'importExport', 'a' => 'index')); + } catch (FreshRSS_Zip_Exception $ze) { + Minz_Log::warning('ZIP archive cannot be imported. Error code: ' . $ze->zipErrorCode()); + Minz_Request::bad(_t('feedback.import_export.zip_error'), + array('c' => 'importExport', 'a' => 'index')); } // And finally, we get import status and redirect to the home page @@ -126,7 +157,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * Itis a *very* basic guess file type function. Only based on filename. * That's could be improved but should be enough for what we have to do. */ - private function guessFileType($filename) { + private static function guessFileType($filename) { if (substr_compare($filename, '.zip', -4) === 0) { return 'zip'; } elseif (substr_compare($filename, '.opml', -5) === 0 || @@ -146,15 +177,19 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * This method parses and imports an OPML file. * * @param string $opml_file the OPML file content. - * @return boolean true if an error occured, false else. + * @return boolean false if an error occured, true otherwise. */ private function importOpml($opml_file) { $opml_array = array(); try { $opml_array = libopml_parse_string($opml_file, false); } catch (LibOPML_Exception $e) { - Minz_Log::warning($e->getMessage()); - return true; + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during OPML parsing: ' . $e->getMessage() . "\n"); + } else { + Minz_Log::warning($e->getMessage()); + } + return false; } $this->catDAO->checkDefault(); @@ -167,51 +202,53 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * * @param array $opml_elements an OPML element (body or outline). * @param string $parent_cat the name of the parent category. - * @return boolean true if an error occured, false else. + * @return boolean false if an error occured, true otherwise. */ private function addOpmlElements($opml_elements, $parent_cat = null) { - $error = false; + $ok = true; $nb_feeds = count($this->feedDAO->listFeeds()); $nb_cats = count($this->catDAO->listCategories(false)); $limits = FreshRSS_Context::$system_conf->limits; foreach ($opml_elements as $elt) { - $is_error = false; if (isset($elt['xmlUrl'])) { // If xmlUrl exists, it means it is a feed - if ($nb_feeds >= $limits['max_feeds']) { - Minz_Log::warning(_t('feedback.sub.feed.over_max', - $limits['max_feeds'])); - $is_error = true; - continue; + if (!FreshRSS_Context::$isCli) { + if ($nb_feeds >= $limits['max_feeds']) { + Minz_Log::warning(_t('feedback.sub.feed.over_max', + $limits['max_feeds'])); + $ok = false; + continue; + } } - $is_error = $this->addFeedOpml($elt, $parent_cat); - if (!$is_error) { - $nb_feeds += 1; + if ($this->addFeedOpml($elt, $parent_cat)) { + $nb_feeds++; + } else { + $ok = false; } } else { // No xmlUrl? It should be a category! $limit_reached = ($nb_cats >= $limits['max_categories']); - if ($limit_reached) { - Minz_Log::warning(_t('feedback.sub.category.over_max', - $limits['max_categories'])); + if (!FreshRSS_Context::$isCli) { + if ($limit_reached) { + Minz_Log::warning(_t('feedback.sub.category.over_max', + $limits['max_categories'])); + } + $ok = false; + continue; } - $is_error = $this->addCategoryOpml($elt, $parent_cat, $limit_reached); - if (!$is_error) { - $nb_cats += 1; + if ($this->addCategoryOpml($elt, $parent_cat, $limit_reached)) { + $nb_cats++; + } else { + $ok = false; } } - - if (!$error && $is_error) { - // oops: there is at least one error! - $error = $is_error; - } } - return $error; + return $ok; } /** @@ -219,21 +256,23 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * * @param array $feed_elt an OPML element (must be a feed element). * @param string $parent_cat the name of the parent category. - * @return boolean true if an error occured, false else. + * @return boolean false if an error occured, true otherwise. */ private function addFeedOpml($feed_elt, $parent_cat) { - $default_cat = $this->catDAO->getDefault(); - if (is_null($parent_cat)) { + if ($parent_cat == null) { // This feed has no parent category so we get the default one + $this->catDAO->checkDefault(); + $default_cat = $this->catDAO->getDefault(); $parent_cat = $default_cat->name(); } $cat = $this->catDAO->searchByName($parent_cat); - if (is_null($cat)) { + if ($cat == null) { // If there is not $cat, it means parent category does not exist in // database. // If it happens, take the default category. - $cat = $default_cat; + $this->catDAO->checkDefault(); + $cat = $this->catDAO->getDefault(); } // We get different useful information @@ -259,7 +298,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // Call the extension hook $feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed); - if (!is_null($feed)) { + if ($feed != null) { // addFeedObject checks if feed is already in DB so nothing else to // check here $id = $this->feedDAO->addFeedObject($feed); @@ -268,11 +307,23 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $error = true; } } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::warning($e->getMessage()); + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during OPML feed import: ' . $e->getMessage() . "\n"); + } else { + Minz_Log::warning($e->getMessage()); + } $error = true; } - return $error; + if ($error) { + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during OPML feed import from URL: ' . $url . ' in category ' . $cat->id() . "\n"); + } else { + Minz_Log::warning('Error during OPML feed import from URL: ' . $url . ' in category ' . $cat->id()); + } + } + + return !$error; } /** @@ -282,29 +333,34 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * @param string $parent_cat the name of the parent category. * @param boolean $cat_limit_reached indicates if category limit has been reached. * if yes, category is not added (but we try for feeds!) - * @return boolean true if an error occured, false else. + * @return boolean false if an error occured, true otherwise. */ private function addCategoryOpml($cat_elt, $parent_cat, $cat_limit_reached) { // Create a new Category object - $cat = new FreshRSS_Category(Minz_Helper::htmlspecialchars_utf8($cat_elt['text'])); + $catName = Minz_Helper::htmlspecialchars_utf8($cat_elt['text']); + $cat = new FreshRSS_Category($catName); $error = true; - if (!$cat_limit_reached) { + if (FreshRSS_Context::$isCli || !$cat_limit_reached) { $id = $this->catDAO->addCategoryObject($cat); $error = ($id === false); } + if ($error) { + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during OPML category import from URL: ' . $catName . "\n"); + } else { + Minz_Log::warning('Error during OPML category import from URL: ' . $catName); + } + } 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; - } + $error &= !$this->addOpmlElements($cat_elt['@outlines'], $catName); } - return $error; + return !$error; } /** @@ -312,13 +368,17 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * * @param string $article_file the JSON file content. * @param boolean $starred true if articles from the file must be starred. - * @return boolean true if an error occured, false else. + * @return boolean false if an error occured, true otherwise. */ private function importJson($article_file, $starred = false) { $article_object = json_decode($article_file, true); - if (is_null($article_object)) { - Minz_Log::warning('Try to import a non-JSON file'); - return true; + if ($article_object == null) { + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error trying to import a non-JSON file' . "\n"); + } else { + Minz_Log::warning('Try to import a non-JSON file'); + } + return false; } $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; @@ -337,25 +397,24 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $feed = new FreshRSS_Feed($item['origin'][$key]); $feed = $this->feedDAO->searchByUrl($feed->url()); - if (is_null($feed)) { + if ($feed == null) { // Feed does not exist in DB,we should to try to add it. - if ($nb_feeds >= $limits['max_feeds']) { + if ((!FreshRSS_Context::$isCli) && ($nb_feeds >= $limits['max_feeds'])) { // Oops, no more place! Minz_Log::warning(_t('feedback.sub.feed.over_max', $limits['max_feeds'])); } else { $feed = $this->addFeedJson($item['origin'], $google_compliant); } - if (is_null($feed)) { + if ($feed == null) { // Still null? It means something went wrong. $error = true; } else { - // Nice! Increase the counter. - $nb_feeds += 1; + $nb_feeds++; } } - if (!is_null($feed)) { + if ($feed != null) { $article_to_feed[$item['id']] = $feed->id(); } } @@ -384,7 +443,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if ($google_compliant) { // Remove tags containing "/state/com.google" which are useless. $tags = array_filter($tags, function($var) { - return strpos($var, '/state/com.google') === false; + return strpos($var, '/state/com.google') !== false; }); } @@ -397,7 +456,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $entry->_tags($tags); $entry = Minz_ExtensionManager::callHook('entry_before_insert', $entry); - if (is_null($entry)) { + if ($entry == null) { // An extension has returned a null value, there is nothing to insert. continue; } @@ -415,7 +474,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } $this->entryDAO->commit(); - return $error; + return !$error; } /** @@ -427,8 +486,6 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * else null. */ private function addFeedJson($origin, $google_compliant) { - $default_cat = $this->catDAO->getDefault(); - $return = null; $key = $google_compliant ? 'htmlUrl' : 'feedUrl'; $url = $origin[$key]; @@ -438,13 +495,13 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { try { // Create a Feed object and add it in database. $feed = new FreshRSS_Feed($url); - $feed->_category($default_cat->id()); + $feed->_category(FreshRSS_CategoryDAO::defaultCategoryId); $feed->_name($name); $feed->_website($website); // Call the extension hook $feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed); - if (!is_null($feed)) { + if ($feed != null) { // addFeedObject checks if feed is already in DB so nothing else to // check here. $id = $this->feedDAO->addFeedObject($feed); @@ -455,7 +512,11 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } } } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::warning($e->getMessage()); + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during JSON feed import: ' . $e->getMessage() . "\n"); + } else { + Minz_Log::warning($e->getMessage()); + } } return $return; @@ -503,18 +564,18 @@ 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 a zip archive. + // 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! + # Oops, there is no ZIP extension! Minz_Request::bad(_t('feedback.import_export.export_no_zip_extension'), array('c' => 'importExport', 'a' => 'index')); } } elseif ($nb_files === 1) { // Only one file? Guess its type and export it. $filename = key($export_files); - $type = $this->guessFileType($filename); + $type = self::guessFileType($filename); $this->exportFile('freshrss_' . $filename, $export_files[$filename], $type); } else { // Nothing to do... @@ -555,7 +616,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->view->entries = $this->entryDAO->listWhere( 's', '', FreshRSS_Entry::STATE_ALL, 'ASC', $unread_fav['all'] ); - } elseif ($type == 'feed' && !is_null($feed)) { + } elseif ($type === 'feed' && $feed != null) { $this->view->list_title = _t('sub.import_export.feed_list', $feed->name()); $this->view->type = 'feed/' . $feed->id(); $this->view->entries = $this->entryDAO->listWhere( diff --git a/app/Exceptions/ZipException.php b/app/Exceptions/ZipException.php new file mode 100644 index 000000000..8441daedf --- /dev/null +++ b/app/Exceptions/ZipException.php @@ -0,0 +1,14 @@ +zipErrorCode = $zipErrorCode; + } + + public function zipErrorCode() { + return $this->zipErrorCode; + } +} diff --git a/app/Exceptions/ZipMissingException.php b/app/Exceptions/ZipMissingException.php new file mode 100644 index 000000000..864cc3991 --- /dev/null +++ b/app/Exceptions/ZipMissingException.php @@ -0,0 +1,4 @@ +prefix . 'category`(name) VALUES(?)'; $stm = $this->bd->prepare($sql); @@ -50,7 +53,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } public function deleteCategory($id) { - if ($id <= 1) { + if ($id <= self::defaultCategoryId) { return false; } $sql = 'DELETE FROM `' . $this->prefix . 'category` WHERE id=?'; @@ -120,7 +123,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } public function getDefault() { - $sql = 'SELECT * FROM `' . $this->prefix . 'category` WHERE id=1'; + $sql = 'SELECT * FROM `' . $this->prefix . 'category` WHERE id=' . self::defaultCategoryId; $stm = $this->bd->prepare($sql); $stm->execute(); @@ -134,11 +137,11 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } } public function checkDefault() { - $def_cat = $this->searchById(1); + $def_cat = $this->searchById(self::defaultCategoryId); if ($def_cat == null) { $cat = new FreshRSS_Category(_t('gen.short.default_category')); - $cat->_id(1); + $cat->_id(self::defaultCategoryId); $values = array( 'id' => $cat->id(), diff --git a/app/i18n/cz/feedback.php b/app/i18n/cz/feedback.php index 81302afca..f2bd87c77 100644 --- a/app/i18n/cz/feedback.php +++ b/app/i18n/cz/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s neexistuje', ), 'import_export' => array( - 'export_no_zip_extension' => 'Na serveru není naistalována podpora zip. Zkuste prosím exportovat soubory jeden po druhém.', + 'export_no_zip_extension' => 'Na serveru není naistalována podpora ZIP. Zkuste prosím exportovat soubory jeden po druhém.', 'feeds_imported' => 'Vaše kanály byly naimportovány a nyní budou aktualizovány', 'feeds_imported_with_errors' => 'Vaše kanály byly naimportovány, došlo ale k nějakým chybám', 'file_cannot_be_uploaded' => 'Soubor nelze nahrát!', - 'no_zip_extension' => 'Na serveru není naistalována podpora zip.', - 'zip_error' => 'Během importu zip souboru došlo k chybě.', + 'no_zip_extension' => 'Na serveru není naistalována podpora ZIP.', + 'zip_error' => 'Během importu ZIP souboru došlo k chybě.', ), 'sub' => array( 'actualize' => 'Aktualizovat', diff --git a/app/i18n/cz/sub.php b/app/i18n/cz/sub.php index cea0541e3..274cf16e1 100644 --- a/app/i18n/cz/sub.php +++ b/app/i18n/cz/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Exportovat seznam kanálů (OPML)', 'export_starred' => 'Exportovat oblíbené', 'feed_list' => 'Seznam %s článků', - 'file_to_import' => 'Soubor k importu
(OPML, Json nebo Zip)', - 'file_to_import_no_zip' => 'Soubor k importu
(OPML nebo Json)', + 'file_to_import' => 'Soubor k importu
(OPML, JSON nebo ZIP)', + 'file_to_import_no_zip' => 'Soubor k importu
(OPML nebo JSON)', 'import' => 'Import', 'starred_list' => 'Seznam oblíbených článků', 'title' => 'Import / export', diff --git a/app/i18n/de/feedback.php b/app/i18n/de/feedback.php index f93992982..195083b36 100644 --- a/app/i18n/de/feedback.php +++ b/app/i18n/de/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s existiert nicht', ), 'import_export' => array( - 'export_no_zip_extension' => 'Die Zip-Erweiterung fehlt auf Ihrem Server. Bitte versuchen Sie die Dateien eine nach der anderen zu exportieren.', + 'export_no_zip_extension' => 'Die ZIP-Erweiterung fehlt auf Ihrem Server. Bitte versuchen Sie die Dateien eine nach der anderen zu exportieren.', 'feeds_imported' => 'Ihre Feeds sind importiert worden und werden jetzt aktualisiert', 'feeds_imported_with_errors' => 'Ihre Feeds sind importiert worden, aber es traten einige Fehler auf', 'file_cannot_be_uploaded' => 'Die Datei kann nicht hochgeladen werden!', - 'no_zip_extension' => 'Die Zip-Erweiterung ist auf Ihrem Server nicht vorhanden.', - 'zip_error' => 'Ein Fehler trat während des Zip-Imports auf.', + 'no_zip_extension' => 'Die ZIP-Erweiterung ist auf Ihrem Server nicht vorhanden.', + 'zip_error' => 'Ein Fehler trat während des ZIP-Imports auf.', ), 'sub' => array( 'actualize' => 'Aktualisieren', diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php index 0f05a5635..cc98fd25c 100644 --- a/app/i18n/de/sub.php +++ b/app/i18n/de/sub.php @@ -44,7 +44,7 @@ return array( 'export_opml' => 'Liste der Feeds exportieren (OPML)', 'export_starred' => 'Ihre Favoriten exportieren', 'feed_list' => 'Liste von %s Artikeln', - 'file_to_import' => 'Zu importierende Datei
(OPML, JSON oder Zip)', + 'file_to_import' => 'Zu importierende Datei
(OPML, JSON oder ZIP)', 'file_to_import_no_zip' => 'Zu importierende Datei
(OPML oder JSON)', 'import' => 'Importieren', 'starred_list' => 'Liste der Lieblingsartikel', diff --git a/app/i18n/en/feedback.php b/app/i18n/en/feedback.php index 7ce2ae9cf..e7f6b9f85 100644 --- a/app/i18n/en/feedback.php +++ b/app/i18n/en/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s does not exist', ), 'import_export' => array( - 'export_no_zip_extension' => 'Zip extension is not present on your server. Please try to export files one by one.', + 'export_no_zip_extension' => 'ZIP extension is not present on your server. Please try to export files one by one.', 'feeds_imported' => 'Your feeds have been imported and will now be updated', 'feeds_imported_with_errors' => 'Your feeds have been imported but some errors occurred', 'file_cannot_be_uploaded' => 'File cannot be uploaded!', - 'no_zip_extension' => 'Zip extension is not present on your server.', - 'zip_error' => 'An error occured during Zip import.', + 'no_zip_extension' => 'ZIP extension is not present on your server.', + 'zip_error' => 'An error occured during ZIP import.', ), 'sub' => array( 'actualize' => 'Actualise', diff --git a/app/i18n/en/sub.php b/app/i18n/en/sub.php index aaaa02827..789433ee6 100644 --- a/app/i18n/en/sub.php +++ b/app/i18n/en/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Export list of feeds (OPML)', 'export_starred' => 'Export your favourites', 'feed_list' => 'List of %s articles', - 'file_to_import' => 'File to import
(OPML, Json or Zip)', - 'file_to_import_no_zip' => 'File to import
(OPML or Json)', + 'file_to_import' => 'File to import
(OPML, JSON or ZIP)', + 'file_to_import_no_zip' => 'File to import
(OPML or JSON)', 'import' => 'Import', 'starred_list' => 'List of favourite articles', 'title' => 'Import / export', diff --git a/app/i18n/fr/feedback.php b/app/i18n/fr/feedback.php index 15f3ab859..5966fc3a7 100644 --- a/app/i18n/fr/feedback.php +++ b/app/i18n/fr/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s n’existe pas', ), 'import_export' => array( - 'export_no_zip_extension' => 'L’extension Zip n’est pas présente sur votre serveur. Veuillez essayer d’exporter les fichiers un par un.', + 'export_no_zip_extension' => 'L’extension ZIP n’est pas présente sur votre serveur. Veuillez essayer d’exporter les fichiers un par un.', 'feeds_imported' => 'Vos flux ont été importés et vont maintenant être actualisés.', 'feeds_imported_with_errors' => 'Vos flux ont été importés mais des erreurs sont survenues.', 'file_cannot_be_uploaded' => 'Le fichier ne peut pas être téléchargé !', - 'no_zip_extension' => 'L’extension Zip n’est pas présente sur votre serveur.', - 'zip_error' => 'Une erreur est survenue durant l’import du fichier Zip.', + 'no_zip_extension' => 'L’extension ZIP n’est pas présente sur votre serveur.', + 'zip_error' => 'Une erreur est survenue durant l’import du fichier ZIP.', ), 'sub' => array( 'actualize' => 'Actualiser', diff --git a/app/i18n/fr/sub.php b/app/i18n/fr/sub.php index e3631eb8b..bb3f2fefc 100644 --- a/app/i18n/fr/sub.php +++ b/app/i18n/fr/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Exporter la liste des flux (OPML)', 'export_starred' => 'Exporter les favoris', 'feed_list' => 'Liste des articles de %s', - 'file_to_import' => 'Fichier à importer
(OPML, Json ou Zip)', - 'file_to_import_no_zip' => 'Fichier à importer
(OPML ou Json)', + 'file_to_import' => 'Fichier à importer
(OPML, JSON ou ZIP)', + 'file_to_import_no_zip' => 'Fichier à importer
(OPML ou JSON)', 'import' => 'Importer', 'starred_list' => 'Liste des articles favoris', 'title' => 'Importer / exporter', diff --git a/app/i18n/it/feedback.php b/app/i18n/it/feedback.php index f217586b0..5851cb2e6 100644 --- a/app/i18n/it/feedback.php +++ b/app/i18n/it/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s non disponibile', ), 'import_export' => array( - 'export_no_zip_extension' => 'Estensione Zip non presente sul server. Per favore esporta i files singolarmente.', + 'export_no_zip_extension' => 'Estensione ZIP non presente sul server. Per favore esporta i files singolarmente.', 'feeds_imported' => 'I tuoi feed sono stati importati e saranno aggiornati', 'feeds_imported_with_errors' => 'I tuoi feeds sono stati importati ma si sono verificati alcuni errori', 'file_cannot_be_uploaded' => 'Il file non può essere caricato!', - 'no_zip_extension' => 'Estensione Zip non presente sul server.', - 'zip_error' => 'Si è verificato un errore importando il file Zip', + 'no_zip_extension' => 'Estensione ZIP non presente sul server.', + 'zip_error' => 'Si è verificato un errore importando il file ZIP', ), 'sub' => array( 'actualize' => 'Aggiorna', diff --git a/app/i18n/it/sub.php b/app/i18n/it/sub.php index dfcee2ce3..758e322c5 100644 --- a/app/i18n/it/sub.php +++ b/app/i18n/it/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Esporta tutta la lista dei feed (OPML)', 'export_starred' => 'Esporta i tuoi preferiti', 'feed_list' => 'Elenco di %s articoli', - 'file_to_import' => 'File da importare
(OPML, Json o Zip)', - 'file_to_import_no_zip' => 'File da importare
(OPML o Json)', + 'file_to_import' => 'File da importare
(OPML, JSON o ZIP)', + 'file_to_import_no_zip' => 'File da importare
(OPML o JSON)', 'import' => 'Importa', 'starred_list' => 'Elenco articoli preferiti', 'title' => 'Importa / esporta', diff --git a/app/i18n/nl/feedback.php b/app/i18n/nl/feedback.php index b703c43cf..386b8d415 100644 --- a/app/i18n/nl/feedback.php +++ b/app/i18n/nl/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s bestaat niet', ), 'import_export' => array( - 'export_no_zip_extension' => 'Zip uitbreiding is niet aanwezig op uw server. Exporteer a.u.b. uw bestanden één voor één.', + 'export_no_zip_extension' => 'ZIP uitbreiding is niet aanwezig op uw server. Exporteer a.u.b. uw bestanden één voor één.', 'feeds_imported' => 'Uw feeds zijn geimporteerd en worden nu vernieuwd', 'feeds_imported_with_errors' => 'Uw feeds zijn geimporteerd maar er zijn enige fouten opgetreden', 'file_cannot_be_uploaded' => 'Bestand kan niet worden verzonden!', - 'no_zip_extension' => 'Zip uitbreiding is niet aanwezig op uw server.', - 'zip_error' => 'Er is een fout opgetreden tijdens het imporeren van het Zip bestand.', + 'no_zip_extension' => 'ZIP uitbreiding is niet aanwezig op uw server.', + 'zip_error' => 'Er is een fout opgetreden tijdens het imporeren van het ZIP bestand.', ), 'sub' => array( 'actualize' => 'Actualiseren', diff --git a/app/i18n/nl/sub.php b/app/i18n/nl/sub.php index 159a58b27..a1ba3f03d 100644 --- a/app/i18n/nl/sub.php +++ b/app/i18n/nl/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Exporteer lijst van feeds (OPML)', 'export_starred' => 'Exporteer je fovorieten', 'feed_list' => 'Lijst van %s artikelen', - 'file_to_import' => 'Bestand om te importeren
(OPML, Json of Zip)', - 'file_to_import_no_zip' => 'Bestand om te importeren
(OPML of Json)', + 'file_to_import' => 'Bestand om te importeren
(OPML, JSON of ZIP)', + 'file_to_import_no_zip' => 'Bestand om te importeren
(OPML of JSON)', 'import' => 'Importeer', 'starred_list' => 'Lijst van favoriete artikelen', 'title' => 'Importeren / exporteren', diff --git a/app/i18n/ru/feedback.php b/app/i18n/ru/feedback.php index 7ce2ae9cf..e7f6b9f85 100644 --- a/app/i18n/ru/feedback.php +++ b/app/i18n/ru/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s does not exist', ), 'import_export' => array( - 'export_no_zip_extension' => 'Zip extension is not present on your server. Please try to export files one by one.', + 'export_no_zip_extension' => 'ZIP extension is not present on your server. Please try to export files one by one.', 'feeds_imported' => 'Your feeds have been imported and will now be updated', 'feeds_imported_with_errors' => 'Your feeds have been imported but some errors occurred', 'file_cannot_be_uploaded' => 'File cannot be uploaded!', - 'no_zip_extension' => 'Zip extension is not present on your server.', - 'zip_error' => 'An error occured during Zip import.', + 'no_zip_extension' => 'ZIP extension is not present on your server.', + 'zip_error' => 'An error occured during ZIP import.', ), 'sub' => array( 'actualize' => 'Actualise', diff --git a/app/i18n/ru/sub.php b/app/i18n/ru/sub.php index aaaa02827..789433ee6 100644 --- a/app/i18n/ru/sub.php +++ b/app/i18n/ru/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Export list of feeds (OPML)', 'export_starred' => 'Export your favourites', 'feed_list' => 'List of %s articles', - 'file_to_import' => 'File to import
(OPML, Json or Zip)', - 'file_to_import_no_zip' => 'File to import
(OPML or Json)', + 'file_to_import' => 'File to import
(OPML, JSON or ZIP)', + 'file_to_import_no_zip' => 'File to import
(OPML or JSON)', 'import' => 'Import', 'starred_list' => 'List of favourite articles', 'title' => 'Import / export', diff --git a/app/i18n/tr/feedback.php b/app/i18n/tr/feedback.php index a53316206..87361ff51 100644 --- a/app/i18n/tr/feedback.php +++ b/app/i18n/tr/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s bulunmamaktadır', ), 'import_export' => array( - 'export_no_zip_extension' => 'Zip eklentisi mevcut sunucunuzda yer almıyor. Lütfen başka dosya formatında dışarı aktarmayı deneyin.', + 'export_no_zip_extension' => 'ZIP eklentisi mevcut sunucunuzda yer almıyor. Lütfen başka dosya formatında dışarı aktarmayı deneyin.', 'feeds_imported' => 'Akışlarınız içe aktarıldı ve şimdi güncellenecek', 'feeds_imported_with_errors' => 'Akışlarınız içeri aktarıldı ama bazı hatalar meydana geldi', 'file_cannot_be_uploaded' => 'Dosya yüklenemedi!', - 'no_zip_extension' => 'Zip eklentisi mevcut sunucunuzda yer almıyor.', - 'zip_error' => 'Zip içe aktarımı sırasında hata meydana geldi.', + 'no_zip_extension' => 'ZIP eklentisi mevcut sunucunuzda yer almıyor.', + 'zip_error' => 'ZIP içe aktarımı sırasında hata meydana geldi.', ), 'sub' => array( 'actualize' => 'Güncelleme', diff --git a/app/i18n/tr/sub.php b/app/i18n/tr/sub.php index 5ab367ebb..7592096d9 100644 --- a/app/i18n/tr/sub.php +++ b/app/i18n/tr/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Akış listesini dışarı aktar (OPML)', 'export_starred' => 'Favorileri dışarı aktar', 'feed_list' => '%s makalenin listesi', - 'file_to_import' => 'Dosyadan içe aktar
(OPML, Json or Zip)', - 'file_to_import_no_zip' => 'Dosyadan içe aktar
(OPML or Json)', + 'file_to_import' => 'Dosyadan içe aktar
(OPML, JSON or ZIP)', + 'file_to_import_no_zip' => 'Dosyadan içe aktar
(OPML or JSON)', 'import' => 'İçe aktar', 'starred_list' => 'Favori makaleleirn listesi', 'title' => 'İçe / dışa aktar', diff --git a/cli/_cli.php b/cli/_cli.php index cb6d8ec32..d81d83d66 100644 --- a/cli/_cli.php +++ b/cli/_cli.php @@ -37,3 +37,8 @@ function cliInitUser($username) { return $username; } + +function done($ok) { + echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; + exit($ok ? 0 : 1); +} diff --git a/cli/create-user.php b/cli/create-user.php index 08c057af8..387b503b6 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -15,19 +15,19 @@ if (empty($options['user'])) { fail('Usage: ' . basename(__FILE__) . " --user=username --password='password' --api-password='api_password'" . " --language=en --email=user@example.net --token='longRandomString'"); } -$new_user_name = $options['user']; -if (!ctype_alnum($new_user_name)) { - fail('FreshRSS error: invalid username “' . $new_user_name . '”'); +$username = $options['user']; +if (!ctype_alnum($username)) { + fail('FreshRSS error: invalid username “' . $username . '”'); } $usernames = listUsers(); -if (preg_grep("/^$new_user_name$/i", $usernames)) { - fail('FreshRSS error: username already taken “' . $new_user_name . '”'); +if (preg_grep("/^$username$/i", $usernames)) { + fail('FreshRSS error: username already taken “' . $username . '”'); } -echo 'FreshRSS creating user “', $new_user_name, "”…\n"; +echo 'FreshRSS creating user “', $username, "”…\n"; -$ok = FreshRSS_user_Controller::createUser($new_user_name, +$ok = FreshRSS_user_Controller::createUser($username, empty($options['password']) ? '' : $options['password'], empty($options['api-password']) ? '' : $options['api-password'], array( @@ -37,5 +37,4 @@ $ok = FreshRSS_user_Controller::createUser($new_user_name, invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); -echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; -exit($ok ? 0 : 1); +done($ok); diff --git a/cli/delete-user.php b/cli/delete-user.php index 46332fe34..da48103f7 100755 --- a/cli/delete-user.php +++ b/cli/delete-user.php @@ -29,5 +29,4 @@ $ok = FreshRSS_user_Controller::deleteUser($username); invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); -echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; -exit($ok ? 0 : 1); +done($ok); diff --git a/cli/import-for-user.php b/cli/import-for-user.php new file mode 100644 index 000000000..308786599 --- /dev/null +++ b/cli/import-for-user.php @@ -0,0 +1,36 @@ +#!/usr/bin/php +importFile($filename, $filename, $username); +} catch (FreshRSS_ZipMissing_Exception $zme) { + fail('FreshRSS error: Lacking php-zip extension!'); +} catch (FreshRSS_Zip_Exception $ze) { + fail('FreshRSS error: ZIP archive cannot be imported! Error code: ' . $ze->zipErrorCode()); +} +invalidateHttpCache($username); + +done($ok); diff --git a/cli/list-users.php b/cli/list-users.php index cc1cf5269..e690ff451 100755 --- a/cli/list-users.php +++ b/cli/list-users.php @@ -4,8 +4,8 @@ require('_cli.php'); $users = listUsers(); sort($users); -if ($system_conf->default_user !== '') { - array_unshift($users, $system_conf->default_user); +if (FreshRSS_Context::$system_conf->default_user !== '') { + array_unshift($users, FreshRSS_Context::$system_conf->default_user); $users = array_unique($users); } diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index da28909df..45139b0d6 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -36,22 +36,21 @@ class Minz_ModelPdo { * HOST, BASE, USER et PASS définies dans le fichier de configuration */ public function __construct($currentUser = null) { - if (self::$useSharedBd && self::$sharedBd != null && $currentUser === null) { + if ($currentUser === null) { + $currentUser = Minz_Session::param('currentUser', '_'); + } + if (self::$useSharedBd && self::$sharedBd != null && $currentUser === self::$sharedCurrentUser) { $this->bd = self::$sharedBd; $this->prefix = self::$sharedPrefix; $this->current_user = self::$sharedCurrentUser; return; } + $this->current_user = $currentUser; + self::$sharedCurrentUser = $currentUser; $conf = Minz_Configuration::get('system'); $db = $conf->db; - if ($currentUser === null) { - $currentUser = Minz_Session::param('currentUser', '_'); - } - $this->current_user = $currentUser; - self::$sharedCurrentUser = $currentUser; - $driver_options = isset($conf->db['pdo_options']) && is_array($conf->db['pdo_options']) ? $conf->db['pdo_options'] : array(); $dbServer = parse_url('db://' . $db['host']); -- cgit v1.2.3 From fcb9280fc87c159539f5832ab35f174cd515654e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 23 Oct 2016 13:37:48 +0200 Subject: CLI export ZIP export, OPML export. Corrected height of feeds select in Pafat theme. https://github.com/FreshRSS/FreshRSS/pull/1338 https://github.com/FreshRSS/FreshRSS/issues/1039 https://github.com/FreshRSS/FreshRSS/issues/1277 --- app/Controllers/importExportController.php | 84 +++++++++++++++++++----------- app/Models/FeedDAO.php | 7 +++ app/views/importExport/index.phtml | 2 +- cli/_cli.php | 4 +- cli/create-user.php | 14 ++--- cli/delete-user.php | 2 +- cli/export-opml-for-user.php | 24 +++++++++ cli/export-zip-for-user.php | 30 +++++++++++ cli/import-for-user.php | 3 +- p/themes/Pafat/pafat.css | 3 -- 10 files changed, 128 insertions(+), 45 deletions(-) create mode 100644 cli/export-opml-for-user.php create mode 100644 cli/export-zip-for-user.php (limited to 'app/Controllers') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index fbebb2a78..bb22ac739 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -522,26 +522,21 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return $return; } - /** - * This action handles export action. - * - * This action must be reached by a POST request. - * - * Parameters are: - * - export_opml (default: false) - * - export_starred (default: false) - * - export_feeds (default: array()) a list of feed ids - */ - public function exportAction() { - if (!Minz_Request::isPost()) { - Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); - } + public function exportFile($export_opml = true, $export_starred = false, $export_feeds = array(), $maxFeedEntries = 50, $username = null) { + require_once(LIB_PATH . '/lib_opml.php'); - $this->view->_useLayout(false); + $this->catDAO = new FreshRSS_CategoryDAO($username); + $this->entryDAO = FreshRSS_Factory::createEntryDao($username); + $this->feedDAO = FreshRSS_Factory::createFeedDao($username); + + if ($export_feeds === true) { + //All feeds + $export_feeds = $this->feedDAO->listFeedsIds(); + } + if (!is_array($export_feeds)) { + $export_feeds = array(); + } - $export_opml = Minz_Request::param('export_opml', false); - $export_starred = Minz_Request::param('export_starred', false); - $export_feeds = Minz_Request::param('export_feeds', array()); $day = date('Y-m-d'); $export_files = array(); @@ -558,7 +553,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if ($feed) { $filename = "feed_${day}_" . $feed->category() . '_' . $feed->id() . '.json'; - $export_files[$filename] = $this->generateEntries('feed', $feed); + $export_files[$filename] = $this->generateEntries('feed', $feed, $maxFeedEntries); } } @@ -566,18 +561,49 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if ($nb_files > 1) { // If there are more than 1 file to export, we need a ZIP archive. try { - $this->exportZip($export_files); + $this->sendZip($export_files); } catch (Exception $e) { - # Oops, there is no ZIP extension! - Minz_Request::bad(_t('feedback.import_export.export_no_zip_extension'), - array('c' => 'importExport', 'a' => 'index')); + throw new FreshRSS_ZipMissing_Exception($e); } } elseif ($nb_files === 1) { // Only one file? Guess its type and export it. $filename = key($export_files); $type = self::guessFileType($filename); - $this->exportFile('freshrss_' . $filename, $export_files[$filename], $type); - } else { + $this->sendFile('freshrss_' . $filename, $export_files[$filename], $type); + } + return $nb_files; + } + + /** + * This action handles export action. + * + * This action must be reached by a POST request. + * + * Parameters are: + * - export_opml (default: false) + * - export_starred (default: false) + * - export_feeds (default: array()) a list of feed ids + */ + public function exportAction() { + if (!Minz_Request::isPost()) { + Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); + } + $this->view->_useLayout(false); + + $nb_files = 0; + try { + $nb_files = $this->exportFile( + Minz_Request::param('export_opml', false), + Minz_Request::param('export_starred', false), + Minz_Request::param('export_feeds', array()) + ); + } catch (FreshRSS_ZipMissing_Exception $zme) { + # Oops, there is no ZIP extension! + Minz_Request::bad(_t('feedback.import_export.export_no_zip_extension'), + array('c' => 'importExport', 'a' => 'index')); + } + + if ($nb_files < 1) { // Nothing to do... Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); } @@ -606,7 +632,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * @param FreshRSS_Feed $feed feed of which we want to get entries. * @return string the JSON file content. */ - private function generateEntries($type, $feed = NULL) { + private function generateEntries($type, $feed = NULL, $maxFeedEntries = 50) { $this->view->categories = $this->catDAO->listCategories(); if ($type == 'starred') { @@ -621,7 +647,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->view->type = 'feed/' . $feed->id(); $this->view->entries = $this->entryDAO->listWhere( 'f', $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', - FreshRSS_Context::$user_conf->posts_per_page + $maxFeedEntries ); $this->view->feed = $feed; } @@ -635,7 +661,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * @param array $files list of files where key is filename and value the content. * @throws Exception if Zip extension is not loaded. */ - private function exportZip($files) { + private function sendZip($files) { if (!extension_loaded('zip')) { throw new Exception(); } @@ -667,7 +693,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * @param string $type the file type (opml, json_feed or json_starred). * If equals to unknown, nothing happens. */ - private function exportFile($filename, $content, $type) { + private function sendFile($filename, $content, $type) { if ($type === 'unknown') { return; } diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index b21f19b66..68398efd5 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -212,6 +212,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } + public function listFeedsIds() { + $sql = 'SELECT id FROM `' . $this->prefix . 'feed`'; + $stm = $this->bd->prepare($sql); + $stm->execute(); + return $stm->fetchAll(PDO::FETCH_COLUMN, 0); + } + public function listFeeds() { $sql = 'SELECT * FROM `' . $this->prefix . 'feed` ORDER BY name'; $stm = $this->bd->prepare($sql); diff --git a/app/views/importExport/index.phtml b/app/views/importExport/index.phtml index c0bc412c3..c5049e3ea 100644 --- a/app/views/importExport/index.phtml +++ b/app/views/importExport/index.phtml @@ -44,7 +44,7 @@ $select_args = ' size="' . min(10, count($this->feeds)) .'" multiple="multiple"'; } ?> - size="10"> '; ?> feeds as $feed) { ?> diff --git a/cli/_cli.php b/cli/_cli.php index d81d83d66..66506f07a 100644 --- a/cli/_cli.php +++ b/cli/_cli.php @@ -10,7 +10,7 @@ Minz_Configuration::register('system', DATA_PATH . '/config.php', DATA_PATH . '/config.default.php'); FreshRSS_Context::$system_conf = Minz_Configuration::get('system'); -Minz_Translate::init(); +Minz_Translate::init('en'); FreshRSS_Context::$isCli = true; @@ -39,6 +39,6 @@ function cliInitUser($username) { } function done($ok) { - echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; + fwrite(STDERR, 'Result: ' . ($ok ? 'success' : 'fail') . "\n"); exit($ok ? 0 : 1); } diff --git a/cli/create-user.php b/cli/create-user.php index 387b503b6..243e65a35 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -4,16 +4,16 @@ require('_cli.php'); $options = getopt('', array( 'user:', - 'password::', - 'api-password::', - 'language::', - 'email::', - 'token::', + 'password:', + 'api-password:', + 'language:', + 'email:', + 'token:', )); if (empty($options['user'])) { - fail('Usage: ' . basename(__FILE__) . " --user=username --password='password' --api-password='api_password'" . - " --language=en --email=user@example.net --token='longRandomString'"); + fail('Usage: ' . basename(__FILE__) . " --user username --password 'password' --api-password 'api_password'" . + " --language en --email user@example.net --token 'longRandomString'"); } $username = $options['user']; if (!ctype_alnum($username)) { diff --git a/cli/delete-user.php b/cli/delete-user.php index da48103f7..6f0e86e17 100755 --- a/cli/delete-user.php +++ b/cli/delete-user.php @@ -7,7 +7,7 @@ $options = getopt('', array( )); if (empty($options['user'])) { - fail('Usage: ' . basename(__FILE__) . " --user=username"); + fail('Usage: ' . basename(__FILE__) . " --user username"); } $username = $options['user']; if (!ctype_alnum($username)) { diff --git a/cli/export-opml-for-user.php b/cli/export-opml-for-user.php new file mode 100644 index 000000000..bce4efd63 --- /dev/null +++ b/cli/export-opml-for-user.php @@ -0,0 +1,24 @@ +#!/usr/bin/php + /path/to/file.opml.xml'"); +} + +$username = cliInitUser($options['user']); + +fwrite(STDERR, 'FreshRSS exporting OPML for user “' . $username . "”…\n"); + +$importController = new FreshRSS_importExport_Controller(); + +$ok = false; +$ok = $importController->exportFile(true, false, array(), 0, $username); + +invalidateHttpCache($username); + +done($ok); diff --git a/cli/export-zip-for-user.php b/cli/export-zip-for-user.php new file mode 100644 index 000000000..f8d24238b --- /dev/null +++ b/cli/export-zip-for-user.php @@ -0,0 +1,30 @@ +#!/usr/bin/php + /path/to/file.zip'"); +} + +$username = cliInitUser($options['user']); + +fwrite(STDERR, 'FreshRSS exporting ZIP for user “' . $username . "”…\n"); + +$importController = new FreshRSS_importExport_Controller(); + +$ok = false; +try { + $ok = $importController->exportFile(true, true, true, + empty($options['max-feed-entries']) ? 100 : intval($options['max-feed-entries']), + $username); +} catch (FreshRSS_ZipMissing_Exception $zme) { + fail('FreshRSS error: Lacking php-zip extension!'); +} +invalidateHttpCache($username); + +done($ok); diff --git a/cli/import-for-user.php b/cli/import-for-user.php index 308786599..29084f062 100755 --- a/cli/import-for-user.php +++ b/cli/import-for-user.php @@ -5,11 +5,10 @@ require('_cli.php'); $options = getopt('', array( 'user:', 'filename:', - 'clear-existing', )); if (empty($options['user']) || empty($options['filename'])) { - fail('Usage: ' . basename(__FILE__) . " --user=username --filename='/path/to/file.ext' --clear-existing"); + fail('Usage: ' . basename(__FILE__) . " --user username --filename /path/to/file.ext"); } $username = cliInitUser($options['user']); diff --git a/p/themes/Pafat/pafat.css b/p/themes/Pafat/pafat.css index 5e46a63ca..abe808eab 100644 --- a/p/themes/Pafat/pafat.css +++ b/p/themes/Pafat/pafat.css @@ -48,9 +48,6 @@ input, select, textarea { vertical-align: middle; } -select{ - height:29px; -} option { padding: 0 .5em; } -- cgit v1.2.3 From ab4ece6780cf841f6ce4e89f7b81a1ff1661f615 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 24 Oct 2016 01:41:09 +0200 Subject: CLI do-install https://github.com/FreshRSS/FreshRSS/issues/1095 https://github.com/FreshRSS/FreshRSS/issues/1090 --- app/Controllers/userController.php | 5 +- app/install.php | 161 +++++++------------------------------ cli/_cli.php | 7 +- cli/create-user.php | 10 ++- cli/do-install.php | 102 +++++++++++++++++++++++ lib/lib_install.php | 115 ++++++++++++++++++++++++++ 6 files changed, 260 insertions(+), 140 deletions(-) create mode 100644 cli/do-install.php create mode 100644 lib/lib_install.php (limited to 'app/Controllers') diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 2f04c7a1d..9dee16e8c 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -24,7 +24,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { } } - private static function hashPassword($passwordPlain) { + public static function hashPassword($passwordPlain) { if (!function_exists('password_hash')) { include_once(LIB_PATH . '/password_compat.php'); } @@ -112,9 +112,6 @@ class FreshRSS_user_Controller extends Minz_ActionController { $userConfig['language'] = 'en'; } - $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 $configPath = join_path(DATA_PATH, 'users', $new_user_name, 'config.php'); diff --git a/app/install.php b/app/install.php index 1972379e5..6956761c7 100644 --- a/app/install.php +++ b/app/install.php @@ -4,15 +4,12 @@ if (function_exists('opcache_reset')) { } header("Content-Security-Policy: default-src 'self'"); -define('BCRYPT_COST', 9); +require(LIB_PATH . '/lib_install.php'); session_name('FreshRSS'); session_set_cookie_params(0, dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirname($_SERVER['REQUEST_URI'])), null, false, true); session_start(); -Minz_Configuration::register('default_system', join_path(DATA_PATH, 'config.default.php')); -Minz_Configuration::register('default_user', join_path(USERS_PATH, '_', 'config.default.php')); - if (isset($_GET['step'])) { define('STEP',(int)$_GET['step']); } else { @@ -26,13 +23,13 @@ if (STEP === 3 && isset($_POST['type'])) { if (isset($_SESSION['bd_type'])) { switch ($_SESSION['bd_type']) { case 'mysql': - include(APP_PATH . '/SQL/install.sql.mysql.php'); + include_once(APP_PATH . '/SQL/install.sql.mysql.php'); break; case 'sqlite': - include(APP_PATH . '/SQL/install.sql.sqlite.php'); + include_once(APP_PATH . '/SQL/install.sql.sqlite.php'); break; case 'pgsql': - include(APP_PATH . '/SQL/install.sql.pgsql.php'); + include_once(APP_PATH . '/SQL/install.sql.pgsql.php'); break; } } @@ -131,12 +128,7 @@ function saveStep2() { $password_plain = param('passwordPlain', false); if ($password_plain !== false && cryptAvailable()) { - if (!function_exists('password_hash')) { - include_once(LIB_PATH . '/password_compat.php'); - } - $passwordHash = password_hash($password_plain, PASSWORD_BCRYPT, array('cost' => BCRYPT_COST)); - $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js - $_SESSION['passwordHash'] = $passwordHash; + $_SESSION['passwordHash'] = FreshRSS_user_Controller::hashPassword($password_plain); } if (empty($_SESSION['old_entries']) || @@ -149,7 +141,7 @@ function saveStep2() { return false; } - $_SESSION['salt'] = sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__))); + $_SESSION['salt'] = generateSalt(); if ((!ctype_digit($_SESSION['old_entries'])) ||($_SESSION['old_entries'] < 1)) { $_SESSION['old_entries'] = $user_default_config->old_entries; } @@ -171,7 +163,7 @@ function saveStep2() { recursive_unlink($user_dir); mkdir($user_dir); - file_put_contents($user_config_path, " 0 && $s0['all'] != 'ok') { @@ -279,49 +265,6 @@ function checkStep0() { ); } -function checkStep1() { - $php = version_compare(PHP_VERSION, '5.3.3') >= 0; - $minz = file_exists(join_path(LIB_PATH, 'Minz')); - $curl = extension_loaded('curl'); - $pdo_mysql = extension_loaded('pdo_mysql'); - $pdo_sqlite = extension_loaded('pdo_sqlite'); - $pdo_pgsql = extension_loaded('pdo_pgsql'); - $pdo = $pdo_mysql || $pdo_sqlite || $pdo_pgsql; - $pcre = extension_loaded('pcre'); - $ctype = extension_loaded('ctype'); - $dom = class_exists('DOMDocument'); - $xml = function_exists('xml_parser_create'); - $json = function_exists('json_encode'); - $data = DATA_PATH && is_writable(DATA_PATH); - $cache = CACHE_PATH && is_writable(CACHE_PATH); - $users = USERS_PATH && is_writable(USERS_PATH); - $favicons = is_writable(join_path(DATA_PATH, 'favicons')); - $http_referer = is_referer_from_same_domain(); - - return array( - 'php' => $php ? 'ok' : 'ko', - 'minz' => $minz ? 'ok' : 'ko', - 'curl' => $curl ? 'ok' : 'ko', - 'pdo-mysql' => $pdo_mysql ? 'ok' : 'ko', - 'pdo-sqlite' => $pdo_sqlite ? 'ok' : 'ko', - 'pdo-pgsql' => $pdo_pgsql ? 'ok' : 'ko', - 'pdo' => $pdo ? 'ok' : 'ko', - 'pcre' => $pcre ? 'ok' : 'ko', - 'ctype' => $ctype ? 'ok' : 'ko', - 'dom' => $dom ? 'ok' : 'ko', - 'xml' => $xml ? 'ok' : 'ko', - 'json' => $json ? 'ok' : 'ko', - 'data' => $data ? 'ok' : 'ko', - 'cache' => $cache ? 'ok' : 'ko', - 'users' => $users ? 'ok' : 'ko', - 'favicons' => $favicons ? 'ok' : 'ko', - 'http_referer' => $http_referer ? 'ok' : 'ko', - 'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom && $xml && - $data && $cache && $users && $favicons && $http_referer ? - 'ok' : 'ko' - ); -} - function freshrss_already_installed() { $conf_path = join_path(DATA_PATH, 'config.php'); if (!file_exists($conf_path)) { @@ -392,60 +335,15 @@ function checkStep3() { ); } -function checkBD() { +function checkDbUser(&$dbOptions) { $ok = false; - + $str = $dbOptions['bd_dsn']; + $driver_options = $dbOptions['bd_options']; try { - $str = ''; - $driver_options = null; - switch ($_SESSION['bd_type']) { - case 'mysql': - $driver_options = array( - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4' - ); - - try { // on ouvre une connexion juste pour créer la base si elle n'existe pas - $str = 'mysql:host=' . $_SESSION['bd_host'] . ';'; - $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); - $sql = sprintf(SQL_CREATE_DB, $_SESSION['bd_base']); - $res = $c->query($sql); - } catch (PDOException $e) { - } - - // on écrase la précédente connexion en sélectionnant la nouvelle BDD - $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; - break; - case 'sqlite': - $str = 'sqlite:' . join_path(USERS_PATH, $_SESSION['default_user'], 'db.sqlite'); - $driver_options = array( - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - ); - break; - case 'pgsql': - $driver_options = array( - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - ); - - try { // on ouvre une connexion juste pour créer la base si elle n'existe pas - $str = 'pgsql:host=' . $_SESSION['bd_host'] . ';dbname=postgres'; - $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); - $sql = sprintf(SQL_CREATE_DB, $_SESSION['bd_base']); - $res = $c->query($sql); - } catch (PDOException $e) { - syslog(LOG_DEBUG, 'pgsql ' . $e->getMessage()); - } - - // on écrase la précédente connexion en sélectionnant la nouvelle BDD - $str = 'pgsql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; - break; - default: - return false; - } - - $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); + $c = new PDO($str, $dbOptions['bd_user'], $dbOptions['bd_password'], $driver_options); if (defined('SQL_CREATE_TABLES')) { - $sql = sprintf(SQL_CREATE_TABLES, $_SESSION['bd_prefix_user'], _t('gen.short.default_category')); + $sql = sprintf(SQL_CREATE_TABLES, $dbOptions['bd_prefix_user'], _t('gen.short.default_category')); $stm = $c->prepare($sql); $ok = $stm->execute(); } else { @@ -453,7 +351,7 @@ function checkBD() { if (is_array($SQL_CREATE_TABLES)) { $ok = true; foreach ($SQL_CREATE_TABLES as $instruction) { - $sql = sprintf($instruction, $_SESSION['bd_prefix_user'], _t('gen.short.default_category')); + $sql = sprintf($instruction, $dbOptions['bd_prefix_user'], _t('gen.short.default_category')); $stm = $c->prepare($sql); $ok &= $stm->execute(); } @@ -461,13 +359,8 @@ function checkBD() { } } catch (PDOException $e) { $ok = false; - $_SESSION['bd_error'] = $e->getMessage(); + $dbOptions['bd_error'] = $e->getMessage(); } - - if (!$ok) { - @unlink(join_path(DATA_PATH, 'config.php')); - } - return $ok; } @@ -510,7 +403,7 @@ function printStep0() { // @todo refactor this view with the check_install action function printStep1() { - $res = checkStep1(); + $res = checkRequirements(); ?> @@ -805,7 +698,9 @@ case 3: case 4: break; case 5: - deleteInstall(); + if (deleteInstall()) { + header('Location: index.php'); + } break; } ?> diff --git a/cli/_cli.php b/cli/_cli.php index 66506f07a..7d1a7c6b2 100644 --- a/cli/_cli.php +++ b/cli/_cli.php @@ -38,7 +38,12 @@ function cliInitUser($username) { return $username; } -function done($ok) { +function accessRights() { + echo '• Remember to re-apply the appropriate access rights, such as:' , "\n", + "\t", 'sudo chown -R :www-data . && sudo chmod -R g+r . && sudo chmod -R g+w ./data/', "\n"; +} + +function done($ok = true) { fwrite(STDERR, 'Result: ' . ($ok ? 'success' : 'fail') . "\n"); exit($ok ? 0 : 1); } diff --git a/cli/create-user.php b/cli/create-user.php index 243e65a35..5e93d4605 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -12,8 +12,8 @@ $options = getopt('', array( )); if (empty($options['user'])) { - fail('Usage: ' . basename(__FILE__) . " --user username --password 'password' --api-password 'api_password'" . - " --language en --email user@example.net --token 'longRandomString'"); + fail('Usage: ' . basename(__FILE__) . " --user username ( --password 'password' --api-password 'api_password'" . + " --language en --email user@example.net --token 'longRandomString' )"); } $username = $options['user']; if (!ctype_alnum($username)) { @@ -35,6 +35,12 @@ $ok = FreshRSS_user_Controller::createUser($username, 'token' => empty($options['token']) ? '' : $options['token'], )); +if (!$ok) { + fail('FreshRSS could not create user!'); +} + invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); +accessRights(); + done($ok); diff --git a/cli/do-install.php b/cli/do-install.php new file mode 100644 index 000000000..5eeedc626 --- /dev/null +++ b/cli/do-install.php @@ -0,0 +1,102 @@ +#!/usr/bin/php + $check) { + if ($check !== 'ok' && $requirement !== 'all') { + $message .= '• ' . $requirement . "\n"; + } + } + fail($message); +} + +if (!ctype_alnum($options['default_user'])) { + fail('FreshRSS invalid default username (must be ASCII alphanumeric): ' . $options['default_user']); +} + +if (!in_array($options['auth_type'], array('form', 'http_auth', 'none'))) { + fail('FreshRSS invalid authentication method (auth_type must be one of { form, http_auth, none }: ' . $options['auth_type']); +} + +$config = array( + 'salt' => generateSalt(), + 'db' => FreshRSS_Context::$system_conf->db, + ); + +foreach ($params as $param) { + $param = rtrim($param, ':'); + if (isset($options[$param])) { + $config[$param] = $options[$param] === false ? true : $options[$param]; + } +} + +if ((!empty($config['base_url'])) && server_is_public($config['base_url'])) { + $config['pubsubhubbub_enabled'] = true; +} + +foreach ($dBparams as $dBparam) { + $dBparam = rtrim($dBparam, ':'); + if (!empty($options[$dBparam])) { + $param = substr($dBparam, strlen('db-')); + $config['db'][$param] = $options[$dBparam]; + } +} + +if (file_put_contents(join_path(DATA_PATH, 'config.php'), "= 0; + $minz = file_exists(join_path(LIB_PATH, 'Minz')); + $curl = extension_loaded('curl'); + $pdo_mysql = extension_loaded('pdo_mysql'); + $pdo_sqlite = extension_loaded('pdo_sqlite'); + $pdo_pgsql = extension_loaded('pdo_pgsql'); + $pdo = $pdo_mysql || $pdo_sqlite || $pdo_pgsql; + $pcre = extension_loaded('pcre'); + $ctype = extension_loaded('ctype'); + $dom = class_exists('DOMDocument'); + $xml = function_exists('xml_parser_create'); + $json = function_exists('json_encode'); + $data = DATA_PATH && is_writable(DATA_PATH); + $cache = CACHE_PATH && is_writable(CACHE_PATH); + $users = USERS_PATH && is_writable(USERS_PATH); + $favicons = is_writable(join_path(DATA_PATH, 'favicons')); + $http_referer = is_referer_from_same_domain(); + + return array( + 'php' => $php ? 'ok' : 'ko', + 'minz' => $minz ? 'ok' : 'ko', + 'curl' => $curl ? 'ok' : 'ko', + 'pdo-mysql' => $pdo_mysql ? 'ok' : 'ko', + 'pdo-sqlite' => $pdo_sqlite ? 'ok' : 'ko', + 'pdo-pgsql' => $pdo_pgsql ? 'ok' : 'ko', + 'pdo' => $pdo ? 'ok' : 'ko', + 'pcre' => $pcre ? 'ok' : 'ko', + 'ctype' => $ctype ? 'ok' : 'ko', + 'dom' => $dom ? 'ok' : 'ko', + 'xml' => $xml ? 'ok' : 'ko', + 'json' => $json ? 'ok' : 'ko', + 'data' => $data ? 'ok' : 'ko', + 'cache' => $cache ? 'ok' : 'ko', + 'users' => $users ? 'ok' : 'ko', + 'favicons' => $favicons ? 'ok' : 'ko', + 'http_referer' => $http_referer ? 'ok' : 'ko', + 'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom && $xml && + $data && $cache && $users && $favicons && $http_referer ? + 'ok' : 'ko' + ); +} + +function generateSalt() { + return sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__))); +} + +function checkDb(&$dbOptions) { + $dsn = ''; + try { + $driver_options = null; + switch ($dbOptions['type']) { + case 'mysql': + include_once(APP_PATH . '/SQL/install.sql.mysql.php'); + $driver_options = array( + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4' + ); + try { // on ouvre une connexion juste pour créer la base si elle n'existe pas + $dsn = 'mysql:host=' . $dbOptions['host'] . ';'; + $c = new PDO($dsn, $dbOptions['user'], $dbOptions['password'], $driver_options); + $sql = sprintf(SQL_CREATE_DB, $dbOptions['base']); + $res = $c->query($sql); + } catch (PDOException $e) { + syslog(LOG_DEBUG, 'FreshRSS MySQL warning: ' . $e->getMessage()); + } + // on écrase la précédente connexion en sélectionnant la nouvelle BDD + $dsn = 'mysql:host=' . $dbOptions['host'] . ';dbname=' . $dbOptions['base']; + break; + case 'sqlite': + include_once(APP_PATH . '/SQL/install.sql.sqlite.php'); + $dsn = 'sqlite:' . join_path(USERS_PATH, $dbOptions['default_user'], 'db.sqlite'); + $driver_options = array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + break; + case 'pgsql': + include_once(APP_PATH . '/SQL/install.sql.pgsql.php'); + $driver_options = array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + try { // on ouvre une connexion juste pour créer la base si elle n'existe pas + $dsn = 'pgsql:host=' . $dbOptions['host'] . ';dbname=postgres'; + $c = new PDO($dsn, $dbOptions['user'], $dbOptions['password'], $driver_options); + $sql = sprintf(SQL_CREATE_DB, $dbOptions['base']); + $res = $c->query($sql); + } catch (PDOException $e) { + syslog(LOG_DEBUG, 'FreshRSS PostgreSQL warning: ' . $e->getMessage()); + } + // on écrase la précédente connexion en sélectionnant la nouvelle BDD + $dsn = 'pgsql:host=' . $dbOptions['host'] . ';dbname=' . $dbOptions['base']; + break; + default: + return false; + } + } catch (PDOException $e) { + $dsn = ''; + $dbOptions['error'] = $e->getMessage(); + } + $dbOptions['dsn'] = $dsn; + $dbOptions['options'] = $driver_options; + return $dsn != ''; +} + +function deleteInstall() { + $path = join_path(DATA_PATH, 'do-install.txt'); + @unlink($path); + return !file_exists($path); +} -- cgit v1.2.3 From 1182129ce5f07892afed190ffbb2ea4c7fc28967 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 24 Oct 2016 20:29:08 +0200 Subject: CLI option no-default-feeds https://github.com/FreshRSS/FreshRSS/issues/1095 --- app/Controllers/userController.php | 4 ++-- app/Models/UserDAO.php | 18 +++++++++++++++++- app/SQL/install.sql.mysql.php | 3 +++ app/SQL/install.sql.pgsql.php | 4 ++++ app/SQL/install.sql.sqlite.php | 4 ++++ app/install.php | 15 +++++++++++++++ cli/create-user.php | 7 +++++-- 7 files changed, 50 insertions(+), 5 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 9dee16e8c..9d6ae18e6 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -99,7 +99,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $this->view->size_user = $entryDAO->size(); } - public static function createUser($new_user_name, $passwordPlain, $apiPasswordPlain, $userConfig = array()) { + public static function createUser($new_user_name, $passwordPlain, $apiPasswordPlain, $userConfig = array(), $insertDefaultFeeds = true) { if (!is_array($userConfig)) { $userConfig = array(); } @@ -138,7 +138,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { } if ($ok) { $userDAO = new FreshRSS_UserDAO(); - $ok &= $userDAO->createUser($new_user_name, $userConfig['language']); + $ok &= $userDAO->createUser($new_user_name, $userConfig['language'], $insertDefaultFeeds); } return $ok; } diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index 597182693..a95ee6bc4 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -1,7 +1,7 @@ db; require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); @@ -28,6 +28,22 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { } } } + if ($insertDefaultFeeds) { + if (defined('SQL_INSERT_FEEDS')) { //E.g. MySQL + $sql = sprintf(SQL_INSERT_FEEDS, $bd_prefix_user); + $stm = $userPDO->bd->prepare($sql); + $ok &= $stm && $stm->execute(); + } else { //E.g. SQLite + global $SQL_INSERT_FEEDS; + if (is_array($SQL_INSERT_FEEDS)) { + foreach ($SQL_INSERT_FEEDS as $instruction) { + $sql = sprintf($instruction, $bd_prefix_user); + $stm = $userPDO->bd->prepare($sql); + $ok &= ($stm && $stm->execute()); + } + } + } + } } catch (Exception $e) { Minz_Log::error('Error while creating user: ' . $e->getMessage()); } diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php index ca181303e..a454829d5 100644 --- a/app/SQL/install.sql.mysql.php +++ b/app/SQL/install.sql.mysql.php @@ -59,6 +59,9 @@ CREATE TABLE IF NOT EXISTS `%1$sentry` ( ENGINE = INNODB; INSERT IGNORE INTO `%1$scategory` (id, name) VALUES(1, "%2$s"); +'); + +define('SQL_INSERT_FEEDS', ' INSERT IGNORE INTO `%1$sfeed` (url, category, name, website, description, ttl) VALUES("http://freshrss.org/feeds/all.atom.xml", 1, "FreshRSS.org", "http://freshrss.org/", "FreshRSS, a free, self-hostable aggregator…", 86400); INSERT IGNORE INTO `%1$sfeed` (url, category, name, website, description, ttl) VALUES("https://github.com/FreshRSS/FreshRSS/releases.atom", 1, "FreshRSS @ GitHub", "https://github.com/FreshRSS/FreshRSS/", "FreshRSS releases @ GitHub", 86400); '); diff --git a/app/SQL/install.sql.pgsql.php b/app/SQL/install.sql.pgsql.php index b343bda86..9f4240b98 100644 --- a/app/SQL/install.sql.pgsql.php +++ b/app/SQL/install.sql.pgsql.php @@ -52,6 +52,10 @@ $SQL_CREATE_TABLES = array( 'CREATE INDEX %1$sentry_lastSeen_index ON "%1$sentry" ("lastSeen");', 'INSERT INTO "%1$scategory" (name) SELECT \'%2$s\' WHERE NOT EXISTS (SELECT id FROM "%1$scategory" WHERE id = 1);', +); + +global $SQL_INSERT_FEEDS; +$SQL_INSERT_FEEDS = array( 'INSERT INTO "%1$sfeed" (url, category, name, website, description, ttl) SELECT \'http://freshrss.org/feeds/all.atom.xml\', 1, \'FreshRSS.org\', \'http://freshrss.org/\', \'FreshRSS, a free, self-hostable aggregator…\', 86400 WHERE NOT EXISTS (SELECT id FROM "%1$sfeed" WHERE url = \'http://freshrss.org/feeds/all.atom.xml\');', 'INSERT INTO "%1$sfeed" (url, category, name, website, description, ttl) SELECT \'https://github.com/FreshRSS/FreshRSS/releases.atom\', 1, \'FreshRSS @ GitHub\', \'https://github.com/FreshRSS/FreshRSS/\', \'FreshRSS releases @ GitHub\', 86400 WHERE NOT EXISTS (SELECT id FROM "%1$sfeed" WHERE url = \'https://github.com/FreshRSS/FreshRSS/releases.atom\');', ); diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php index 1d3a5d92f..68d93ba92 100644 --- a/app/SQL/install.sql.sqlite.php +++ b/app/SQL/install.sql.sqlite.php @@ -55,6 +55,10 @@ $SQL_CREATE_TABLES = array( 'CREATE INDEX IF NOT EXISTS entry_lastSeen_index ON `entry`(`lastSeen`);', //v1.1.1 'INSERT OR IGNORE INTO `category` (id, name) VALUES(1, "%2$s");', +); + +global $SQL_INSERT_FEEDS; +$SQL_INSERT_FEEDS = array( 'INSERT OR IGNORE INTO `feed` (url, category, name, website, description, ttl) VALUES("http://freshrss.org/feeds/all.atom.xml", 1, "FreshRSS.org", "http://freshrss.org/", "FreshRSS, a free, self-hostable aggregator…", 86400);', 'INSERT OR IGNORE INTO `feed` (url, category, name, website, description, ttl) VALUES("https://github.com/FreshRSS/FreshRSS/releases.atom", 1, "FreshRSS releases", "https://github.com/FreshRSS/FreshRSS/", "FreshRSS releases @ GitHub", 86400);', ); diff --git a/app/install.php b/app/install.php index 6956761c7..0daa02b1b 100644 --- a/app/install.php +++ b/app/install.php @@ -357,6 +357,21 @@ function checkDbUser(&$dbOptions) { } } } + + if (defined('SQL_INSERT_FEEDS')) { + $sql = sprintf(SQL_INSERT_FEEDS, $dbOptions['bd_prefix_user']); + $stm = $c->prepare($sql); + $ok &= $stm->execute(); + } else { + global $SQL_INSERT_FEEDS; + if (is_array($SQL_INSERT_FEEDS)) { + foreach ($SQL_INSERT_FEEDS as $instruction) { + $sql = sprintf($instruction, $dbOptions['bd_prefix_user']); + $stm = $c->prepare($sql); + $ok &= $stm->execute(); + } + } + } } catch (PDOException $e) { $ok = false; $dbOptions['bd_error'] = $e->getMessage(); diff --git a/cli/create-user.php b/cli/create-user.php index 5e93d4605..c790acb59 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -9,11 +9,12 @@ $options = getopt('', array( 'language:', 'email:', 'token:', + 'no-default-feeds', )); if (empty($options['user'])) { fail('Usage: ' . basename(__FILE__) . " --user username ( --password 'password' --api-password 'api_password'" . - " --language en --email user@example.net --token 'longRandomString' )"); + " --language en --email user@example.net --token 'longRandomString --no-default-feeds' )"); } $username = $options['user']; if (!ctype_alnum($username)) { @@ -33,7 +34,9 @@ $ok = FreshRSS_user_Controller::createUser($username, array( 'language' => empty($options['language']) ? '' : $options['language'], 'token' => empty($options['token']) ? '' : $options['token'], - )); + ), + !isset($options['no-default-feeds']) + ); if (!$ok) { fail('FreshRSS could not create user!'); -- cgit v1.2.3 From 7dc9862596d9b598c4b1146b29cfb3548dafce40 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 25 Oct 2016 00:10:49 +0200 Subject: Fix OPML import bug --- app/Controllers/importExportController.php | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index bb22ac739..3ba91a243 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -84,6 +84,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $ok = false; if (FreshRSS_Context::$isCli) { fwrite(STDERR, 'FreshRSS error during OPML import' . "\n"); + } else { + Minz_Log::warning('Error during OPML import'); } } } @@ -92,6 +94,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $ok = false; if (FreshRSS_Context::$isCli) { fwrite(STDERR, 'FreshRSS error during JSON stars import' . "\n"); + } else { + Minz_Log::warning('Error during JSON stars import'); } } } @@ -100,6 +104,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $ok = false; if (FreshRSS_Context::$isCli) { fwrite(STDERR, 'FreshRSS error during JSON feeds import' . "\n"); + } else { + Minz_Log::warning('Error during JSON feeds import'); } } } @@ -214,13 +220,11 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { foreach ($opml_elements as $elt) { if (isset($elt['xmlUrl'])) { // If xmlUrl exists, it means it is a feed - if (!FreshRSS_Context::$isCli) { - if ($nb_feeds >= $limits['max_feeds']) { - Minz_Log::warning(_t('feedback.sub.feed.over_max', - $limits['max_feeds'])); - $ok = false; - continue; - } + if (FreshRSS_Context::$isCli && $nb_feeds >= $limits['max_feeds']) { + Minz_Log::warning(_t('feedback.sub.feed.over_max', + $limits['max_feeds'])); + $ok = false; + continue; } if ($this->addFeedOpml($elt, $parent_cat)) { @@ -231,11 +235,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } else { // No xmlUrl? It should be a category! $limit_reached = ($nb_cats >= $limits['max_categories']); - if (!FreshRSS_Context::$isCli) { - if ($limit_reached) { - Minz_Log::warning(_t('feedback.sub.category.over_max', - $limits['max_categories'])); - } + if (!FreshRSS_Context::$isCli && $limit_reached) { + Minz_Log::warning(_t('feedback.sub.category.over_max', + $limits['max_categories'])); $ok = false; continue; } -- cgit v1.2.3