4
0

feedController.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. <?php
  2. class FreshRSS_feed_Controller extends Minz_ActionController {
  3. public function firstAction () {
  4. if (!$this->view->loginOk) {
  5. // Token is useful in the case that anonymous refresh is forbidden
  6. // and CRON task cannot be used with php command so the user can
  7. // set a CRON task to refresh his feeds by using token inside url
  8. $token = $this->view->conf->token;
  9. $token_param = Minz_Request::param ('token', '');
  10. $token_is_ok = ($token != '' && $token == $token_param);
  11. $action = Minz_Request::actionName ();
  12. if (!(($token_is_ok || Minz_Configuration::allowAnonymousRefresh()) &&
  13. $action === 'actualize')
  14. ) {
  15. Minz_Error::error (
  16. 403,
  17. array ('error' => array (Minz_Translate::t ('access_denied')))
  18. );
  19. }
  20. }
  21. }
  22. public function addAction () {
  23. $url = Minz_Request::param('url_rss', false);
  24. if ($url === false) {
  25. Minz_Request::forward(array(
  26. 'c' => 'subscription',
  27. 'a' => 'index'
  28. ), true);
  29. }
  30. $feedDAO = FreshRSS_Factory::createFeedDao();
  31. $this->catDAO = new FreshRSS_CategoryDAO ();
  32. $this->catDAO->checkDefault ();
  33. if (Minz_Request::isPost()) {
  34. @set_time_limit(300);
  35. $cat = Minz_Request::param ('category', false);
  36. if ($cat === 'nc') {
  37. $new_cat = Minz_Request::param ('new_category');
  38. if (empty($new_cat['name'])) {
  39. $cat = false;
  40. } else {
  41. $cat = $this->catDAO->addCategory($new_cat);
  42. }
  43. }
  44. if ($cat === false) {
  45. $def_cat = $this->catDAO->getDefault ();
  46. $cat = $def_cat->id ();
  47. }
  48. $user = Minz_Request::param ('http_user');
  49. $pass = Minz_Request::param ('http_pass');
  50. $params = array ();
  51. $transactionStarted = false;
  52. try {
  53. $feed = new FreshRSS_Feed ($url);
  54. $feed->_category ($cat);
  55. $httpAuth = '';
  56. if ($user != '' || $pass != '') {
  57. $httpAuth = $user . ':' . $pass;
  58. }
  59. $feed->_httpAuth ($httpAuth);
  60. $feed->load(true);
  61. $values = array (
  62. 'url' => $feed->url (),
  63. 'category' => $feed->category (),
  64. 'name' => $feed->name (),
  65. 'website' => $feed->website (),
  66. 'description' => $feed->description (),
  67. 'lastUpdate' => time (),
  68. 'httpAuth' => $feed->httpAuth (),
  69. );
  70. if ($feedDAO->searchByUrl ($values['url'])) {
  71. // on est déjà abonné à ce flux
  72. $notif = array (
  73. 'type' => 'bad',
  74. 'content' => Minz_Translate::t ('already_subscribed', $feed->name ())
  75. );
  76. Minz_Session::_param ('notification', $notif);
  77. } else {
  78. $id = $feedDAO->addFeed ($values);
  79. if (!$id) {
  80. // problème au niveau de la base de données
  81. $notif = array (
  82. 'type' => 'bad',
  83. 'content' => Minz_Translate::t ('feed_not_added', $feed->name ())
  84. );
  85. Minz_Session::_param ('notification', $notif);
  86. } else {
  87. $feed->_id ($id);
  88. $feed->faviconPrepare();
  89. $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0;
  90. $entryDAO = FreshRSS_Factory::createEntryDao();
  91. $entries = array_reverse($feed->entries()); //We want chronological order and SimplePie uses reverse order
  92. // on calcule la date des articles les plus anciens qu'on accepte
  93. $nb_month_old = $this->view->conf->old_entries;
  94. $date_min = time () - (3600 * 24 * 30 * $nb_month_old);
  95. //MySQL: http://docs.oracle.com/cd/E17952_01/refman-5.5-en/optimizing-innodb-transaction-management.html
  96. //SQLite: http://stackoverflow.com/questions/1711631/how-do-i-improve-the-performance-of-sqlite
  97. $preparedStatement = $entryDAO->addEntryPrepare();
  98. $transactionStarted = true;
  99. $feedDAO->beginTransaction();
  100. // on ajoute les articles en masse sans vérification
  101. foreach ($entries as $entry) {
  102. $values = $entry->toArray();
  103. $values['id_feed'] = $feed->id();
  104. $values['id'] = min(time(), $entry->date(true)) . uSecString();
  105. $values['is_read'] = $is_read;
  106. $entryDAO->addEntry($values, $preparedStatement);
  107. }
  108. $feedDAO->updateLastUpdate($feed->id());
  109. if ($transactionStarted) {
  110. $feedDAO->commit();
  111. }
  112. $transactionStarted = false;
  113. // ok, ajout terminé
  114. $notif = array (
  115. 'type' => 'good',
  116. 'content' => Minz_Translate::t ('feed_added', $feed->name ())
  117. );
  118. Minz_Session::_param ('notification', $notif);
  119. // permet de rediriger vers la page de conf du flux
  120. $params['id'] = $feed->id ();
  121. }
  122. }
  123. } catch (FreshRSS_BadUrl_Exception $e) {
  124. Minz_Log::record ($e->getMessage (), Minz_Log::WARNING);
  125. $notif = array (
  126. 'type' => 'bad',
  127. 'content' => Minz_Translate::t ('invalid_url', $url)
  128. );
  129. Minz_Session::_param ('notification', $notif);
  130. } catch (FreshRSS_Feed_Exception $e) {
  131. Minz_Log::record ($e->getMessage (), Minz_Log::WARNING);
  132. $notif = array (
  133. 'type' => 'bad',
  134. 'content' => Minz_Translate::t ('internal_problem_feed', Minz_Url::display(array('a' => 'logs')))
  135. );
  136. Minz_Session::_param ('notification', $notif);
  137. } catch (Minz_FileNotExistException $e) {
  138. // Répertoire de cache n'existe pas
  139. Minz_Log::record ($e->getMessage (), Minz_Log::ERROR);
  140. $notif = array (
  141. 'type' => 'bad',
  142. 'content' => Minz_Translate::t ('internal_problem_feed', Minz_Url::display(array('a' => 'logs')))
  143. );
  144. Minz_Session::_param ('notification', $notif);
  145. }
  146. if ($transactionStarted) {
  147. $feedDAO->rollBack ();
  148. }
  149. Minz_Request::forward (array ('c' => 'subscription', 'a' => 'index', 'params' => $params), true);
  150. } else {
  151. // GET request so we must ask confirmation to user
  152. Minz_View::prependTitle(Minz_Translate::t('add_rss_feed') . ' · ');
  153. $this->view->categories = $this->catDAO->listCategories();
  154. $this->view->feed = new FreshRSS_Feed($url);
  155. try {
  156. // We try to get some more information about the feed
  157. $this->view->feed->load(true);
  158. $this->view->load_ok = true;
  159. } catch (Exception $e) {
  160. $this->view->load_ok = false;
  161. }
  162. $feed = $feedDAO->searchByUrl($this->view->feed->url());
  163. if ($feed) {
  164. // Already subscribe so we redirect to the feed configuration page
  165. $notif = array(
  166. 'type' => 'bad',
  167. 'content' => Minz_Translate::t(
  168. 'already_subscribed', $feed->name()
  169. )
  170. );
  171. Minz_Session::_param('notification', $notif);
  172. Minz_Request::forward(array(
  173. 'c' => 'subscription',
  174. 'a' => 'index',
  175. 'params' => array(
  176. 'id' => $feed->id()
  177. )
  178. ), true);
  179. }
  180. }
  181. }
  182. public function truncateAction () {
  183. if (Minz_Request::isPost ()) {
  184. $id = Minz_Request::param ('id');
  185. $feedDAO = FreshRSS_Factory::createFeedDao();
  186. $n = $feedDAO->truncate($id);
  187. $notif = array(
  188. 'type' => $n === false ? 'bad' : 'good',
  189. 'content' => Minz_Translate::t ('n_entries_deleted', $n)
  190. );
  191. Minz_Session::_param ('notification', $notif);
  192. invalidateHttpCache();
  193. Minz_Request::forward (array ('c' => 'subscription',
  194. 'a' => 'index',
  195. 'params' => array('id' => $id)), true);
  196. }
  197. }
  198. public function actualizeAction () {
  199. @set_time_limit(300);
  200. $feedDAO = FreshRSS_Factory::createFeedDao();
  201. $entryDAO = FreshRSS_Factory::createEntryDao();
  202. Minz_Session::_param('actualize_feeds', false);
  203. $id = Minz_Request::param ('id');
  204. $force = Minz_Request::param ('force', false);
  205. // on créé la liste des flux à mettre à actualiser
  206. // si on veut mettre un flux à jour spécifiquement, on le met
  207. // dans la liste, mais seul (permet d'automatiser le traitement)
  208. $feeds = array ();
  209. if ($id) {
  210. $feed = $feedDAO->searchById ($id);
  211. if ($feed) {
  212. $feeds = array ($feed);
  213. }
  214. } else {
  215. $feeds = $feedDAO->listFeedsOrderUpdate($this->view->conf->ttl_default);
  216. }
  217. // on calcule la date des articles les plus anciens qu'on accepte
  218. $nb_month_old = max($this->view->conf->old_entries, 1);
  219. $date_min = time () - (3600 * 24 * 30 * $nb_month_old);
  220. $i = 0;
  221. $flux_update = 0;
  222. $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0;
  223. foreach ($feeds as $feed) {
  224. if (!$feed->lock()) {
  225. Minz_Log::record('Feed already being actualized: ' . $feed->url(), Minz_Log::NOTICE);
  226. continue;
  227. }
  228. try {
  229. $url = $feed->url();
  230. $feedHistory = $feed->keepHistory();
  231. $feed->load(false);
  232. $entries = array_reverse($feed->entries()); //We want chronological order and SimplePie uses reverse order
  233. $hasTransaction = false;
  234. if (count($entries) > 0) {
  235. //For this feed, check last n entry GUIDs already in database
  236. $existingGuids = array_fill_keys ($entryDAO->listLastGuidsByFeed ($feed->id (), count($entries) + 10), 1);
  237. $useDeclaredDate = empty($existingGuids);
  238. if ($feedHistory == -2) { //default
  239. $feedHistory = $this->view->conf->keep_history_default;
  240. }
  241. $preparedStatement = $entryDAO->addEntryPrepare();
  242. $hasTransaction = true;
  243. $feedDAO->beginTransaction();
  244. // On ne vérifie pas strictement que l'article n'est pas déjà en BDD
  245. // La BDD refusera l'ajout car (id_feed, guid) doit être unique
  246. foreach ($entries as $entry) {
  247. $eDate = $entry->date(true);
  248. if ((!isset($existingGuids[$entry->guid()])) &&
  249. (($feedHistory != 0) || ($eDate >= $date_min))) {
  250. $values = $entry->toArray();
  251. //Use declared date at first import, otherwise use discovery date
  252. $values['id'] = ($useDeclaredDate || $eDate < $date_min) ?
  253. min(time(), $eDate) . uSecString() :
  254. uTimeString();
  255. $values['is_read'] = $is_read;
  256. $entryDAO->addEntry($values, $preparedStatement);
  257. }
  258. }
  259. }
  260. if (($feedHistory >= 0) && (rand(0, 30) === 1)) {
  261. if (!$hasTransaction) {
  262. $feedDAO->beginTransaction();
  263. }
  264. $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, max($feedHistory, count($entries) + 10));
  265. if ($nb > 0) {
  266. Minz_Log::record ($nb . ' old entries cleaned in feed [' . $feed->url() . ']', Minz_Log::DEBUG);
  267. }
  268. }
  269. // on indique que le flux vient d'être mis à jour en BDD
  270. $feedDAO->updateLastUpdate ($feed->id (), 0, $hasTransaction);
  271. if ($hasTransaction) {
  272. $feedDAO->commit();
  273. }
  274. $flux_update++;
  275. if (($feed->url() !== $url)) { //HTTP 301 Moved Permanently
  276. Minz_Log::record('Feed ' . $url . ' moved permanently to ' . $feed->url(), Minz_Log::NOTICE);
  277. $feedDAO->updateFeed($feed->id(), array('url' => $feed->url()));
  278. }
  279. } catch (FreshRSS_Feed_Exception $e) {
  280. Minz_Log::record ($e->getMessage (), Minz_Log::NOTICE);
  281. $feedDAO->updateLastUpdate ($feed->id (), 1);
  282. }
  283. $feed->faviconPrepare();
  284. $feed->unlock();
  285. unset($feed);
  286. // On arrête à 10 flux pour ne pas surcharger le serveur
  287. // sauf si le paramètre $force est à vrai
  288. $i++;
  289. if ($i >= 10 && !$force) {
  290. break;
  291. }
  292. }
  293. $url = array ();
  294. if ($flux_update === 1) {
  295. // on a mis un seul flux à jour
  296. $feed = reset ($feeds);
  297. $notif = array (
  298. 'type' => 'good',
  299. 'content' => Minz_Translate::t ('feed_actualized', $feed->name ())
  300. );
  301. } elseif ($flux_update > 1) {
  302. // plusieurs flux on été mis à jour
  303. $notif = array (
  304. 'type' => 'good',
  305. 'content' => Minz_Translate::t ('n_feeds_actualized', $flux_update)
  306. );
  307. } else {
  308. // aucun flux n'a été mis à jour, oups
  309. $notif = array (
  310. 'type' => 'good',
  311. 'content' => Minz_Translate::t ('no_feed_to_refresh')
  312. );
  313. }
  314. if ($i === 1) {
  315. // Si on a voulu mettre à jour qu'un flux
  316. // on filtre l'affichage par ce flux
  317. $feed = reset ($feeds);
  318. $url['params'] = array ('get' => 'f_' . $feed->id ());
  319. }
  320. if (Minz_Request::param ('ajax', 0) === 0) {
  321. Minz_Session::_param ('notification', $notif);
  322. Minz_Request::forward ($url, true);
  323. } else {
  324. // Une requête Ajax met un seul flux à jour.
  325. // Comme en principe plusieurs requêtes ont lieu,
  326. // on indique que "plusieurs flux ont été mis à jour".
  327. // Cela permet d'avoir une notification plus proche du
  328. // ressenti utilisateur
  329. $notif = array (
  330. 'type' => 'good',
  331. 'content' => Minz_Translate::t ('feeds_actualized')
  332. );
  333. Minz_Session::_param ('notification', $notif);
  334. // et on désactive le layout car ne sert à rien
  335. $this->view->_useLayout (false);
  336. }
  337. }
  338. public function deleteAction() {
  339. if (Minz_Request::isPost()) {
  340. $id = Minz_Request::param('id');
  341. $feedDAO = FreshRSS_Factory::createFeedDao();
  342. if ($feedDAO->deleteFeed($id)) {
  343. // TODO: Delete old favicon
  344. // Remove related queries
  345. $this->view->conf->remove_query_by_get('f_' . $id);
  346. $this->view->conf->save();
  347. $notif = array(
  348. 'type' => 'good',
  349. 'content' => _t('feed_deleted')
  350. );
  351. } else {
  352. $notif = array(
  353. 'type' => 'bad',
  354. 'content' => _t('error_occured')
  355. );
  356. }
  357. Minz_Session::_param('notification', $notif);
  358. $redirect_url = Minz_Request::param('r', false, true);
  359. if ($redirect_url) {
  360. Minz_Request::forward($redirect_url);
  361. } else {
  362. Minz_Request::forward(array('c' => 'subscription', 'a' => 'index'), true);
  363. }
  364. }
  365. }
  366. }