FeedDAO.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. <?php
  2. class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
  3. protected function addColumn(string $name) {
  4. Minz_Log::warning(__method__ . ': ' . $name);
  5. try {
  6. if ($name === 'kind') { //v1.20.0
  7. return $this->pdo->exec('ALTER TABLE `_feed` ADD COLUMN kind SMALLINT DEFAULT 0') !== false;
  8. } elseif ($name === 'attributes') { //v1.11.0
  9. return $this->pdo->exec('ALTER TABLE `_feed` ADD COLUMN attributes TEXT') !== false;
  10. }
  11. } catch (Exception $e) {
  12. Minz_Log::error(__method__ . ' error: ' . $e->getMessage());
  13. }
  14. return false;
  15. }
  16. protected function autoUpdateDb(array $errorInfo) {
  17. if (isset($errorInfo[0])) {
  18. if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_FIELD_ERROR || $errorInfo[0] === FreshRSS_DatabaseDAOPGSQL::UNDEFINED_COLUMN) {
  19. foreach (['attributes', 'kind'] as $column) {
  20. if (stripos($errorInfo[2], $column) !== false) {
  21. return $this->addColumn($column);
  22. }
  23. }
  24. }
  25. }
  26. return false;
  27. }
  28. public function addFeed(array $valuesTmp) {
  29. $sql = '
  30. INSERT INTO `_feed`
  31. (
  32. url,
  33. kind,
  34. category,
  35. name,
  36. website,
  37. description,
  38. `lastUpdate`,
  39. priority,
  40. `pathEntries`,
  41. `httpAuth`,
  42. error,
  43. ttl,
  44. attributes
  45. )
  46. VALUES
  47. (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
  48. $stm = $this->pdo->prepare($sql);
  49. $valuesTmp['url'] = safe_ascii($valuesTmp['url']);
  50. $valuesTmp['website'] = safe_ascii($valuesTmp['website']);
  51. if (!isset($valuesTmp['pathEntries'])) {
  52. $valuesTmp['pathEntries'] = '';
  53. }
  54. if (!isset($valuesTmp['attributes'])) {
  55. $valuesTmp['attributes'] = [];
  56. }
  57. $values = array(
  58. substr($valuesTmp['url'], 0, 511),
  59. $valuesTmp['kind'] ?? FreshRSS_Feed::KIND_RSS,
  60. $valuesTmp['category'],
  61. mb_strcut(trim($valuesTmp['name']), 0, FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE, 'UTF-8'),
  62. substr($valuesTmp['website'], 0, 255),
  63. sanitizeHTML($valuesTmp['description'], '', 1023),
  64. $valuesTmp['lastUpdate'],
  65. isset($valuesTmp['priority']) ? intval($valuesTmp['priority']) : FreshRSS_Feed::PRIORITY_MAIN_STREAM,
  66. mb_strcut($valuesTmp['pathEntries'], 0, 511, 'UTF-8'),
  67. base64_encode($valuesTmp['httpAuth']),
  68. isset($valuesTmp['error']) ? intval($valuesTmp['error']) : 0,
  69. isset($valuesTmp['ttl']) ? intval($valuesTmp['ttl']) : FreshRSS_Feed::TTL_DEFAULT,
  70. is_string($valuesTmp['attributes']) ? $valuesTmp['attributes'] : json_encode($valuesTmp['attributes'], JSON_UNESCAPED_SLASHES),
  71. );
  72. if ($stm && $stm->execute($values)) {
  73. return $this->pdo->lastInsertId('`_feed_id_seq`');
  74. } else {
  75. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  76. if ($this->autoUpdateDb($info)) {
  77. return $this->addFeed($valuesTmp);
  78. }
  79. Minz_Log::error('SQL error addFeed: ' . $info[2]);
  80. return false;
  81. }
  82. }
  83. public function addFeedObject(FreshRSS_Feed $feed): int {
  84. // TODO: not sure if we should write this method in DAO since DAO
  85. // should not be aware about feed class
  86. // Add feed only if we don’t find it in DB
  87. $feed_search = $this->searchByUrl($feed->url());
  88. if (!$feed_search) {
  89. $values = array(
  90. 'id' => $feed->id(),
  91. 'url' => $feed->url(),
  92. 'kind' => $feed->kind(),
  93. 'category' => $feed->category(),
  94. 'name' => $feed->name(),
  95. 'website' => $feed->website(),
  96. 'description' => $feed->description(),
  97. 'lastUpdate' => 0,
  98. 'pathEntries' => $feed->pathEntries(),
  99. 'httpAuth' => $feed->httpAuth(),
  100. 'attributes' => $feed->attributes(),
  101. );
  102. if ($feed->mute() || (
  103. FreshRSS_Context::$user_conf != null && //When creating a new user
  104. $feed->ttl() != FreshRSS_Context::$user_conf->ttl_default)) {
  105. $values['ttl'] = $feed->ttl() * ($feed->mute() ? -1 : 1);
  106. }
  107. $id = $this->addFeed($values);
  108. if ($id) {
  109. $feed->_id($id);
  110. $feed->faviconPrepare();
  111. }
  112. return $id;
  113. }
  114. return $feed_search->id();
  115. }
  116. public function updateFeed(int $id, array $valuesTmp) {
  117. if (isset($valuesTmp['name'])) {
  118. $valuesTmp['name'] = mb_strcut(trim($valuesTmp['name']), 0, FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE, 'UTF-8');
  119. }
  120. if (isset($valuesTmp['url'])) {
  121. $valuesTmp['url'] = safe_ascii($valuesTmp['url']);
  122. }
  123. if (isset($valuesTmp['website'])) {
  124. $valuesTmp['website'] = safe_ascii($valuesTmp['website']);
  125. }
  126. $set = '';
  127. foreach ($valuesTmp as $key => $v) {
  128. $set .= '`' . $key . '`=?, ';
  129. if ($key === 'httpAuth') {
  130. $valuesTmp[$key] = base64_encode($v);
  131. } elseif ($key === 'attributes') {
  132. $valuesTmp[$key] = is_string($valuesTmp[$key]) ? $valuesTmp[$key] : json_encode($valuesTmp[$key], JSON_UNESCAPED_SLASHES);
  133. }
  134. }
  135. $set = substr($set, 0, -2);
  136. $sql = 'UPDATE `_feed` SET ' . $set . ' WHERE id=?';
  137. $stm = $this->pdo->prepare($sql);
  138. foreach ($valuesTmp as $v) {
  139. $values[] = $v;
  140. }
  141. $values[] = $id;
  142. if ($stm && $stm->execute($values)) {
  143. return $stm->rowCount();
  144. } else {
  145. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  146. if ($this->autoUpdateDb($info)) {
  147. return $this->updateFeed($id, $valuesTmp);
  148. }
  149. Minz_Log::error('SQL error updateFeed: ' . $info[2] . ' for feed ' . $id);
  150. return false;
  151. }
  152. }
  153. public function updateFeedAttribute(FreshRSS_Feed $feed, string $key, $value) {
  154. $feed->_attributes($key, $value);
  155. return $this->updateFeed(
  156. $feed->id(),
  157. array('attributes' => $feed->attributes())
  158. );
  159. }
  160. /**
  161. * @see updateCachedValue()
  162. */
  163. public function updateLastUpdate(int $id, bool $inError = false, int $mtime = 0) {
  164. $sql = 'UPDATE `_feed` SET `lastUpdate`=?, error=? WHERE id=?';
  165. $values = array(
  166. $mtime <= 0 ? time() : $mtime,
  167. $inError ? 1 : 0,
  168. $id,
  169. );
  170. $stm = $this->pdo->prepare($sql);
  171. if ($stm && $stm->execute($values)) {
  172. return $stm->rowCount();
  173. } else {
  174. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  175. Minz_Log::error('SQL error updateLastUpdate: ' . $info[2]);
  176. return false;
  177. }
  178. }
  179. public function mute(int $id, bool $value = true) {
  180. $sql = 'UPDATE `_feed` SET ttl=' . ($value ? '-' : '') . 'ABS(ttl) WHERE id=' . intval($id);
  181. return $this->pdo->exec($sql);
  182. }
  183. public function changeCategory(int $idOldCat, int $idNewCat) {
  184. $catDAO = FreshRSS_Factory::createCategoryDao();
  185. $newCat = $catDAO->searchById($idNewCat);
  186. if (!$newCat) {
  187. $newCat = $catDAO->getDefault();
  188. }
  189. $sql = 'UPDATE `_feed` SET category=? WHERE category=?';
  190. $stm = $this->pdo->prepare($sql);
  191. $values = array(
  192. $newCat->id(),
  193. $idOldCat
  194. );
  195. if ($stm && $stm->execute($values)) {
  196. return $stm->rowCount();
  197. } else {
  198. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  199. Minz_Log::error('SQL error changeCategory: ' . $info[2]);
  200. return false;
  201. }
  202. }
  203. public function deleteFeed(int $id) {
  204. $sql = 'DELETE FROM `_feed` WHERE id=?';
  205. $stm = $this->pdo->prepare($sql);
  206. $values = array($id);
  207. if ($stm && $stm->execute($values)) {
  208. return $stm->rowCount();
  209. } else {
  210. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  211. Minz_Log::error('SQL error deleteFeed: ' . $info[2]);
  212. return false;
  213. }
  214. }
  215. public function deleteFeedByCategory(int $id) {
  216. $sql = 'DELETE FROM `_feed` WHERE category=?';
  217. $stm = $this->pdo->prepare($sql);
  218. $values = array($id);
  219. if ($stm && $stm->execute($values)) {
  220. return $stm->rowCount();
  221. } else {
  222. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  223. Minz_Log::error('SQL error deleteFeedByCategory: ' . $info[2]);
  224. return false;
  225. }
  226. }
  227. public function selectAll() {
  228. $sql = <<<'SQL'
  229. SELECT id, url, kind, category, name, website, description, `lastUpdate`,
  230. priority, `pathEntries`, `httpAuth`, error, ttl, attributes
  231. FROM `_feed`
  232. SQL;
  233. $stm = $this->pdo->query($sql);
  234. while ($row = $stm->fetch(PDO::FETCH_ASSOC)) {
  235. yield $row;
  236. }
  237. }
  238. /**
  239. * @return FreshRSS_Feed|null
  240. */
  241. public function searchById($id) {
  242. $sql = 'SELECT * FROM `_feed` WHERE id=:id';
  243. $stm = $this->pdo->prepare($sql);
  244. $stm->bindParam(':id', $id, PDO::PARAM_INT);
  245. $stm->execute();
  246. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  247. $feed = self::daoToFeed($res);
  248. return $feed[$id] ?? null;
  249. }
  250. /**
  251. * @return FreshRSS_Feed|null
  252. */
  253. public function searchByUrl(string $url) {
  254. $sql = 'SELECT * FROM `_feed` WHERE url=?';
  255. $stm = $this->pdo->prepare($sql);
  256. $values = array($url);
  257. $stm->execute($values);
  258. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  259. $feed = current(self::daoToFeed($res));
  260. return $feed == false ? null : $feed;
  261. }
  262. public function listFeedsIds(): array {
  263. $sql = 'SELECT id FROM `_feed`';
  264. $stm = $this->pdo->query($sql);
  265. return $stm->fetchAll(PDO::FETCH_COLUMN, 0);
  266. }
  267. /**
  268. * @return array<FreshRSS_Feed>
  269. */
  270. public function listFeeds(): array {
  271. $sql = 'SELECT * FROM `_feed` ORDER BY name';
  272. $stm = $this->pdo->query($sql);
  273. return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
  274. }
  275. public function listFeedsNewestItemUsec($id_feed = null) {
  276. $sql = 'SELECT id_feed, MAX(id) as newest_item_us FROM `_entry` ';
  277. if ($id_feed === null) {
  278. $sql .= 'GROUP BY id_feed';
  279. } else {
  280. $sql .= 'WHERE id_feed=' . intval($id_feed);
  281. }
  282. $stm = $this->pdo->query($sql);
  283. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  284. $newestItemUsec = [];
  285. foreach ($res as $line) {
  286. $newestItemUsec['f_' . $line['id_feed']] = $line['newest_item_us'];
  287. }
  288. return $newestItemUsec;
  289. }
  290. /**
  291. * For API
  292. */
  293. public function arrayFeedCategoryNames(): array {
  294. $sql = <<<'SQL'
  295. SELECT f.id, f.name, c.name as c_name FROM `_feed` f
  296. INNER JOIN `_category` c ON c.id = f.category
  297. SQL;
  298. $stm = $this->pdo->query($sql);
  299. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  300. $feedCategoryNames = array();
  301. foreach ($res as $line) {
  302. $feedCategoryNames[$line['id']] = array(
  303. 'name' => $line['name'],
  304. 'c_name' => $line['c_name'],
  305. );
  306. }
  307. return $feedCategoryNames;
  308. }
  309. /**
  310. * Use $defaultCacheDuration == -1 to return all feeds, without filtering them by TTL.
  311. */
  312. public function listFeedsOrderUpdate(int $defaultCacheDuration = 3600, int $limit = 0) {
  313. $this->updateTTL();
  314. $sql = 'SELECT id, url, kind, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, ttl, attributes '
  315. . 'FROM `_feed` '
  316. . ($defaultCacheDuration < 0 ? '' : 'WHERE ttl >= ' . FreshRSS_Feed::TTL_DEFAULT
  317. . ' AND `lastUpdate` < (' . (time() + 60)
  318. . '-(CASE WHEN ttl=' . FreshRSS_Feed::TTL_DEFAULT . ' THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) ')
  319. . 'ORDER BY `lastUpdate` '
  320. . ($limit < 1 ? '' : 'LIMIT ' . intval($limit));
  321. $stm = $this->pdo->query($sql);
  322. if ($stm !== false) {
  323. return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
  324. } else {
  325. $info = $this->pdo->errorInfo();
  326. if ($this->autoUpdateDb($info)) {
  327. return $this->listFeedsOrderUpdate($defaultCacheDuration);
  328. }
  329. Minz_Log::error('SQL error listFeedsOrderUpdate: ' . $info[2]);
  330. return array();
  331. }
  332. }
  333. public function listTitles(int $id, int $limit = 0) {
  334. $sql = 'SELECT title FROM `_entry` WHERE id_feed=:id_feed ORDER BY id DESC'
  335. . ($limit < 1 ? '' : ' LIMIT ' . intval($limit));
  336. $stm = $this->pdo->prepare($sql);
  337. $stm->bindParam(':id_feed', $id, PDO::PARAM_INT);
  338. if ($stm && $stm->execute()) {
  339. return $stm->fetchAll(PDO::FETCH_COLUMN, 0);
  340. }
  341. return false;
  342. }
  343. /**
  344. * @return array<FreshRSS_Feed>
  345. */
  346. public function listByCategory(int $cat): array {
  347. $sql = 'SELECT * FROM `_feed` WHERE category=?';
  348. $stm = $this->pdo->prepare($sql);
  349. $stm->execute(array($cat));
  350. $feeds = self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
  351. usort($feeds, function ($a, $b) {
  352. return strnatcasecmp($a->name(), $b->name());
  353. });
  354. return $feeds;
  355. }
  356. public function countEntries(int $id) {
  357. $sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=?';
  358. $stm = $this->pdo->prepare($sql);
  359. $values = array($id);
  360. $stm->execute($values);
  361. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  362. return $res[0]['count'];
  363. }
  364. public function countNotRead(int $id) {
  365. $sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=? AND is_read=0';
  366. $stm = $this->pdo->prepare($sql);
  367. $values = array($id);
  368. $stm->execute($values);
  369. $res = $stm->fetchAll(PDO::FETCH_ASSOC);
  370. return $res[0]['count'];
  371. }
  372. /**
  373. * @return int|false
  374. */
  375. public function updateCachedValues(int $id = 0) {
  376. //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE
  377. $sql = 'UPDATE `_feed` '
  378. . 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `_entry` e1 WHERE e1.id_feed=`_feed`.id),'
  379. . '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `_entry` e2 WHERE e2.id_feed=`_feed`.id AND e2.is_read=0)'
  380. . ($id != 0 ? ' WHERE id=:id' : '');
  381. $stm = $this->pdo->prepare($sql);
  382. if ($id != 0) {
  383. $stm->bindParam(':id', $id, PDO::PARAM_INT);
  384. }
  385. if ($stm && $stm->execute()) {
  386. return $stm->rowCount();
  387. } else {
  388. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  389. Minz_Log::error('SQL error updateCachedValue: ' . $info[2]);
  390. return false;
  391. }
  392. }
  393. /**
  394. * @return int|false
  395. */
  396. public function keepMaxUnread(int $id, int $n) {
  397. //Double SELECT for MySQL workaround ERROR 1093 (HY000)
  398. $sql = <<<'SQL'
  399. UPDATE `_entry` SET is_read=1
  400. WHERE id_feed=:id_feed1 AND is_read=0 AND id <= (SELECT e3.id FROM (
  401. SELECT e2.id FROM `_entry` e2
  402. WHERE e2.id_feed=:id_feed2 AND e2.is_read=0
  403. ORDER BY e2.id DESC
  404. LIMIT 1
  405. OFFSET :limit) e3)
  406. SQL;
  407. $stm = $this->pdo->prepare($sql);
  408. $stm->bindParam(':id_feed1', $id, PDO::PARAM_INT);
  409. $stm->bindParam(':id_feed2', $id, PDO::PARAM_INT);
  410. $stm->bindParam(':limit', $n, PDO::PARAM_INT);
  411. if (!$stm || !$stm->execute()) {
  412. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  413. Minz_Log::error('SQL error keepMaxUnread: ' . json_encode($info));
  414. return false;
  415. }
  416. $affected = $stm->rowCount();
  417. if ($affected > 0) {
  418. $sql = 'UPDATE `_feed` '
  419. . 'SET `cache_nbUnreads`=`cache_nbUnreads`-' . $affected
  420. . ' WHERE id=:id';
  421. $stm = $this->pdo->prepare($sql);
  422. $stm->bindParam(':id', $id, PDO::PARAM_INT);
  423. if (!($stm && $stm->execute())) {
  424. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  425. Minz_Log::error('SQL error keepMaxUnread cache: ' . json_encode($info));
  426. return false;
  427. }
  428. }
  429. return $affected;
  430. }
  431. /**
  432. * @return int|false
  433. */
  434. public function truncate(int $id) {
  435. $sql = 'DELETE FROM `_entry` WHERE id_feed=:id';
  436. $stm = $this->pdo->prepare($sql);
  437. $stm->bindParam(':id', $id, PDO::PARAM_INT);
  438. $this->pdo->beginTransaction();
  439. if (!($stm && $stm->execute())) {
  440. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  441. Minz_Log::error('SQL error truncate: ' . $info[2]);
  442. $this->pdo->rollBack();
  443. return false;
  444. }
  445. $affected = $stm->rowCount();
  446. $sql = 'UPDATE `_feed` '
  447. . 'SET `cache_nbEntries`=0, `cache_nbUnreads`=0, `lastUpdate`=0 WHERE id=:id';
  448. $stm = $this->pdo->prepare($sql);
  449. $stm->bindParam(':id', $id, PDO::PARAM_INT);
  450. if (!($stm && $stm->execute())) {
  451. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  452. Minz_Log::error('SQL error truncate: ' . $info[2]);
  453. $this->pdo->rollBack();
  454. return false;
  455. }
  456. $this->pdo->commit();
  457. return $affected;
  458. }
  459. public function purge() {
  460. $sql = 'DELETE FROM `_entry`';
  461. $stm = $this->pdo->prepare($sql);
  462. $this->pdo->beginTransaction();
  463. if (!($stm && $stm->execute())) {
  464. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  465. Minz_Log::error('SQL error truncate: ' . $info[2]);
  466. $this->pdo->rollBack();
  467. return false;
  468. }
  469. $sql = 'UPDATE `_feed` SET `cache_nbEntries` = 0, `cache_nbUnreads` = 0';
  470. $stm = $this->pdo->prepare($sql);
  471. if (!($stm && $stm->execute())) {
  472. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  473. Minz_Log::error('SQL error truncate: ' . $info[2]);
  474. $this->pdo->rollBack();
  475. return false;
  476. }
  477. $this->pdo->commit();
  478. }
  479. /**
  480. * @return array<FreshRSS_Feed>
  481. */
  482. public static function daoToFeed($listDAO, $catID = null): array {
  483. $list = array();
  484. if (!is_array($listDAO)) {
  485. $listDAO = array($listDAO);
  486. }
  487. foreach ($listDAO as $key => $dao) {
  488. if (!isset($dao['name'])) {
  489. continue;
  490. }
  491. if (isset($dao['id'])) {
  492. $key = $dao['id'];
  493. }
  494. if ($catID === null) {
  495. $category = isset($dao['category']) ? $dao['category'] : 0;
  496. } else {
  497. $category = $catID;
  498. }
  499. $myFeed = new FreshRSS_Feed($dao['url'] ?? '', false);
  500. $myFeed->_kind($dao['kind'] ?? FreshRSS_Feed::KIND_RSS);
  501. $myFeed->_category($category);
  502. $myFeed->_name($dao['name']);
  503. $myFeed->_website($dao['website'] ?? '', false);
  504. $myFeed->_description($dao['description'] ?? '');
  505. $myFeed->_lastUpdate($dao['lastUpdate'] ?? 0);
  506. $myFeed->_priority($dao['priority'] ?? 10);
  507. $myFeed->_pathEntries($dao['pathEntries'] ?? '');
  508. $myFeed->_httpAuth(base64_decode($dao['httpAuth'] ?? ''));
  509. $myFeed->_error($dao['error'] ?? 0);
  510. $myFeed->_ttl($dao['ttl'] ?? FreshRSS_Feed::TTL_DEFAULT);
  511. $myFeed->_attributes('', $dao['attributes'] ?? '');
  512. $myFeed->_nbNotRead($dao['cache_nbUnreads'] ?? 0);
  513. $myFeed->_nbEntries($dao['cache_nbEntries'] ?? 0);
  514. if (isset($dao['id'])) {
  515. $myFeed->_id($dao['id']);
  516. }
  517. $list[$key] = $myFeed;
  518. }
  519. return $list;
  520. }
  521. public function updateTTL() {
  522. $sql = 'UPDATE `_feed` SET ttl=:new_value WHERE ttl=:old_value';
  523. $stm = $this->pdo->prepare($sql);
  524. if (!($stm && $stm->execute(array(':new_value' => FreshRSS_Feed::TTL_DEFAULT, ':old_value' => -2)))) {
  525. $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
  526. Minz_Log::error('SQL warning updateTTL 1: ' . $info[2] . ' ' . $sql);
  527. $sql2 = 'ALTER TABLE `_feed` ADD COLUMN ttl INT NOT NULL DEFAULT ' . FreshRSS_Feed::TTL_DEFAULT; //v0.7.3
  528. $stm = $this->pdo->query($sql2);
  529. if ($stm === false) {
  530. $info = $this->pdo->errorInfo();
  531. Minz_Log::error('SQL error updateTTL 2: ' . $info[2] . ' ' . $sql2);
  532. }
  533. } else {
  534. $stm->execute(array(':new_value' => -3600, ':old_value' => -1));
  535. }
  536. }
  537. /**
  538. * @return int|false
  539. */
  540. public function count() {
  541. $sql = 'SELECT COUNT(e.id) AS count FROM `_feed` e';
  542. $stm = $this->pdo->query($sql);
  543. if ($stm == false) {
  544. return false;
  545. }
  546. $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
  547. return isset($res[0]) ? $res[0] : 0;
  548. }
  549. }