diff options
| author | 2017-02-15 14:14:03 +0100 | |
|---|---|---|
| committer | 2017-02-15 14:14:03 +0100 | |
| commit | 5a1bb1393b4496eb35a2ffb3cc63d41c9dc1e2e5 (patch) | |
| tree | 67028e45792c575c25c92616633f64cc7a4a13eb /app/Controllers/feedController.php | |
| parent | 7e949d50320317b5c3b5a2da2bdaf324e794b2f7 (diff) | |
| parent | 5f637bd816b7323885bfe1751a1724ee59a822f6 (diff) | |
Merge remote-tracking branch 'FreshRSS/master'
Diffstat (limited to 'app/Controllers/feedController.php')
| -rwxr-xr-x | app/Controllers/feedController.php | 860 |
1 files changed, 500 insertions, 360 deletions
diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index c718fcd5c..f71f26a4e 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -1,451 +1,591 @@ <?php +/** + * Controller to handle every feed actions. + */ class FreshRSS_feed_Controller extends Minz_ActionController { - public function firstAction () { - if (!$this->view->loginOk) { + /** + * This action is called before every other action in that class. It is + * the common boiler plate for every action. It is triggered by the + * underlying framework. + */ + public function firstAction() { + if (!FreshRSS_Auth::hasAccess()) { // Token is useful in the case that anonymous refresh is forbidden // and CRON task cannot be used with php command so the user can // set a CRON task to refresh his feeds by using token inside url - $token = $this->view->conf->token; - $token_param = Minz_Request::param ('token', ''); + $token = FreshRSS_Context::$user_conf->token; + $token_param = Minz_Request::param('token', ''); $token_is_ok = ($token != '' && $token == $token_param); - $action = Minz_Request::actionName (); - if (!(($token_is_ok || Minz_Configuration::allowAnonymousRefresh()) && - $action === 'actualize') - ) { - Minz_Error::error ( - 403, - array ('error' => array (Minz_Translate::t ('access_denied'))) - ); + $action = Minz_Request::actionName(); + $allow_anonymous_refresh = FreshRSS_Context::$system_conf->allow_anonymous_refresh; + if ($action !== 'actualize' || + !($allow_anonymous_refresh || $token_is_ok)) { + Minz_Error::error(403); } } } - public function addAction () { + public static function addFeed($url, $title = '', $cat_id = 0, $new_cat_name = '', $http_auth = '') { + FreshRSS_UserDAO::touch(); @set_time_limit(300); - if (Minz_Request::isPost ()) { - $this->catDAO = new FreshRSS_CategoryDAO (); - $this->catDAO->checkDefault (); + $catDAO = new FreshRSS_CategoryDAO(); - $url = Minz_Request::param ('url_rss'); - $cat = Minz_Request::param ('category', false); - if ($cat === false) { - $def_cat = $this->catDAO->getDefault (); - $cat = $def_cat->id (); + $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(); + } + $cat_id = $cat == null ? FreshRSS_CategoryDAO::defaultCategoryId : $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' => $title != '' ? $title : $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. + * + * It can be reached by both GET and POST requests. + * + * GET request displays a form to add and configure a feed. + * Request parameter is: + * - url_rss (default: false) + * + * POST request adds a feed in database. + * Parameters are: + * - url_rss (default: false) + * - category (default: false) + * - new_category (required if category == 'nc') + * - http_user (default: false) + * - http_pass (default: false) + * It tries to get website information from RSS feed. + * If no category is given, feed is added to the default one. + * + * If url_rss is false, nothing happened. + */ + public function addAction() { + $url = Minz_Request::param('url_rss'); + + if ($url === false) { + // No url, do nothing + Minz_Request::forward(array( + 'c' => 'subscription', + 'a' => 'index' + ), true); + } + + $feedDAO = FreshRSS_Factory::createFeedDao(); + $url_redirect = array( + 'c' => 'subscription', + 'a' => 'index', + 'params' => array(), + ); + + $limits = FreshRSS_Context::$system_conf->limits; + $this->view->feeds = $feedDAO->listFeeds(); + if (count($this->view->feeds) >= $limits['max_feeds']) { + Minz_Request::bad(_t('feedback.sub.feed.over_max', $limits['max_feeds']), + $url_redirect); + } + + if (Minz_Request::isPost()) { + $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'); + $new_cat_name = isset($new_cat['name']) ? $new_cat['name'] : ''; } - $user = Minz_Request::param ('http_user'); - $pass = Minz_Request::param ('http_pass'); - $params = array (); + // HTTP information are useful if feed is protected behind a + // HTTP authentication + $user = trim(Minz_Request::param('http_user', '')); + $pass = Minz_Request::param('http_pass', ''); + $http_auth = ''; + if ($user != '' && $pass != '') { //TODO: Sanitize + $http_auth = $user . ':' . $pass; + } - $transactionStarted = false; try { - $feed = new FreshRSS_Feed ($url); - $feed->_category ($cat); - - $httpAuth = ''; - if ($user != '' || $pass != '') { - $httpAuth = $user . ':' . $pass; - } - $feed->_httpAuth ($httpAuth); - - $feed->load(true); - - $feedDAO = new FreshRSS_FeedDAO (); - $values = array ( - 'url' => $feed->url (), - 'category' => $feed->category (), - 'name' => $feed->name (), - 'website' => $feed->website (), - 'description' => $feed->description (), - 'lastUpdate' => time (), - 'httpAuth' => $feed->httpAuth (), - ); - - if ($feedDAO->searchByUrl ($values['url'])) { - // on est déjà abonné à ce flux - $notif = array ( - 'type' => 'bad', - 'content' => Minz_Translate::t ('already_subscribed', $feed->name ()) - ); - Minz_Session::_param ('notification', $notif); - } else { - $id = $feedDAO->addFeed ($values); - if (!$id) { - // problème au niveau de la base de données - $notif = array ( - 'type' => 'bad', - 'content' => Minz_Translate::t ('feed_not_added', $feed->name ()) - ); - Minz_Session::_param ('notification', $notif); - } else { - $feed->_id ($id); - $feed->faviconPrepare(); - - $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0; - - $entryDAO = new FreshRSS_EntryDAO (); - $entries = array_reverse($feed->entries()); //We want chronological order and SimplePie uses reverse order - - // on calcule la date des articles les plus anciens qu'on accepte - $nb_month_old = $this->view->conf->old_entries; - $date_min = time () - (3600 * 24 * 30 * $nb_month_old); - - $transactionStarted = true; - $feedDAO->beginTransaction (); - // on ajoute les articles en masse sans vérification - foreach ($entries as $entry) { - $values = $entry->toArray (); - $values['id_feed'] = $feed->id (); - $values['id'] = min(time(), $entry->date (true)) . uSecString(); - $values['is_read'] = $is_read; - $entryDAO->addEntry ($values); - } - $feedDAO->updateLastUpdate ($feed->id ()); - $feedDAO->commit (); - $transactionStarted = false; - - // ok, ajout terminé - $notif = array ( - 'type' => 'good', - 'content' => Minz_Translate::t ('feed_added', $feed->name ()) - ); - Minz_Session::_param ('notification', $notif); - - // permet de rediriger vers la page de conf du flux - $params['id'] = $feed->id (); - } - } + $feed = self::addFeed($url, '', $cat, $new_cat_name, $http_auth); } catch (FreshRSS_BadUrl_Exception $e) { - Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); - $notif = array ( - 'type' => 'bad', - 'content' => Minz_Translate::t ('invalid_url', $url) - ); - Minz_Session::_param ('notification', $notif); + // Given url was not a valid url! + Minz_Log::warning($e->getMessage()); + Minz_Request::bad(_t('feedback.sub.feed.invalid_url', $url), $url_redirect); } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); - $notif = array ( - 'type' => 'bad', - 'content' => Minz_Translate::t ('internal_problem_feed') - ); - Minz_Session::_param ('notification', $notif); + // 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); } catch (Minz_FileNotExistException $e) { - // Répertoire de cache n'existe pas - Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); - $notif = array ( - 'type' => 'bad', - 'content' => Minz_Translate::t ('internal_problem_feed') - ); - Minz_Session::_param ('notification', $notif); + // Cache directory doesn't exist! + Minz_Log::error($e->getMessage()); + 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); } - if ($transactionStarted) { - $feedDAO->rollBack (); + + // Entries are in DB, we redirect to feed configuration page. + $url_redirect['params']['id'] = $feed->id(); + Minz_Request::good(_t('feedback.sub.feed.added', $feed->name()), $url_redirect); + } else { + // 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 { + // We try to get more information about the feed. + $this->view->feed->load(true); + $this->view->load_ok = true; + } catch (Exception $e) { + $this->view->load_ok = false; } - Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed', 'params' => $params), true); + $feed = $feedDAO->searchByUrl($this->view->feed->url()); + if ($feed) { + // Already subscribe so we redirect to the feed configuration page. + $url_redirect['params']['id'] = $feed->id(); + Minz_Request::good(_t('feedback.sub.feed.already_subscribed', $feed->name()), $url_redirect); + } } } - public function truncateAction () { - if (Minz_Request::isPost ()) { - $id = Minz_Request::param ('id'); - $feedDAO = new FreshRSS_FeedDAO (); - $n = $feedDAO->truncate($id); - $notif = array( - 'type' => $n === false ? 'bad' : 'good', - 'content' => Minz_Translate::t ('n_entries_deleted', $n) - ); - Minz_Session::_param ('notification', $notif); - invalidateHttpCache(); - Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed', 'params' => array('id' => $id)), true); + /** + * This action remove entries from a given feed. + * + * It should be reached by a POST action. + * + * Parameter is: + * - id (default: false) + */ + public function truncateAction() { + $id = Minz_Request::param('id'); + $url_redirect = array( + 'c' => 'subscription', + 'a' => 'index', + 'params' => array('id' => $id) + ); + + if (!Minz_Request::isPost()) { + Minz_Request::forward($url_redirect, true); + } + + $feedDAO = FreshRSS_Factory::createFeedDao(); + $n = $feedDAO->truncate($id); + + invalidateHttpCache(); + if ($n === false) { + Minz_Request::bad(_t('feedback.sub.feed.error'), $url_redirect); + } else { + Minz_Request::good(_t('feedback.sub.feed.n_entries_deleted', $n), $url_redirect); } } - public function actualizeAction () { + public static function actualizeFeed($feed_id, $feed_url, $force, $simplePiePush = null, $isNewFeed = false) { @set_time_limit(300); - $feedDAO = new FreshRSS_FeedDAO (); - $entryDAO = new FreshRSS_EntryDAO (); + $feedDAO = FreshRSS_Factory::createFeedDao(); + $entryDAO = FreshRSS_Factory::createEntryDao(); - Minz_Session::_param('actualize_feeds', false); - $id = Minz_Request::param ('id'); - $force = Minz_Request::param ('force', false); - - // on créé la liste des flux à mettre à actualiser - // si on veut mettre un flux à jour spécifiquement, on le met - // dans la liste, mais seul (permet d'automatiser le traitement) - $feeds = array (); - if ($id) { - $feed = $feedDAO->searchById ($id); + // Create a list of feeds to actualize. + // 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 ($feed_id > 0 || $feed_url) { + $feed = $feed_id > 0 ? $feedDAO->searchById($feed_id) : $feedDAO->searchByUrl($feed_url); if ($feed) { - $feeds = array ($feed); + $feeds[] = $feed; } } else { - $feeds = $feedDAO->listFeedsOrderUpdate (); + $feeds = $feedDAO->listFeedsOrderUpdate(-1); } - // on calcule la date des articles les plus anciens qu'on accepte - $nb_month_old = max($this->view->conf->old_entries, 1); - $date_min = time () - (3600 * 24 * 30 * $nb_month_old); + // Calculate date of oldest entries we accept in DB. + $nb_month_old = max(FreshRSS_Context::$user_conf->old_entries, 1); + $date_min = time() - (3600 * 24 * 30 * $nb_month_old); - $i = 0; - $flux_update = 0; - $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0; + // PubSubHubbub support + $pubsubhubbubEnabledGeneral = FreshRSS_Context::$system_conf->pubsubhubbub_enabled; + $pshbMinAge = time() - (3600 * 24); //TODO: Make a configuration. + + $updated_feeds = 0; + $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; foreach ($feeds as $feed) { + $url = $feed->url(); //For detection of HTTP 301 + + $pubSubHubbubEnabled = $pubsubhubbubEnabledGeneral && $feed->pubSubHubbubEnabled(); + 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); + 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 ((!$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) { + continue; //Nothing newer from other users + } + //Minz_Log::debug($feed->url() . ' was updated at ' . date('c', $mtime) . ' by another user'); + //Will take advantage of the newer cache + } + if (!$feed->lock()) { - Minz_Log::record('Feed already being actualized: ' . $feed->url(), Minz_Log::NOTICE); + Minz_Log::notice('Feed already being actualized: ' . $feed->url()); continue; } + try { - $url = $feed->url(); - $feedHistory = $feed->keepHistory(); + if ($simplePiePush) { + $feed->loadEntries($simplePiePush); //Used by PubSubHubbub + } else { + $feed->load(false, $isNewFeed); + } + } catch (FreshRSS_Feed_Exception $e) { + Minz_Log::warning($e->getMessage()); + $feedDAO->updateLastUpdate($feed->id(), true); + $feed->unlock(); + continue; + } - $feed->load(false); - $entries = array_reverse($feed->entries()); //We want chronological order and SimplePie uses reverse order - $hasTransaction = false; + $feed_history = $feed->keepHistory(); + 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; + } - if (count($entries) > 0) { - //For this feed, check last n entry GUIDs already in database - $existingGuids = array_fill_keys ($entryDAO->listLastGuidsByFeed ($feed->id (), count($entries) + 10), 1); - $useDeclaredDate = empty($existingGuids); + // We want chronological order and SimplePie uses reverse order. + $entries = array_reverse($feed->entries()); + if (count($entries) > 0) { + $newGuids = array(); + foreach ($entries as $entry) { + $newGuids[] = safe_ascii($entry->guid()); + } + // For this feed, check existing GUIDs already in database. + $existingHashForGuids = $entryDAO->listHashForFeedGuids($feed->id(), $newGuids); + unset($newGuids); + + $oldGuids = array(); + // Add entries in database if possible. + foreach ($entries as $entry) { + $entry_date = $entry->date(true); + if (isset($existingHashForGuids[$entry->guid()])) { + $existingHash = $existingHashForGuids[$entry->guid()]; + if (strcasecmp($existingHash, $entry->hash()) === 0 || trim($existingHash, '0') == '') { + //This entry already exists and is unchanged. TODO: Remove the test with the zero'ed hash in FreshRSS v1.3 + $oldGuids[] = $entry->guid(); + } else { //This entry already exists but has been updated + //Minz_Log::debug('Entry with GUID `' . $entry->guid() . '` updated in feed ' . $feed->id() . + //', old hash ' . $existingHash . ', new hash ' . $entry->hash()); + //TODO: Make an updated/is_read policy by feed, in addition to the global one. + $entry->_isRead(FreshRSS_Context::$user_conf->mark_updated_article_unread ? false : null); //Change is_read according to policy. + if (!$entryDAO->inTransaction()) { + $entryDAO->beginTransaction(); + } + $entryDAO->updateEntry($entry->toArray()); + } + } elseif ($feed_history == 0 && $entry_date < $date_min) { + // This entry should not be added considering configuration and date. + $oldGuids[] = $entry->guid(); + } else { + 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 { + $id = uTimeString(); + $entry->_isRead($is_read); + } + $entry->_id($id); - if ($feedHistory == -2) { //default - $feedHistory = $this->view->conf->keep_history_default; - } + $entry = Minz_ExtensionManager::callHook('entry_before_insert', $entry); + if ($entry === null) { + // An extension has returned a null value, there is nothing to insert. + continue; + } - $hasTransaction = true; - $feedDAO->beginTransaction(); - - // On ne vérifie pas strictement que l'article n'est pas déjà en BDD - // La BDD refusera l'ajout car (id_feed, guid) doit être unique - foreach ($entries as $entry) { - $eDate = $entry->date (true); - if ((!isset ($existingGuids[$entry->guid ()])) && - (($feedHistory != 0) || ($eDate >= $date_min))) { - $values = $entry->toArray (); - //Use declared date at first import, otherwise use discovery date - $values['id'] = ($useDeclaredDate || $eDate < $date_min) ? - min(time(), $eDate) . uSecString() : - uTimeString(); - $values['is_read'] = $is_read; - $entryDAO->addEntry ($values); + if ($pubSubHubbubEnabled && !$simplePiePush) { //We use push, but have discovered an article by pull! + $text = 'An article was discovered by pull although we use PubSubHubbub!: Feed ' . $url . ' GUID ' . $entry->guid(); + file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND); + Minz_Log::warning($text); + $pubSubHubbubEnabled = false; + $feed->pubSubHubbubError(true); } + + if (!$entryDAO->inTransaction()) { + $entryDAO->beginTransaction(); + } + $entryDAO->addEntry($entry->toArray()); } } + $entryDAO->updateLastSeen($feed->id(), $oldGuids, $mtime); + } - if (($feedHistory >= 0) && (rand(0, 30) === 1)) { - if (!$hasTransaction) { - $feedDAO->beginTransaction(); - } - $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, max($feedHistory, count($entries) + 10)); - if ($nb > 0) { - Minz_Log::record ($nb . ' old entries cleaned in feed [' . $feed->url() . ']', Minz_Log::DEBUG); - } + if ($feed_history >= 0 && rand(0, 30) === 1) { + // TODO: move this function in web cron when available (see entry::purge) + // Remove old entries once in 30. + if (!$entryDAO->inTransaction()) { + $entryDAO->beginTransaction(); } - // on indique que le flux vient d'être mis à jour en BDD - $feedDAO->updateLastUpdate ($feed->id (), 0, $hasTransaction); - if ($hasTransaction) { - $feedDAO->commit(); + $nb = $feedDAO->cleanOldEntries($feed->id(), + $date_min, + max($feed_history, count($entries) + 10)); + if ($nb > 0) { + Minz_Log::debug($nb . ' old entries cleaned in feed [' . + $feed->url() . ']'); } - $flux_update++; - if ($feed->url() !== $url) { //URL has changed (auto-discovery) - $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); + } + + $feedDAO->updateLastUpdate($feed->id(), false, $entryDAO->inTransaction(), $mtime); + if ($entryDAO->inTransaction()) { + $entryDAO->commit(); + } + + if ($feed->hubUrl() && $feed->selfUrl()) { //selfUrl has priority for PubSubHubbub + if ($feed->selfUrl() !== $url) { //https://code.google.com/p/pubsubhubbub/wiki/MovingFeedsOrChangingHubs + $selfUrl = checkUrl($feed->selfUrl()); + if ($selfUrl) { + Minz_Log::debug('PubSubHubbub unsubscribe ' . $feed->url()); + if (!$feed->pubSubHubbubSubscribe(false)) { //Unsubscribe + Minz_Log::warning('Error while PubSubHubbub unsubscribing from ' . $feed->url()); + } + $feed->_url($selfUrl, false); + Minz_Log::notice('Feed ' . $url . ' canonical address moved to ' . $feed->url()); + $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); + } } - } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::record ($e->getMessage (), Minz_Log::NOTICE); - $feedDAO->updateLastUpdate ($feed->id (), 1); + } + elseif ($feed->url() !== $url) { // HTTP 301 Moved Permanently + Minz_Log::notice('Feed ' . $url . ' moved permanently to ' . $feed->url()); + $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); } $feed->faviconPrepare(); + if ($pubsubhubbubEnabledGeneral && $feed->pubSubHubbubPrepare()) { + Minz_Log::notice('PubSubHubbub subscribe ' . $feed->url()); + if (!$feed->pubSubHubbubSubscribe(true)) { //Subscribe + Minz_Log::warning('Error while PubSubHubbub subscribing to ' . $feed->url()); + } + } $feed->unlock(); + $updated_feeds++; unset($feed); - // On arrête à 10 flux pour ne pas surcharger le serveur - // sauf si le paramètre $force est à vrai - $i++; - if ($i >= 10 && !$force) { + // No more than 10 feeds unless $force is true to avoid overloading + // the server. + if ($updated_feeds >= 10 && !$force) { break; } } + return array($updated_feeds, reset($feeds)); + } - $url = array (); - if ($flux_update === 1) { - // on a mis un seul flux à jour - $feed = reset ($feeds); - $notif = array ( - 'type' => 'good', - 'content' => Minz_Translate::t ('feed_actualized', $feed->name ()) - ); - } elseif ($flux_update > 1) { - // plusieurs flux on été mis à jour - $notif = array ( - 'type' => 'good', - 'content' => Minz_Translate::t ('n_feeds_actualized', $flux_update) - ); - } else { - // aucun flux n'a été mis à jour, oups - $notif = array ( - 'type' => 'good', - 'content' => Minz_Translate::t ('no_feed_to_refresh') - ); - } + /** + * 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'); - if ($i === 1) { - // Si on a voulu mettre à jour qu'un flux - // on filtre l'affichage par ce flux - $feed = reset ($feeds); - $url['params'] = array ('get' => 'f_' . $feed->id ()); - } + list($updated_feeds, $feed) = self::actualizeFeed($id, $url, $force); - if (Minz_Request::param ('ajax', 0) === 0) { - Minz_Session::_param ('notification', $notif); - Minz_Request::forward ($url, true); - } else { - // Une requête Ajax met un seul flux à jour. - // Comme en principe plusieurs requêtes ont lieu, - // on indique que "plusieurs flux ont été mis à jour". - // Cela permet d'avoir une notification plus proche du - // ressenti utilisateur - $notif = array ( + if (Minz_Request::param('ajax')) { + // Most of the time, ajax request is for only one feed. But since + // there are several parallel requests, we should return that there + // are several updated feeds. + $notif = array( 'type' => 'good', - 'content' => Minz_Translate::t ('feeds_actualized') + 'content' => _t('feedback.sub.feed.actualizeds') ); - Minz_Session::_param ('notification', $notif); - // et on désactive le layout car ne sert à rien - $this->view->_useLayout (false); + Minz_Session::_param('notification', $notif); + // No layout in ajax request. + $this->view->_useLayout(false); + } else { + // Redirect to the main page with correct notification. + if ($updated_feeds === 1) { + Minz_Request::good(_t('feedback.sub.feed.actualized', $feed->name()), array( + 'params' => array('get' => 'f_' . $feed->id()) + )); + } elseif ($updated_feeds > 1) { + Minz_Request::good(_t('feedback.sub.feed.n_actualized', $updated_feeds), array()); + } else { + Minz_Request::good(_t('feedback.sub.feed.no_refresh'), array()); + } } + return $updated_feeds; } - public function massiveImportAction () { - @set_time_limit(300); - - $this->catDAO = new FreshRSS_CategoryDAO (); - $this->catDAO->checkDefault (); - - $entryDAO = new FreshRSS_EntryDAO (); - $feedDAO = new FreshRSS_FeedDAO (); + public static function renameFeed($feed_id, $feed_name) { + if ($feed_id <= 0 || $feed_name == '') { + return false; + } + FreshRSS_UserDAO::touch(); + $feedDAO = FreshRSS_Factory::createFeedDao(); + return $feedDAO->updateFeed($feed_id, array('name' => $feed_name)); + } - $categories = Minz_Request::param ('categories', array (), true); - $feeds = Minz_Request::param ('feeds', array (), true); + public static function moveFeed($feed_id, $cat_id, $new_cat_name = '') { + if ($feed_id <= 0 || ($cat_id <= 0 && $new_cat_name == '')) { + return false; + } + FreshRSS_UserDAO::touch(); - // on ajoute les catégories en masse dans une fonction à part - $this->addCategories ($categories); + $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(); + $cat_id = FreshRSS_CategoryDAO::defaultCategoryId; + } - // on calcule la date des articles les plus anciens qu'on accepte - $nb_month_old = $this->view->conf->old_entries; - $date_min = time () - (3600 * 24 * 30 * $nb_month_old); + $feedDAO = FreshRSS_Factory::createFeedDao(); + return $feedDAO->updateFeed($feed_id, array('category' => $cat_id)); + } - // la variable $error permet de savoir si une erreur est survenue - // Le but est de ne pas arrêter l'import même en cas d'erreur - // L'utilisateur sera mis au courant s'il y a eu des erreurs, mais - // ne connaîtra pas les détails. Ceux-ci seront toutefois logguées - $error = false; - $i = 0; - foreach ($feeds as $feed) { - try { - $values = array ( - 'id' => $feed->id (), - 'url' => $feed->url (), - 'category' => $feed->category (), - 'name' => $feed->name (), - 'website' => $feed->website (), - 'description' => $feed->description (), - 'lastUpdate' => 0, - 'httpAuth' => $feed->httpAuth () - ); - - // ajout du flux que s'il n'est pas déjà en BDD - if (!$feedDAO->searchByUrl ($values['url'])) { - $id = $feedDAO->addFeed ($values); - if ($id) { - $feed->_id ($id); - $feed->faviconPrepare(); - } else { - $error = true; - } - } - } catch (FreshRSS_Feed_Exception $e) { - $error = true; - Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); - } + /** + * This action changes the category of a feed. + * + * This page must be reached by a POST request. + * + * Parameters are: + * - f_id (default: false) + * - c_id (default: false) + * If c_id is false, default category is used. + * + * @todo should handle order of the feed inside the category. + */ + public function moveAction() { + if (!Minz_Request::isPost()) { + Minz_Request::forward(array('c' => 'subscription'), true); } - if ($error) { - $res = Minz_Translate::t ('feeds_imported_with_errors'); + $feed_id = Minz_Request::param('f_id'); + $cat_id = Minz_Request::param('c_id'); + + if (self::moveFeed($feed_id, $cat_id)) { + // TODO: return something useful } else { - $res = Minz_Translate::t ('feeds_imported'); + Minz_Log::warning('Cannot move feed `' . $feed_id . '` ' . + 'in the category `' . $cat_id . '`'); + Minz_Error::error(404); } - - $notif = array ( - 'type' => 'good', - 'content' => $res - ); - Minz_Session::_param ('notification', $notif); - Minz_Session::_param ('actualize_feeds', true); - - // et on redirige vers la page d'accueil - Minz_Request::forward (array ( - 'c' => 'index', - 'a' => 'index' - ), true); } - public function deleteAction () { - if (Minz_Request::isPost ()) { - $type = Minz_Request::param ('type', 'feed'); - $id = Minz_Request::param ('id'); - - $feedDAO = new FreshRSS_FeedDAO (); - if ($type == 'category') { - if ($feedDAO->deleteFeedByCategory ($id)) { - $notif = array ( - 'type' => 'good', - 'content' => Minz_Translate::t ('category_emptied') - ); - //TODO: Delete old favicons - } else { - $notif = array ( - 'type' => 'bad', - 'content' => Minz_Translate::t ('error_occured') - ); - } - } else { - if ($feedDAO->deleteFeed ($id)) { - $notif = array ( - 'type' => 'good', - 'content' => Minz_Translate::t ('feed_deleted') - ); - //TODO: Delete old favicon - } else { - $notif = array ( - 'type' => 'bad', - 'content' => Minz_Translate::t ('error_occured') - ); - } - } + public static function deleteFeed($feed_id) { + FreshRSS_UserDAO::touch(); + $feedDAO = FreshRSS_Factory::createFeedDao(); + if ($feedDAO->deleteFeed($feed_id)) { + // TODO: Delete old favicon - Minz_Session::_param ('notification', $notif); + // 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(); - if ($type == 'category') { - Minz_Request::forward (array ('c' => 'configure', 'a' => 'categorize'), true); - } else { - Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed'), true); - } + return true; } + return false; } - private function addCategories ($categories) { - foreach ($categories as $cat) { - if (!$this->catDAO->searchByName ($cat->name ())) { - $values = array ( - 'id' => $cat->id (), - 'name' => $cat->name (), - ); - $catDAO->addCategory ($values); - } + /** + * This action deletes a feed. + * + * This page must be reached by a POST request. + * If there are related queries, they are deleted too. + * + * Parameters are: + * - id (default: false) + * - r (default: false) + * r permits to redirect to a given page at the end of this action. + * + * @todo handle "r" redirection in Minz_Request::forward()? + */ + public function deleteAction() { + $redirect_url = Minz_Request::param('r', false, true); + 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'); + + 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); } } } |
