mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-01-28 23:21:04 -05:00
* Refactor entry to GReader API format Some code was copied in two locations and not completely uniform. Cleaning of related variables and functions (e.g. better types for entries and categories as objects vs. as IDs). Usecase: I need to call the same GReader-compatible serialization from an extension * Fixed some edge cases * Keep summary instead of content `summary` and `content` seems to be used interchangeably in the Google Reader API. We have been using `summary` for our client API and `content` in our export/import, so stick to that.
632 lines
19 KiB
PHP
632 lines
19 KiB
PHP
<?php
|
||
|
||
class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||
|
||
protected function addColumn(string $name) {
|
||
Minz_Log::warning(__method__ . ': ' . $name);
|
||
try {
|
||
if ($name === 'kind') { //v1.20.0
|
||
return $this->pdo->exec('ALTER TABLE `_feed` ADD COLUMN kind SMALLINT DEFAULT 0') !== false;
|
||
} elseif ($name === 'attributes') { //v1.11.0
|
||
return $this->pdo->exec('ALTER TABLE `_feed` ADD COLUMN attributes TEXT') !== false;
|
||
}
|
||
} catch (Exception $e) {
|
||
Minz_Log::error(__method__ . ' error: ' . $e->getMessage());
|
||
}
|
||
return false;
|
||
}
|
||
|
||
protected function autoUpdateDb(array $errorInfo) {
|
||
if (isset($errorInfo[0])) {
|
||
if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_FIELD_ERROR || $errorInfo[0] === FreshRSS_DatabaseDAOPGSQL::UNDEFINED_COLUMN) {
|
||
$errorLines = explode("\n", $errorInfo[2], 2); // The relevant column name is on the first line, other lines are noise
|
||
foreach (['attributes', 'kind'] as $column) {
|
||
if (stripos($errorLines[0], $column) !== false) {
|
||
return $this->addColumn($column);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/** @return int|false */
|
||
public function addFeed(array $valuesTmp) {
|
||
$sql = 'INSERT INTO `_feed` (url, kind, category, name, website, description, `lastUpdate`, priority, `pathEntries`, `httpAuth`, error, ttl, attributes)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||
$stm = $this->pdo->prepare($sql);
|
||
|
||
$valuesTmp['url'] = safe_ascii($valuesTmp['url']);
|
||
$valuesTmp['website'] = safe_ascii($valuesTmp['website']);
|
||
if (!isset($valuesTmp['pathEntries'])) {
|
||
$valuesTmp['pathEntries'] = '';
|
||
}
|
||
if (!isset($valuesTmp['attributes'])) {
|
||
$valuesTmp['attributes'] = [];
|
||
}
|
||
|
||
$values = array(
|
||
substr($valuesTmp['url'], 0, 511),
|
||
$valuesTmp['kind'] ?? FreshRSS_Feed::KIND_RSS,
|
||
$valuesTmp['category'],
|
||
mb_strcut(trim($valuesTmp['name']), 0, FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE, 'UTF-8'),
|
||
substr($valuesTmp['website'], 0, 255),
|
||
sanitizeHTML($valuesTmp['description'], '', 1023),
|
||
$valuesTmp['lastUpdate'],
|
||
isset($valuesTmp['priority']) ? intval($valuesTmp['priority']) : FreshRSS_Feed::PRIORITY_MAIN_STREAM,
|
||
mb_strcut($valuesTmp['pathEntries'], 0, 511, 'UTF-8'),
|
||
base64_encode($valuesTmp['httpAuth']),
|
||
isset($valuesTmp['error']) ? intval($valuesTmp['error']) : 0,
|
||
isset($valuesTmp['ttl']) ? intval($valuesTmp['ttl']) : FreshRSS_Feed::TTL_DEFAULT,
|
||
is_string($valuesTmp['attributes']) ? $valuesTmp['attributes'] : json_encode($valuesTmp['attributes'], JSON_UNESCAPED_SLASHES),
|
||
);
|
||
|
||
if ($stm && $stm->execute($values)) {
|
||
return $this->pdo->lastInsertId('`_feed_id_seq`');
|
||
} else {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
if ($this->autoUpdateDb($info)) {
|
||
return $this->addFeed($valuesTmp);
|
||
}
|
||
Minz_Log::error('SQL error addFeed: ' . $info[2]);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/** @return int|false */
|
||
public function addFeedObject(FreshRSS_Feed $feed) {
|
||
// Add feed only if we don’t find it in DB
|
||
$feed_search = $this->searchByUrl($feed->url());
|
||
if (!$feed_search) {
|
||
$values = array(
|
||
'id' => $feed->id(),
|
||
'url' => $feed->url(),
|
||
'kind' => $feed->kind(),
|
||
'category' => $feed->categoryId(),
|
||
'name' => $feed->name(),
|
||
'website' => $feed->website(),
|
||
'description' => $feed->description(),
|
||
'lastUpdate' => 0,
|
||
'pathEntries' => $feed->pathEntries(),
|
||
'httpAuth' => $feed->httpAuth(),
|
||
'ttl' => $feed->ttl(true),
|
||
'attributes' => $feed->attributes(),
|
||
);
|
||
|
||
$id = $this->addFeed($values);
|
||
if ($id) {
|
||
$feed->_id($id);
|
||
$feed->faviconPrepare();
|
||
}
|
||
|
||
return $id;
|
||
} else {
|
||
// The feed already exists so make sure it is not muted
|
||
$feed->_ttl($feed_search->ttl());
|
||
$feed->_mute(false);
|
||
|
||
// Merge existing and import attributes
|
||
$existingAttributes = $feed_search->attributes();
|
||
$importAttributes = $feed->attributes();
|
||
$feed->_attributes('', array_merge_recursive($existingAttributes, $importAttributes));
|
||
|
||
// Update some values of the existing feed using the import
|
||
$values = [
|
||
'kind' => $feed->kind(),
|
||
'name' => $feed->name(),
|
||
'website' => $feed->website(),
|
||
'description' => $feed->description(),
|
||
'pathEntries' => $feed->pathEntries(),
|
||
'ttl' => $feed->ttl(true),
|
||
'attributes' => $feed->attributes(),
|
||
];
|
||
|
||
if (!$this->updateFeed($feed_search->id(), $values)) {
|
||
return false;
|
||
}
|
||
|
||
return $feed_search->id();
|
||
}
|
||
}
|
||
|
||
/** @return int|false */
|
||
public function updateFeed(int $id, array $valuesTmp) {
|
||
if (isset($valuesTmp['name'])) {
|
||
$valuesTmp['name'] = mb_strcut(trim($valuesTmp['name']), 0, FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE, 'UTF-8');
|
||
}
|
||
if (isset($valuesTmp['url'])) {
|
||
$valuesTmp['url'] = safe_ascii($valuesTmp['url']);
|
||
}
|
||
if (isset($valuesTmp['website'])) {
|
||
$valuesTmp['website'] = safe_ascii($valuesTmp['website']);
|
||
}
|
||
|
||
$set = '';
|
||
foreach ($valuesTmp as $key => $v) {
|
||
$set .= '`' . $key . '`=?, ';
|
||
|
||
if ($key === 'httpAuth') {
|
||
$valuesTmp[$key] = base64_encode($v);
|
||
} elseif ($key === 'attributes') {
|
||
$valuesTmp[$key] = is_string($valuesTmp[$key]) ? $valuesTmp[$key] : json_encode($valuesTmp[$key], JSON_UNESCAPED_SLASHES);
|
||
}
|
||
}
|
||
$set = substr($set, 0, -2);
|
||
|
||
$sql = 'UPDATE `_feed` SET ' . $set . ' WHERE id=?';
|
||
$stm = $this->pdo->prepare($sql);
|
||
|
||
foreach ($valuesTmp as $v) {
|
||
$values[] = $v;
|
||
}
|
||
$values[] = $id;
|
||
|
||
if ($stm && $stm->execute($values)) {
|
||
return $stm->rowCount();
|
||
} else {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
if ($this->autoUpdateDb($info)) {
|
||
return $this->updateFeed($id, $valuesTmp);
|
||
}
|
||
Minz_Log::error('SQL error updateFeed: ' . $info[2] . ' for feed ' . $id);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public function updateFeedAttribute(FreshRSS_Feed $feed, string $key, $value) {
|
||
$feed->_attributes($key, $value);
|
||
return $this->updateFeed(
|
||
$feed->id(),
|
||
array('attributes' => $feed->attributes())
|
||
);
|
||
}
|
||
|
||
/**
|
||
* @see updateCachedValue()
|
||
*/
|
||
public function updateLastUpdate(int $id, bool $inError = false, int $mtime = 0) {
|
||
$sql = 'UPDATE `_feed` SET `lastUpdate`=?, error=? WHERE id=?';
|
||
$values = array(
|
||
$mtime <= 0 ? time() : $mtime,
|
||
$inError ? 1 : 0,
|
||
$id,
|
||
);
|
||
$stm = $this->pdo->prepare($sql);
|
||
|
||
if ($stm && $stm->execute($values)) {
|
||
return $stm->rowCount();
|
||
} else {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::warning(__METHOD__ . ' error: ' . $sql . ' : ' . json_encode($info));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public function mute(int $id, bool $value = true) {
|
||
$sql = 'UPDATE `_feed` SET ttl=' . ($value ? '-' : '') . 'ABS(ttl) WHERE id=' . intval($id);
|
||
return $this->pdo->exec($sql);
|
||
}
|
||
|
||
public function changeCategory(int $idOldCat, int $idNewCat) {
|
||
$catDAO = FreshRSS_Factory::createCategoryDao();
|
||
$newCat = $catDAO->searchById($idNewCat);
|
||
if (!$newCat) {
|
||
$newCat = $catDAO->getDefault();
|
||
}
|
||
|
||
$sql = 'UPDATE `_feed` SET category=? WHERE category=?';
|
||
$stm = $this->pdo->prepare($sql);
|
||
|
||
$values = array(
|
||
$newCat->id(),
|
||
$idOldCat
|
||
);
|
||
|
||
if ($stm && $stm->execute($values)) {
|
||
return $stm->rowCount();
|
||
} else {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::error('SQL error changeCategory: ' . $info[2]);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/** @return int|false */
|
||
public function deleteFeed(int $id) {
|
||
$sql = 'DELETE FROM `_feed` WHERE id=?';
|
||
$stm = $this->pdo->prepare($sql);
|
||
|
||
$values = array($id);
|
||
|
||
if ($stm && $stm->execute($values)) {
|
||
return $stm->rowCount();
|
||
} else {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::error('SQL error deleteFeed: ' . $info[2]);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @param bool|null $muted to include only muted feeds
|
||
* @return int|false
|
||
*/
|
||
public function deleteFeedByCategory(int $id, $muted = null) {
|
||
$sql = 'DELETE FROM `_feed` WHERE category=?';
|
||
if ($muted) {
|
||
$sql .= ' AND ttl < 0';
|
||
}
|
||
$stm = $this->pdo->prepare($sql);
|
||
|
||
$values = array($id);
|
||
|
||
if ($stm && $stm->execute($values)) {
|
||
return $stm->rowCount();
|
||
} else {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::error('SQL error deleteFeedByCategory: ' . $info[2]);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public function selectAll() {
|
||
$sql = <<<'SQL'
|
||
SELECT id, url, kind, category, name, website, description, `lastUpdate`,
|
||
priority, `pathEntries`, `httpAuth`, error, ttl, attributes
|
||
FROM `_feed`
|
||
SQL;
|
||
$stm = $this->pdo->query($sql);
|
||
while ($row = $stm->fetch(PDO::FETCH_ASSOC)) {
|
||
yield $row;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @return FreshRSS_Feed|null
|
||
*/
|
||
public function searchById($id) {
|
||
$sql = 'SELECT * FROM `_feed` WHERE id=:id';
|
||
$stm = $this->pdo->prepare($sql);
|
||
$stm->bindParam(':id', $id, PDO::PARAM_INT);
|
||
$stm->execute();
|
||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||
$feed = self::daoToFeed($res);
|
||
return $feed[$id] ?? null;
|
||
}
|
||
|
||
/**
|
||
* @return FreshRSS_Feed|null
|
||
*/
|
||
public function searchByUrl(string $url) {
|
||
$sql = 'SELECT * FROM `_feed` WHERE url=?';
|
||
$stm = $this->pdo->prepare($sql);
|
||
|
||
$values = array($url);
|
||
|
||
$stm->execute($values);
|
||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||
$feed = current(self::daoToFeed($res));
|
||
return $feed == false ? null : $feed;
|
||
}
|
||
|
||
public function listFeedsIds(): array {
|
||
$sql = 'SELECT id FROM `_feed`';
|
||
$stm = $this->pdo->query($sql);
|
||
return $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||
}
|
||
|
||
/**
|
||
* @return array<FreshRSS_Feed>
|
||
*/
|
||
public function listFeeds(): array {
|
||
$sql = 'SELECT * FROM `_feed` ORDER BY name';
|
||
$stm = $this->pdo->query($sql);
|
||
return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
|
||
}
|
||
|
||
public function listFeedsNewestItemUsec($id_feed = null) {
|
||
$sql = 'SELECT id_feed, MAX(id) as newest_item_us FROM `_entry` ';
|
||
if ($id_feed === null) {
|
||
$sql .= 'GROUP BY id_feed';
|
||
} else {
|
||
$sql .= 'WHERE id_feed=' . intval($id_feed);
|
||
}
|
||
$stm = $this->pdo->query($sql);
|
||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||
$newestItemUsec = [];
|
||
foreach ($res as $line) {
|
||
$newestItemUsec['f_' . $line['id_feed']] = $line['newest_item_us'];
|
||
}
|
||
return $newestItemUsec;
|
||
}
|
||
|
||
/**
|
||
* Use $defaultCacheDuration == -1 to return all feeds, without filtering them by TTL.
|
||
* @return array<FreshRSS_Feed>
|
||
*/
|
||
public function listFeedsOrderUpdate(int $defaultCacheDuration = 3600, int $limit = 0) {
|
||
$this->updateTTL();
|
||
$sql = 'SELECT id, url, kind, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, ttl, attributes '
|
||
. 'FROM `_feed` '
|
||
. ($defaultCacheDuration < 0 ? '' : 'WHERE ttl >= ' . FreshRSS_Feed::TTL_DEFAULT
|
||
. ' AND `lastUpdate` < (' . (time() + 60)
|
||
. '-(CASE WHEN ttl=' . FreshRSS_Feed::TTL_DEFAULT . ' THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) ')
|
||
. 'ORDER BY `lastUpdate` '
|
||
. ($limit < 1 ? '' : 'LIMIT ' . intval($limit));
|
||
$stm = $this->pdo->query($sql);
|
||
if ($stm !== false) {
|
||
return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
|
||
} else {
|
||
$info = $this->pdo->errorInfo();
|
||
if ($this->autoUpdateDb($info)) {
|
||
return $this->listFeedsOrderUpdate($defaultCacheDuration, $limit);
|
||
}
|
||
Minz_Log::error('SQL error listFeedsOrderUpdate: ' . $info[2]);
|
||
return array();
|
||
}
|
||
}
|
||
|
||
public function listTitles(int $id, int $limit = 0) {
|
||
$sql = 'SELECT title FROM `_entry` WHERE id_feed=:id_feed ORDER BY id DESC'
|
||
. ($limit < 1 ? '' : ' LIMIT ' . intval($limit));
|
||
|
||
$stm = $this->pdo->prepare($sql);
|
||
$stm->bindParam(':id_feed', $id, PDO::PARAM_INT);
|
||
|
||
if ($stm && $stm->execute()) {
|
||
return $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* @param bool|null $muted to include only muted feeds
|
||
* @return array<FreshRSS_Feed>
|
||
*/
|
||
public function listByCategory(int $cat, $muted = null): array {
|
||
$sql = 'SELECT * FROM `_feed` WHERE category=?';
|
||
if ($muted) {
|
||
$sql .= ' AND ttl < 0';
|
||
}
|
||
$stm = $this->pdo->prepare($sql);
|
||
|
||
$stm->execute(array($cat));
|
||
|
||
$feeds = self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
|
||
|
||
usort($feeds, function ($a, $b) {
|
||
return strnatcasecmp($a->name(), $b->name());
|
||
});
|
||
|
||
return $feeds;
|
||
}
|
||
|
||
public function countEntries(int $id) {
|
||
$sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=?';
|
||
$stm = $this->pdo->prepare($sql);
|
||
$values = array($id);
|
||
$stm->execute($values);
|
||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
return $res[0]['count'];
|
||
}
|
||
|
||
public function countNotRead(int $id) {
|
||
$sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=? AND is_read=0';
|
||
$stm = $this->pdo->prepare($sql);
|
||
$values = array($id);
|
||
$stm->execute($values);
|
||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
return $res[0]['count'];
|
||
}
|
||
|
||
/**
|
||
* @return int|false
|
||
*/
|
||
public function updateCachedValues(int $id = 0) {
|
||
//2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE
|
||
$sql = 'UPDATE `_feed` '
|
||
. 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `_entry` e1 WHERE e1.id_feed=`_feed`.id),'
|
||
. '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `_entry` e2 WHERE e2.id_feed=`_feed`.id AND e2.is_read=0)'
|
||
. ($id != 0 ? ' WHERE id=:id' : '');
|
||
$stm = $this->pdo->prepare($sql);
|
||
if ($id != 0) {
|
||
$stm->bindParam(':id', $id, PDO::PARAM_INT);
|
||
}
|
||
|
||
if ($stm && $stm->execute()) {
|
||
return $stm->rowCount();
|
||
} else {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::error('SQL error updateCachedValue: ' . $info[2]);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Remember to call updateCachedValues() after calling this function
|
||
* @return int|false number of lines affected or false in case of error
|
||
*/
|
||
public function keepMaxUnread(int $id, int $n) {
|
||
//Double SELECT for MySQL workaround ERROR 1093 (HY000)
|
||
$sql = <<<'SQL'
|
||
UPDATE `_entry` SET is_read=1
|
||
WHERE id_feed=:id_feed1 AND is_read=0 AND id <= (SELECT e3.id FROM (
|
||
SELECT e2.id FROM `_entry` e2
|
||
WHERE e2.id_feed=:id_feed2 AND e2.is_read=0
|
||
ORDER BY e2.id DESC
|
||
LIMIT 1
|
||
OFFSET :limit) e3)
|
||
SQL;
|
||
|
||
if (($stm = $this->pdo->prepare($sql)) &&
|
||
$stm->bindParam(':id_feed1', $id, PDO::PARAM_INT) &&
|
||
$stm->bindParam(':id_feed2', $id, PDO::PARAM_INT) &&
|
||
$stm->bindParam(':limit', $n, PDO::PARAM_INT) &&
|
||
$stm->execute()) {
|
||
return $stm->rowCount();
|
||
} else {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::error('SQL error keepMaxUnread: ' . json_encode($info));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Remember to call updateCachedValues() after calling this function
|
||
* @return int|false number of lines affected or false in case of error
|
||
*/
|
||
public function markAsReadUponGone(int $id) {
|
||
//Double SELECT for MySQL workaround ERROR 1093 (HY000)
|
||
$sql = <<<'SQL'
|
||
UPDATE `_entry` SET is_read=1
|
||
WHERE id_feed=:id_feed1 AND is_read=0 AND `lastSeen` < (SELECT e3.maxlastseen FROM (
|
||
SELECT MAX(e2.`lastSeen`) AS maxlastseen FROM `_entry` e2 WHERE e2.id_feed = :id_feed2) e3)
|
||
SQL;
|
||
|
||
if (($stm = $this->pdo->prepare($sql)) &&
|
||
$stm->bindParam(':id_feed1', $id, PDO::PARAM_INT) &&
|
||
$stm->bindParam(':id_feed2', $id, PDO::PARAM_INT) &&
|
||
$stm->execute()) {
|
||
return $stm->rowCount();
|
||
} else {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::error('SQL error markAsReadUponGone: ' . json_encode($info));
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @return int|false
|
||
*/
|
||
public function truncate(int $id) {
|
||
$sql = 'DELETE FROM `_entry` WHERE id_feed=:id';
|
||
$stm = $this->pdo->prepare($sql);
|
||
$stm->bindParam(':id', $id, PDO::PARAM_INT);
|
||
$this->pdo->beginTransaction();
|
||
if (!($stm && $stm->execute())) {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::error('SQL error truncate: ' . $info[2]);
|
||
$this->pdo->rollBack();
|
||
return false;
|
||
}
|
||
$affected = $stm->rowCount();
|
||
|
||
$sql = 'UPDATE `_feed` '
|
||
. 'SET `cache_nbEntries`=0, `cache_nbUnreads`=0, `lastUpdate`=0 WHERE id=:id';
|
||
$stm = $this->pdo->prepare($sql);
|
||
$stm->bindParam(':id', $id, PDO::PARAM_INT);
|
||
if (!($stm && $stm->execute())) {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::error('SQL error truncate: ' . $info[2]);
|
||
$this->pdo->rollBack();
|
||
return false;
|
||
}
|
||
|
||
$this->pdo->commit();
|
||
return $affected;
|
||
}
|
||
|
||
public function purge() {
|
||
$sql = 'DELETE FROM `_entry`';
|
||
$stm = $this->pdo->prepare($sql);
|
||
$this->pdo->beginTransaction();
|
||
if (!($stm && $stm->execute())) {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::error('SQL error truncate: ' . $info[2]);
|
||
$this->pdo->rollBack();
|
||
return false;
|
||
}
|
||
|
||
$sql = 'UPDATE `_feed` SET `cache_nbEntries` = 0, `cache_nbUnreads` = 0';
|
||
$stm = $this->pdo->prepare($sql);
|
||
if (!($stm && $stm->execute())) {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::error('SQL error truncate: ' . $info[2]);
|
||
$this->pdo->rollBack();
|
||
return false;
|
||
}
|
||
|
||
$this->pdo->commit();
|
||
}
|
||
|
||
/**
|
||
* @return array<FreshRSS_Feed>
|
||
*/
|
||
public static function daoToFeed($listDAO, $catID = null): array {
|
||
$list = array();
|
||
|
||
if (!is_array($listDAO)) {
|
||
$listDAO = array($listDAO);
|
||
}
|
||
|
||
foreach ($listDAO as $key => $dao) {
|
||
if (!isset($dao['name'])) {
|
||
continue;
|
||
}
|
||
if (isset($dao['id'])) {
|
||
$key = $dao['id'];
|
||
}
|
||
if ($catID === null) {
|
||
$category = isset($dao['category']) ? $dao['category'] : 0;
|
||
} else {
|
||
$category = $catID;
|
||
}
|
||
|
||
$myFeed = new FreshRSS_Feed($dao['url'] ?? '', false);
|
||
$myFeed->_kind($dao['kind'] ?? FreshRSS_Feed::KIND_RSS);
|
||
$myFeed->_categoryId($category);
|
||
$myFeed->_name($dao['name']);
|
||
$myFeed->_website($dao['website'] ?? '', false);
|
||
$myFeed->_description($dao['description'] ?? '');
|
||
$myFeed->_lastUpdate($dao['lastUpdate'] ?? 0);
|
||
$myFeed->_priority($dao['priority'] ?? 10);
|
||
$myFeed->_pathEntries($dao['pathEntries'] ?? '');
|
||
$myFeed->_httpAuth(base64_decode($dao['httpAuth'] ?? ''));
|
||
$myFeed->_error($dao['error'] ?? 0);
|
||
$myFeed->_ttl($dao['ttl'] ?? FreshRSS_Feed::TTL_DEFAULT);
|
||
$myFeed->_attributes('', $dao['attributes'] ?? '');
|
||
$myFeed->_nbNotRead($dao['cache_nbUnreads'] ?? 0);
|
||
$myFeed->_nbEntries($dao['cache_nbEntries'] ?? 0);
|
||
if (isset($dao['id'])) {
|
||
$myFeed->_id($dao['id']);
|
||
}
|
||
$list[$key] = $myFeed;
|
||
}
|
||
|
||
return $list;
|
||
}
|
||
|
||
public function updateTTL() {
|
||
$sql = 'UPDATE `_feed` SET ttl=:new_value WHERE ttl=:old_value';
|
||
$stm = $this->pdo->prepare($sql);
|
||
if (!($stm && $stm->execute(array(':new_value' => FreshRSS_Feed::TTL_DEFAULT, ':old_value' => -2)))) {
|
||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||
Minz_Log::error('SQL warning updateTTL 1: ' . $info[2] . ' ' . $sql);
|
||
|
||
$sql2 = 'ALTER TABLE `_feed` ADD COLUMN ttl INT NOT NULL DEFAULT ' . FreshRSS_Feed::TTL_DEFAULT; //v0.7.3
|
||
$stm = $this->pdo->query($sql2);
|
||
if ($stm === false) {
|
||
$info = $this->pdo->errorInfo();
|
||
Minz_Log::error('SQL error updateTTL 2: ' . $info[2] . ' ' . $sql2);
|
||
}
|
||
} else {
|
||
$stm->execute(array(':new_value' => -3600, ':old_value' => -1));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @return int|false
|
||
*/
|
||
public function count() {
|
||
$sql = 'SELECT COUNT(e.id) AS count FROM `_feed` e';
|
||
$stm = $this->pdo->query($sql);
|
||
if ($stm == false) {
|
||
return false;
|
||
}
|
||
$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||
return isset($res[0]) ? $res[0] : 0;
|
||
}
|
||
}
|