EntryDAO.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. <?php
  2. class FreshRSS_EntryDAO extends Minz_ModelPdo {
  3. public function addEntry ($valuesTmp) {
  4. $sql = 'INSERT INTO `' . $this->prefix . 'entry`(id, guid, title, author, content_bin, link, date, is_read, is_favorite, id_feed, tags) '
  5. . 'VALUES(?, ?, ?, ?, COMPRESS(?), ?, ?, ?, ?, ?, ?)';
  6. $stm = $this->bd->prepare ($sql);
  7. $values = array (
  8. $valuesTmp['id'],
  9. substr($valuesTmp['guid'], 0, 760),
  10. substr($valuesTmp['title'], 0, 255),
  11. substr($valuesTmp['author'], 0, 255),
  12. $valuesTmp['content'],
  13. substr($valuesTmp['link'], 0, 1023),
  14. $valuesTmp['date'],
  15. $valuesTmp['is_read'] ? 1 : 0,
  16. $valuesTmp['is_favorite'] ? 1 : 0,
  17. $valuesTmp['id_feed'],
  18. substr($valuesTmp['tags'], 0, 1023),
  19. );
  20. if ($stm && $stm->execute ($values)) {
  21. return $this->bd->lastInsertId();
  22. } else {
  23. $info = $stm->errorInfo();
  24. if ((int)($info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries
  25. Minz_Log::record ('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
  26. . ' while adding entry in feed ' . $valuesTmp['id_feed'] . ' with title: ' . $valuesTmp['title'], Minz_Log::ERROR);
  27. } /*else {
  28. Minz_Log::record ('SQL error ' . $info[0] . ': ' . $info[1] . ' ' . $info[2]
  29. . ' while adding entry in feed ' . $valuesTmp['id_feed'] . ' with title: ' . $valuesTmp['title'], Minz_Log::DEBUG);
  30. }*/
  31. return false;
  32. }
  33. }
  34. public function addEntryObject($entry, $conf, $feedHistory) {
  35. $existingGuids = array_fill_keys(
  36. $this->listLastGuidsByFeed($entry->feed(), 20), 1
  37. );
  38. $nb_month_old = max($conf->old_entries, 1);
  39. $date_min = time() - (3600 * 24 * 30 * $nb_month_old);
  40. $eDate = $entry->date(true);
  41. if ($feedHistory == -2) {
  42. $feedHistory = $conf->keep_history_default;
  43. }
  44. if (!isset($existingGuids[$entry->guid()]) &&
  45. ($feedHistory != 0 || $eDate >= $date_min)) {
  46. $values = $entry->toArray();
  47. $useDeclaredDate = empty($existingGuids);
  48. $values['id'] = ($useDeclaredDate || $eDate < $date_min) ?
  49. min(time(), $eDate) . uSecString() :
  50. uTimeString();
  51. return $this->addEntry($values);
  52. }
  53. // We don't return Entry object to avoid a research in DB
  54. return -1;
  55. }
  56. public function markFavorite($ids, $is_favorite = true) {
  57. if (!is_array($ids)) {
  58. $ids = array($ids);
  59. }
  60. $sql = 'UPDATE `' . $this->prefix . 'entry` e '
  61. . 'SET e.is_favorite = ? '
  62. . 'WHERE e.id IN (' . str_repeat('?,', count($ids) - 1). '?)';
  63. $values = array ($is_favorite ? 1 : 0);
  64. $values = array_merge($values, $ids);
  65. $stm = $this->bd->prepare ($sql);
  66. if ($stm && $stm->execute ($values)) {
  67. return $stm->rowCount();
  68. } else {
  69. $info = $stm->errorInfo();
  70. Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
  71. return false;
  72. }
  73. }
  74. public function markRead($ids, $is_read = true) {
  75. if (is_array($ids)) {
  76. if (count($ids) < 6) { //Speed heuristics
  77. $affected = 0;
  78. foreach ($ids as $id) {
  79. $affected += $this->markRead($id, $is_read);
  80. }
  81. return $affected;
  82. }
  83. $this->bd->beginTransaction();
  84. $sql = 'UPDATE `' . $this->prefix . 'entry` e '
  85. . 'SET e.is_read = ? '
  86. . 'WHERE e.id IN (' . str_repeat('?,', count($ids) - 1). '?)';
  87. $values = array($is_read ? 1 : 0);
  88. $values = array_merge($values, $ids);
  89. $stm = $this->bd->prepare($sql);
  90. if (!($stm && $stm->execute($values))) {
  91. $info = $stm->errorInfo();
  92. Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
  93. $this->bd->rollBack();
  94. return false;
  95. }
  96. $affected = $stm->rowCount();
  97. if ($affected > 0) {
  98. $sql = 'UPDATE `' . $this->prefix . 'feed` f '
  99. . 'INNER JOIN ('
  100. . 'SELECT e.id_feed, '
  101. . 'COUNT(CASE WHEN e.is_read = 0 THEN 1 END) AS nbUnreads, '
  102. . 'COUNT(e.id) AS nbEntries '
  103. . 'FROM `' . $this->prefix . 'entry` e '
  104. . 'GROUP BY e.id_feed'
  105. . ') x ON x.id_feed=f.id '
  106. . 'SET f.cache_nbEntries=x.nbEntries, f.cache_nbUnreads=x.nbUnreads';
  107. $stm = $this->bd->prepare($sql);
  108. if (!($stm && $stm->execute())) {
  109. $info = $stm->errorInfo();
  110. Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
  111. $this->bd->rollBack();
  112. return false;
  113. }
  114. }
  115. $this->bd->commit();
  116. return $affected;
  117. } else {
  118. $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
  119. . 'SET e.is_read = ?,'
  120. . 'f.cache_nbUnreads=f.cache_nbUnreads' . ($is_read ? '-' : '+') . '1 '
  121. . 'WHERE e.id=?';
  122. $values = array($is_read ? 1 : 0, $ids);
  123. $stm = $this->bd->prepare($sql);
  124. if ($stm && $stm->execute($values)) {
  125. return $stm->rowCount();
  126. } else {
  127. $info = $stm->errorInfo();
  128. Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
  129. return false;
  130. }
  131. }
  132. }
  133. public function markReadEntries ($idMax = 0, $onlyFavorites = false, $priorityMin = 0) {
  134. if ($idMax == 0) {
  135. $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
  136. . 'SET e.is_read = 1, f.cache_nbUnreads=0 '
  137. . 'WHERE e.is_read = 0';
  138. if ($onlyFavorites) {
  139. $sql .= ' AND e.is_favorite = 1';
  140. } elseif ($priorityMin >= 0) {
  141. $sql .= ' AND f.priority > ' . intval($priorityMin);
  142. }
  143. $stm = $this->bd->prepare ($sql);
  144. if ($stm && $stm->execute ()) {
  145. return $stm->rowCount();
  146. } else {
  147. $info = $stm->errorInfo();
  148. Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
  149. return false;
  150. }
  151. } else {
  152. $this->bd->beginTransaction ();
  153. $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
  154. . 'SET e.is_read = 1 '
  155. . 'WHERE e.is_read = 0 AND e.id <= ?';
  156. if ($onlyFavorites) {
  157. $sql .= ' AND e.is_favorite = 1';
  158. } elseif ($priorityMin >= 0) {
  159. $sql .= ' AND f.priority > ' . intval($priorityMin);
  160. }
  161. $values = array ($idMax);
  162. $stm = $this->bd->prepare ($sql);
  163. if (!($stm && $stm->execute ($values))) {
  164. $info = $stm->errorInfo();
  165. Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
  166. $this->bd->rollBack ();
  167. return false;
  168. }
  169. $affected = $stm->rowCount();
  170. if ($affected > 0) {
  171. $sql = 'UPDATE `' . $this->prefix . 'feed` f '
  172. . 'LEFT OUTER JOIN ('
  173. . 'SELECT e.id_feed, '
  174. . 'COUNT(*) AS nbUnreads '
  175. . 'FROM `' . $this->prefix . 'entry` e '
  176. . 'WHERE e.is_read = 0 '
  177. . 'GROUP BY e.id_feed'
  178. . ') x ON x.id_feed=f.id '
  179. . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0)';
  180. $stm = $this->bd->prepare ($sql);
  181. if (!($stm && $stm->execute ())) {
  182. $info = $stm->errorInfo();
  183. Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
  184. $this->bd->rollBack ();
  185. return false;
  186. }
  187. }
  188. $this->bd->commit ();
  189. return $affected;
  190. }
  191. }
  192. public function markReadCat ($id, $idMax = 0) {
  193. if ($idMax == 0) {
  194. $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
  195. . 'SET e.is_read = 1, f.cache_nbUnreads=0 '
  196. . 'WHERE f.category = ? AND e.is_read = 0';
  197. $values = array ($id);
  198. $stm = $this->bd->prepare ($sql);
  199. if ($stm && $stm->execute ($values)) {
  200. return $stm->rowCount();
  201. } else {
  202. $info = $stm->errorInfo();
  203. Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
  204. return false;
  205. }
  206. } else {
  207. $this->bd->beginTransaction ();
  208. $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
  209. . 'SET e.is_read = 1 '
  210. . 'WHERE f.category = ? AND e.is_read = 0 AND e.id <= ?';
  211. $values = array ($id, $idMax);
  212. $stm = $this->bd->prepare ($sql);
  213. if (!($stm && $stm->execute ($values))) {
  214. $info = $stm->errorInfo();
  215. Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
  216. $this->bd->rollBack ();
  217. return false;
  218. }
  219. $affected = $stm->rowCount();
  220. if ($affected > 0) {
  221. $sql = 'UPDATE `' . $this->prefix . 'feed` f '
  222. . 'LEFT OUTER JOIN ('
  223. . 'SELECT e.id_feed, '
  224. . 'COUNT(*) AS nbUnreads '
  225. . 'FROM `' . $this->prefix . 'entry` e '
  226. . 'WHERE e.is_read = 0 '
  227. . 'GROUP BY e.id_feed'
  228. . ') x ON x.id_feed=f.id '
  229. . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0) '
  230. . 'WHERE f.category = ?';
  231. $values = array ($id);
  232. $stm = $this->bd->prepare ($sql);
  233. if (!($stm && $stm->execute ($values))) {
  234. $info = $stm->errorInfo();
  235. Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
  236. $this->bd->rollBack ();
  237. return false;
  238. }
  239. }
  240. $this->bd->commit ();
  241. return $affected;
  242. }
  243. }
  244. public function markReadCatName($name, $idMax = 0) {
  245. if ($idMax == 0) {
  246. $sql = 'UPDATE `' . $this->prefix . 'entry` e '
  247. . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
  248. . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category '
  249. . 'SET e.is_read = 1, f.cache_nbUnreads=0 '
  250. . 'WHERE c.name = ?';
  251. $values = array($name);
  252. $stm = $this->bd->prepare($sql);
  253. if ($stm && $stm->execute($values)) {
  254. return $stm->rowCount();
  255. } else {
  256. $info = $stm->errorInfo();
  257. Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
  258. return false;
  259. }
  260. } else {
  261. $this->bd->beginTransaction();
  262. $sql = 'UPDATE `' . $this->prefix . 'entry` e '
  263. . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
  264. . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category '
  265. . 'SET e.is_read = 1 '
  266. . 'WHERE c.name = ? AND e.id <= ?';
  267. $values = array($name, $idMax);
  268. $stm = $this->bd->prepare($sql);
  269. if (!($stm && $stm->execute($values))) {
  270. $info = $stm->errorInfo();
  271. Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
  272. $this->bd->rollBack();
  273. return false;
  274. }
  275. $affected = $stm->rowCount();
  276. if ($affected > 0) {
  277. $sql = 'UPDATE `' . $this->prefix . 'feed` f '
  278. . 'LEFT OUTER JOIN ('
  279. . 'SELECT e.id_feed, '
  280. . 'COUNT(*) AS nbUnreads '
  281. . 'FROM `' . $this->prefix . 'entry` e '
  282. . 'WHERE e.is_read = 0 '
  283. . 'GROUP BY e.id_feed'
  284. . ') x ON x.id_feed=f.id '
  285. . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category '
  286. . 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0) '
  287. . 'WHERE c.name = ?';
  288. $values = array($name);
  289. $stm = $this->bd->prepare($sql);
  290. if (!($stm && $stm->execute($values))) {
  291. $info = $stm->errorInfo();
  292. Minz_Log::record('SQL error : ' . $info[2], Minz_Log::ERROR);
  293. $this->bd->rollBack();
  294. return false;
  295. }
  296. }
  297. $this->bd->commit();
  298. return $affected;
  299. }
  300. }
  301. public function markReadFeed ($id, $idMax = 0) {
  302. if ($idMax == 0) {
  303. $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
  304. . 'SET e.is_read = 1, f.cache_nbUnreads=0 '
  305. . 'WHERE f.id=? AND e.is_read = 0';
  306. $values = array ($id);
  307. $stm = $this->bd->prepare ($sql);
  308. if ($stm && $stm->execute ($values)) {
  309. return $stm->rowCount();
  310. } else {
  311. $info = $stm->errorInfo();
  312. Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
  313. return false;
  314. }
  315. } else {
  316. $this->bd->beginTransaction ();
  317. $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id '
  318. . 'SET e.is_read = 1 '
  319. . 'WHERE f.id=? AND e.is_read = 0 AND e.id <= ?';
  320. $values = array ($id, $idMax);
  321. $stm = $this->bd->prepare ($sql);
  322. if (!($stm && $stm->execute ($values))) {
  323. $info = $stm->errorInfo();
  324. Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
  325. $this->bd->rollBack ();
  326. return false;
  327. }
  328. $affected = $stm->rowCount();
  329. if ($affected > 0) {
  330. $sql = 'UPDATE `' . $this->prefix . 'feed` f '
  331. . 'SET f.cache_nbUnreads=f.cache_nbUnreads-' . $affected
  332. . ' WHERE f.id=?';
  333. $values = array ($id);
  334. $stm = $this->bd->prepare ($sql);
  335. if (!($stm && $stm->execute ($values))) {
  336. $info = $stm->errorInfo();
  337. Minz_Log::record ('SQL error : ' . $info[2], Minz_Log::ERROR);
  338. $this->bd->rollBack ();
  339. return false;
  340. }
  341. }
  342. $this->bd->commit ();
  343. return $affected;
  344. }
  345. }
  346. public function searchByGuid ($feed_id, $id) {
  347. // un guid est unique pour un flux donné
  348. $sql = 'SELECT id, guid, title, author, UNCOMPRESS(content_bin) AS content, link, date, is_read, is_favorite, id_feed, tags '
  349. . 'FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid=?';
  350. $stm = $this->bd->prepare ($sql);
  351. $values = array (
  352. $feed_id,
  353. $id
  354. );
  355. $stm->execute ($values);
  356. $res = $stm->fetchAll (PDO::FETCH_ASSOC);
  357. $entries = self::daoToEntry ($res);
  358. return isset ($entries[0]) ? $entries[0] : null;
  359. }
  360. public function searchById ($id) {
  361. $sql = 'SELECT id, guid, title, author, UNCOMPRESS(content_bin) AS content, link, date, is_read, is_favorite, id_feed, tags '
  362. . 'FROM `' . $this->prefix . 'entry` WHERE id=?';
  363. $stm = $this->bd->prepare ($sql);
  364. $values = array ($id);
  365. $stm->execute ($values);
  366. $res = $stm->fetchAll (PDO::FETCH_ASSOC);
  367. $entries = self::daoToEntry ($res);
  368. return isset ($entries[0]) ? $entries[0] : null;
  369. }
  370. private function sqlListWhere($type = 'a', $id = '', $state = null , $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) {
  371. if (!$state) {
  372. $state = FreshRSS_Configuration::STATE_ALL;
  373. }
  374. $where = '';
  375. $joinFeed = false;
  376. $values = array();
  377. switch ($type) {
  378. case 'a':
  379. $where .= 'f.priority > 0 ';
  380. $joinFeed = true;
  381. break;
  382. case 's':
  383. // This is deprecated. The favorite button does not exist anymore
  384. $where .= 'e1.is_favorite = 1 ';
  385. break;
  386. case 'c':
  387. $where .= 'f.category = ? ';
  388. $values[] = intval($id);
  389. $joinFeed = true;
  390. break;
  391. case 'f':
  392. $where .= 'e1.id_feed = ? ';
  393. $values[] = intval($id);
  394. break;
  395. case 'A':
  396. $where .= '1 ';
  397. break;
  398. default:
  399. throw new FreshRSS_EntriesGetter_Exception ('Bad type in Entry->listByType: [' . $type . ']!');
  400. }
  401. if ($state & FreshRSS_Configuration::STATE_NOT_READ) {
  402. if (!($state & FreshRSS_Configuration::STATE_READ)) {
  403. $where .= 'AND e1.is_read = 0 ';
  404. }
  405. }
  406. if ($state & FreshRSS_Configuration::STATE_READ) {
  407. if (!($state & FreshRSS_Configuration::STATE_NOT_READ)) {
  408. $where .= 'AND e1.is_read = 1 ';
  409. }
  410. }
  411. if ($state & FreshRSS_Configuration::STATE_NOT_FAVORITE) {
  412. if (!($state & FreshRSS_Configuration::STATE_FAVORITE)) {
  413. $where .= 'AND e1.is_favorite = 0 ';
  414. }
  415. }
  416. if ($state & FreshRSS_Configuration::STATE_FAVORITE) {
  417. if (!($state & FreshRSS_Configuration::STATE_NOT_FAVORITE)) {
  418. $where .= 'AND e1.is_favorite = 1 ';
  419. }
  420. }
  421. switch ($order) {
  422. case 'DESC':
  423. case 'ASC':
  424. break;
  425. default:
  426. throw new FreshRSS_EntriesGetter_Exception ('Bad order in Entry->listByType: [' . $order . ']!');
  427. }
  428. if ($firstId !== '') {
  429. $where .= 'AND e1.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' ';
  430. }
  431. if (($date_min > 0) && ($type !== 's')) {
  432. $where .= 'AND (e1.id >= ' . $date_min . '000000';
  433. if ($showOlderUnreadsorFavorites) { //Lax date constraint
  434. $where .= ' OR e1.is_read = 0 OR e1.is_favorite = 1 OR (f.keep_history <> 0';
  435. if (intval($keepHistoryDefault) === 0) {
  436. $where .= ' AND f.keep_history <> -2'; //default
  437. }
  438. $where .= ')';
  439. }
  440. $where .= ') ';
  441. $joinFeed = true;
  442. }
  443. $search = '';
  444. if ($filter !== '') {
  445. $filter = trim($filter);
  446. $filter = addcslashes($filter, '\\%_');
  447. if (stripos($filter, 'intitle:') === 0) {
  448. $filter = substr($filter, strlen('intitle:'));
  449. $intitle = true;
  450. } else {
  451. $intitle = false;
  452. }
  453. if (stripos($filter, 'inurl:') === 0) {
  454. $filter = substr($filter, strlen('inurl:'));
  455. $inurl = true;
  456. } else {
  457. $inurl = false;
  458. }
  459. if (stripos($filter, 'author:') === 0) {
  460. $filter = substr($filter, strlen('author:'));
  461. $author = true;
  462. } else {
  463. $author = false;
  464. }
  465. $terms = array_unique(explode(' ', $filter));
  466. sort($terms); //Put #tags first
  467. foreach ($terms as $word) {
  468. $word = trim($word);
  469. if (strlen($word) > 0) {
  470. if ($intitle) {
  471. $search .= 'AND e1.title LIKE ? ';
  472. $values[] = '%' . $word .'%';
  473. } elseif ($inurl) {
  474. $search .= 'AND CONCAT(e1.link, e1.guid) LIKE ? ';
  475. $values[] = '%' . $word .'%';
  476. } elseif ($author) {
  477. $search .= 'AND e1.author LIKE ? ';
  478. $values[] = '%' . $word .'%';
  479. } else {
  480. if ($word[0] === '#' && isset($word[1])) {
  481. $search .= 'AND e1.tags LIKE ? ';
  482. $values[] = '%' . $word .'%';
  483. } else {
  484. $search .= 'AND CONCAT(e1.title, UNCOMPRESS(e1.content_bin)) LIKE ? ';
  485. $values[] = '%' . $word .'%';
  486. }
  487. }
  488. }
  489. }
  490. }
  491. return array($values,
  492. 'SELECT e1.id FROM `' . $this->prefix . 'entry` e1 '
  493. . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e1.id_feed = f.id ' : '')
  494. . 'WHERE ' . $where
  495. . $search
  496. . 'ORDER BY e1.id ' . $order
  497. . ($limit > 0 ? ' LIMIT ' . $limit : '')); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/
  498. }
  499. public function listWhere($type = 'a', $id = '', $state = null, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) {
  500. list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $showOlderUnreadsorFavorites, $keepHistoryDefault);
  501. $sql = 'SELECT e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags '
  502. . 'FROM `' . $this->prefix . 'entry` e '
  503. . 'INNER JOIN ('
  504. . $sql
  505. . ') e2 ON e2.id = e.id '
  506. . 'ORDER BY e.id ' . $order;
  507. $stm = $this->bd->prepare ($sql);
  508. $stm->execute ($values);
  509. return self::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC));
  510. }
  511. public function listIdsWhere($type = 'a', $id = '', $state = null, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $showOlderUnreadsorFavorites = false, $keepHistoryDefault = 0) { //For API
  512. list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min, $showOlderUnreadsorFavorites, $keepHistoryDefault);
  513. $stm = $this->bd->prepare($sql);
  514. $stm->execute($values);
  515. return $stm->fetchAll(PDO::FETCH_COLUMN, 0);
  516. }
  517. public function listLastGuidsByFeed($id, $n) {
  518. $sql = 'SELECT guid FROM `' . $this->prefix . 'entry` WHERE id_feed=? ORDER BY id DESC LIMIT ' . intval($n);
  519. $stm = $this->bd->prepare ($sql);
  520. $values = array ($id);
  521. $stm->execute ($values);
  522. return $stm->fetchAll (PDO::FETCH_COLUMN, 0);
  523. }
  524. public function countUnreadRead () {
  525. $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE priority > 0'
  526. . ' UNION SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE priority > 0 AND is_read = 0';
  527. $stm = $this->bd->prepare ($sql);
  528. $stm->execute ();
  529. $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
  530. $all = empty($res[0]) ? 0 : $res[0];
  531. $unread = empty($res[1]) ? 0 : $res[1];
  532. return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread);
  533. }
  534. public function count ($minPriority = null) {
  535. $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id';
  536. if ($minPriority !== null) {
  537. $sql = ' WHERE priority > ' . intval($minPriority);
  538. }
  539. $stm = $this->bd->prepare ($sql);
  540. $stm->execute ();
  541. $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
  542. return $res[0];
  543. }
  544. public function countNotRead ($minPriority = null) {
  545. $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE is_read = 0';
  546. if ($minPriority !== null) {
  547. $sql = ' AND priority > ' . intval($minPriority);
  548. }
  549. $stm = $this->bd->prepare ($sql);
  550. $stm->execute ();
  551. $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
  552. return $res[0];
  553. }
  554. public function countUnreadReadFavorites () {
  555. $sql = 'SELECT COUNT(id) FROM `' . $this->prefix . 'entry` WHERE is_favorite=1'
  556. . ' UNION SELECT COUNT(id) FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 AND is_read = 0';
  557. $stm = $this->bd->prepare ($sql);
  558. $stm->execute ();
  559. $res = $stm->fetchAll (PDO::FETCH_COLUMN, 0);
  560. $all = empty($res[0]) ? 0 : $res[0];
  561. $unread = empty($res[1]) ? 0 : $res[1];
  562. return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread);
  563. }
  564. public function optimizeTable() {
  565. $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`';
  566. $stm = $this->bd->prepare ($sql);
  567. $stm->execute ();
  568. }
  569. public static function daoToEntry ($listDAO) {
  570. $list = array ();
  571. if (!is_array ($listDAO)) {
  572. $listDAO = array ($listDAO);
  573. }
  574. foreach ($listDAO as $key => $dao) {
  575. $entry = new FreshRSS_Entry (
  576. $dao['id_feed'],
  577. $dao['guid'],
  578. $dao['title'],
  579. $dao['author'],
  580. $dao['content'],
  581. $dao['link'],
  582. $dao['date'],
  583. $dao['is_read'],
  584. $dao['is_favorite'],
  585. $dao['tags']
  586. );
  587. if (isset ($dao['id'])) {
  588. $entry->_id ($dao['id']);
  589. }
  590. $list[] = $entry;
  591. }
  592. unset ($listDAO);
  593. return $list;
  594. }
  595. }