2013-12-15 03:30:24 +01:00
|
|
|
|
<?php
|
2023-11-16 22:43:00 +01:00
|
|
|
|
declare(strict_types=1);
|
2013-12-23 13:35:54 +01:00
|
|
|
|
|
2023-03-22 08:26:39 +01:00
|
|
|
|
class FreshRSS_FeedDAO extends Minz_ModelPdo {
|
2018-05-01 17:02:11 +02:00
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
protected function addColumn(string $name): bool {
|
2022-09-19 11:51:44 +02:00
|
|
|
|
if ($this->pdo->inTransaction()) {
|
|
|
|
|
$this->pdo->commit();
|
|
|
|
|
}
|
2019-09-29 16:22:50 +02:00
|
|
|
|
Minz_Log::warning(__method__ . ': ' . $name);
|
2018-05-01 17:02:11 +02:00
|
|
|
|
try {
|
2022-02-28 20:22:43 +01:00
|
|
|
|
if ($name === 'kind') { //v1.20.0
|
|
|
|
|
return $this->pdo->exec('ALTER TABLE `_feed` ADD COLUMN kind SMALLINT DEFAULT 0') !== false;
|
2018-05-01 17:02:11 +02:00
|
|
|
|
}
|
|
|
|
|
} catch (Exception $e) {
|
2019-09-29 16:22:50 +02:00
|
|
|
|
Minz_Log::error(__method__ . ' error: ' . $e->getMessage());
|
2018-05-01 17:02:11 +02:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-07 13:15:48 +01:00
|
|
|
|
/** @param array<int|string> $errorInfo */
|
2023-03-31 08:23:39 +02:00
|
|
|
|
protected function autoUpdateDb(array $errorInfo): bool {
|
2018-05-01 17:02:11 +02:00
|
|
|
|
if (isset($errorInfo[0])) {
|
2018-09-29 20:47:17 +02:00
|
|
|
|
if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_FIELD_ERROR || $errorInfo[0] === FreshRSS_DatabaseDAOPGSQL::UNDEFINED_COLUMN) {
|
2023-11-07 13:15:48 +01:00
|
|
|
|
$errorLines = explode("\n", (string)$errorInfo[2], 2); // The relevant column name is on the first line, other lines are noise
|
2023-09-12 10:43:14 +02:00
|
|
|
|
foreach (['kind'] as $column) {
|
2022-07-04 09:53:26 +02:00
|
|
|
|
if (stripos($errorLines[0], $column) !== false) {
|
2018-05-01 17:02:11 +02:00
|
|
|
|
return $this->addColumn($column);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
/**
|
2023-05-02 14:38:32 +02:00
|
|
|
|
* @param array{'url':string,'kind':int,'category':int,'name':string,'website':string,'description':string,'lastUpdate':int,'priority'?:int,
|
|
|
|
|
* 'pathEntries'?:string,'httpAuth':string,'error':int|bool,'ttl'?:int,'attributes'?:string|array<string|mixed>} $valuesTmp
|
2023-04-15 22:04:37 +02:00
|
|
|
|
* @return int|false
|
|
|
|
|
*/
|
2022-02-06 14:31:36 +01:00
|
|
|
|
public function addFeed(array $valuesTmp) {
|
2022-07-04 09:53:26 +02:00
|
|
|
|
$sql = 'INSERT INTO `_feed` (url, kind, category, name, website, description, `lastUpdate`, priority, `pathEntries`, `httpAuth`, error, ttl, attributes)
|
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$stm = $this->pdo->prepare($sql);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2016-10-20 01:38:23 +02:00
|
|
|
|
$valuesTmp['url'] = safe_ascii($valuesTmp['url']);
|
|
|
|
|
$valuesTmp['website'] = safe_ascii($valuesTmp['website']);
|
2019-09-15 21:36:53 +02:00
|
|
|
|
if (!isset($valuesTmp['pathEntries'])) {
|
|
|
|
|
$valuesTmp['pathEntries'] = '';
|
|
|
|
|
}
|
Feature/new archiving (#2335)
* Change archiving config page layout
I've changed some wording and moved actions into a
maintenance section.
* Update purge action
Now we have more control on the purge action. The configuration allows
us to choose what to keep and what to discard in a more precise way.
At the moment, the configuration applies for all feeds.
* Add purge configuration on feed level
Now the extend purge configuration is available on feed level.
It is stored as attributes and will be used in the purge action.
* Update purge action
Now the purge action uses the feed configuration if it exists and
defaults on user configuration if not.
* Add empty option in period list
* Fix configuration warnings
* Add archiving configuration on categories
See #2369
* Add user info back
* Add explanations in UI
* Fixes for SQLite + error + misc.
* Fix invalid feed reference
* Short array syntax
Only for new code, so far
* Fix prefix error
* Query performance, default values
Work in progress
* Fix default values and confirm before leaving
Form cancel and confirm changes before leaving were broken.
And start taking advantage of the short echo syntax `<?= ?>` as we have
moved to PHP 5.4+
* More work
* Tuning SQL
* Fix MariaDB + performance issue
* SQL performance
* Fix SQLite bug
* Fix some attributes JSON encoding bugs
Especially for SQLite export/import
* More uniform, fix bugs
More uniform between global, category, feed settings
* Drop special cases for old articles during refresh
Instead will use lastSeen date with the new archiving logic.
This was generating problems anyway
https://github.com/FreshRSS/FreshRSS/issues/2154
* Draft drop index keep_history
Not needed anymore
* MySQL typo
Now properly tested with MySQL, PostgreSQL, SQLite
* More work for legacy values
Important to avoid overriding user's preference and risking deleting
data erroneously
* Fix PHP 7.3 / 7.4 warnings
@aledeg "Trying to use values of type null, bool, int, float or resource
as an array (such as $null["key"]) will now generate a notice. "
https://php.net/migration74.incompatible
* Reintroduce min articles and take care of legacy parameters
* A few changes forgotten
* Draft of migration + DROP of feed.keep_history
* Fix several errors
And give up using const for SQL to allow multiple database types (and we
cannot redefine a const)
* Add keep_min to categories + factorise archiving logic
* Legacy fix
* Fix bug yield from
* Minor: Use JSON_UNESCAPED_SLASHE for attributes
And make more uniform
* Fix sign and missing variable
* Fine tune the logic
2019-10-23 00:52:15 +02:00
|
|
|
|
if (!isset($valuesTmp['attributes'])) {
|
|
|
|
|
$valuesTmp['attributes'] = [];
|
|
|
|
|
}
|
2016-10-20 01:19:59 +02:00
|
|
|
|
|
2023-07-07 22:36:27 +02:00
|
|
|
|
$values = [
|
2023-01-26 08:13:38 +01:00
|
|
|
|
$valuesTmp['url'],
|
2022-02-28 20:22:43 +01:00
|
|
|
|
$valuesTmp['kind'] ?? FreshRSS_Feed::KIND_RSS,
|
2013-12-15 03:30:24 +01:00
|
|
|
|
$valuesTmp['category'],
|
2018-09-29 20:47:17 +02:00
|
|
|
|
mb_strcut(trim($valuesTmp['name']), 0, FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE, 'UTF-8'),
|
2023-01-26 08:13:38 +01:00
|
|
|
|
$valuesTmp['website'],
|
2023-10-30 20:47:50 +01:00
|
|
|
|
sanitizeHTML($valuesTmp['description'], ''),
|
2013-12-15 03:30:24 +01:00
|
|
|
|
$valuesTmp['lastUpdate'],
|
2023-07-07 22:36:27 +02:00
|
|
|
|
isset($valuesTmp['priority']) ? (int)$valuesTmp['priority'] : FreshRSS_Feed::PRIORITY_MAIN_STREAM,
|
2023-10-31 02:01:50 +01:00
|
|
|
|
mb_strcut($valuesTmp['pathEntries'], 0, 4096, 'UTF-8'),
|
2014-07-05 01:52:41 +02:00
|
|
|
|
base64_encode($valuesTmp['httpAuth']),
|
2023-07-07 22:36:27 +02:00
|
|
|
|
isset($valuesTmp['error']) ? (int)$valuesTmp['error'] : 0,
|
|
|
|
|
isset($valuesTmp['ttl']) ? (int)$valuesTmp['ttl'] : FreshRSS_Feed::TTL_DEFAULT,
|
2023-05-04 19:48:09 +02:00
|
|
|
|
is_string($valuesTmp['attributes']) ? $valuesTmp['attributes'] : json_encode($valuesTmp['attributes'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
|
2023-07-07 22:36:27 +02:00
|
|
|
|
];
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2023-04-28 14:01:11 +02:00
|
|
|
|
if ($stm !== false && $stm->execute($values)) {
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$feedId = $this->pdo->lastInsertId('`_feed_id_seq`');
|
|
|
|
|
return $feedId === false ? false : (int)$feedId;
|
2013-12-15 03:30:24 +01:00
|
|
|
|
} else {
|
2019-10-06 00:14:19 +02:00
|
|
|
|
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
2018-05-01 17:02:11 +02:00
|
|
|
|
if ($this->autoUpdateDb($info)) {
|
|
|
|
|
return $this->addFeed($valuesTmp);
|
|
|
|
|
}
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-04 09:53:26 +02:00
|
|
|
|
/** @return int|false */
|
|
|
|
|
public function addFeedObject(FreshRSS_Feed $feed) {
|
2022-01-08 21:58:55 +01:00
|
|
|
|
// Add feed only if we don’t find it in DB
|
2014-03-30 14:28:13 +02:00
|
|
|
|
$feed_search = $this->searchByUrl($feed->url());
|
|
|
|
|
if (!$feed_search) {
|
2023-07-07 22:36:27 +02:00
|
|
|
|
$values = [
|
2014-03-29 20:18:57 +01:00
|
|
|
|
'id' => $feed->id(),
|
|
|
|
|
'url' => $feed->url(),
|
2022-02-28 20:22:43 +01:00
|
|
|
|
'kind' => $feed->kind(),
|
2022-08-08 12:04:02 +02:00
|
|
|
|
'category' => $feed->categoryId(),
|
2022-09-09 22:56:34 +02:00
|
|
|
|
'name' => $feed->name(true),
|
2014-03-29 20:18:57 +01:00
|
|
|
|
'website' => $feed->website(),
|
|
|
|
|
'description' => $feed->description(),
|
|
|
|
|
'lastUpdate' => 0,
|
2023-05-02 14:38:32 +02:00
|
|
|
|
'error' => false,
|
2022-05-12 22:15:10 +02:00
|
|
|
|
'pathEntries' => $feed->pathEntries(),
|
2018-05-01 17:02:11 +02:00
|
|
|
|
'httpAuth' => $feed->httpAuth(),
|
2022-07-04 09:53:26 +02:00
|
|
|
|
'ttl' => $feed->ttl(true),
|
2018-05-01 17:02:11 +02:00
|
|
|
|
'attributes' => $feed->attributes(),
|
2023-07-07 22:36:27 +02:00
|
|
|
|
];
|
2014-03-29 20:18:57 +01:00
|
|
|
|
|
|
|
|
|
$id = $this->addFeed($values);
|
|
|
|
|
if ($id) {
|
|
|
|
|
$feed->_id($id);
|
|
|
|
|
$feed->faviconPrepare();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $id;
|
2022-07-04 09:53:26 +02:00
|
|
|
|
} 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();
|
2023-12-18 17:59:16 +01:00
|
|
|
|
$feed->_attributes(array_replace_recursive($existingAttributes, $importAttributes));
|
2014-03-29 20:18:57 +01:00
|
|
|
|
|
2022-07-04 09:53:26 +02:00
|
|
|
|
// Update some values of the existing feed using the import
|
|
|
|
|
$values = [
|
|
|
|
|
'kind' => $feed->kind(),
|
2022-09-09 22:56:34 +02:00
|
|
|
|
'name' => $feed->name(true),
|
2022-07-04 09:53:26 +02:00
|
|
|
|
'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();
|
|
|
|
|
}
|
2014-03-29 20:18:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
/**
|
2023-05-02 14:38:32 +02:00
|
|
|
|
* @param array{'url'?:string,'kind'?:int,'category'?:int,'name'?:string,'website'?:string,'description'?:string,'lastUpdate'?:int,'priority'?:int,
|
|
|
|
|
* 'pathEntries'?:string,'httpAuth'?:string,'error'?:int,'ttl'?:int,'attributes'?:string|array<string,mixed>} $valuesTmp $valuesTmp
|
2023-04-15 22:04:37 +02:00
|
|
|
|
* @return int|false
|
|
|
|
|
*/
|
2022-02-06 14:31:36 +01:00
|
|
|
|
public function updateFeed(int $id, array $valuesTmp) {
|
2023-07-07 22:36:27 +02:00
|
|
|
|
$values = [];
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$originalValues = $valuesTmp;
|
2018-09-29 20:47:17 +02:00
|
|
|
|
if (isset($valuesTmp['name'])) {
|
|
|
|
|
$valuesTmp['name'] = mb_strcut(trim($valuesTmp['name']), 0, FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE, 'UTF-8');
|
|
|
|
|
}
|
2016-10-20 01:19:59 +02:00
|
|
|
|
if (isset($valuesTmp['url'])) {
|
2016-10-20 01:38:23 +02:00
|
|
|
|
$valuesTmp['url'] = safe_ascii($valuesTmp['url']);
|
2016-10-20 01:19:59 +02:00
|
|
|
|
}
|
|
|
|
|
if (isset($valuesTmp['website'])) {
|
2016-10-20 01:38:23 +02:00
|
|
|
|
$valuesTmp['website'] = safe_ascii($valuesTmp['website']);
|
2016-10-20 01:19:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-12-15 03:30:24 +01:00
|
|
|
|
$set = '';
|
|
|
|
|
foreach ($valuesTmp as $key => $v) {
|
2017-01-27 10:26:31 +01:00
|
|
|
|
$set .= '`' . $key . '`=?, ';
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2018-05-01 17:02:11 +02:00
|
|
|
|
if ($key === 'httpAuth') {
|
2014-07-05 01:52:41 +02:00
|
|
|
|
$valuesTmp[$key] = base64_encode($v);
|
2018-05-01 17:02:11 +02:00
|
|
|
|
} elseif ($key === 'attributes') {
|
Feature/new archiving (#2335)
* Change archiving config page layout
I've changed some wording and moved actions into a
maintenance section.
* Update purge action
Now we have more control on the purge action. The configuration allows
us to choose what to keep and what to discard in a more precise way.
At the moment, the configuration applies for all feeds.
* Add purge configuration on feed level
Now the extend purge configuration is available on feed level.
It is stored as attributes and will be used in the purge action.
* Update purge action
Now the purge action uses the feed configuration if it exists and
defaults on user configuration if not.
* Add empty option in period list
* Fix configuration warnings
* Add archiving configuration on categories
See #2369
* Add user info back
* Add explanations in UI
* Fixes for SQLite + error + misc.
* Fix invalid feed reference
* Short array syntax
Only for new code, so far
* Fix prefix error
* Query performance, default values
Work in progress
* Fix default values and confirm before leaving
Form cancel and confirm changes before leaving were broken.
And start taking advantage of the short echo syntax `<?= ?>` as we have
moved to PHP 5.4+
* More work
* Tuning SQL
* Fix MariaDB + performance issue
* SQL performance
* Fix SQLite bug
* Fix some attributes JSON encoding bugs
Especially for SQLite export/import
* More uniform, fix bugs
More uniform between global, category, feed settings
* Drop special cases for old articles during refresh
Instead will use lastSeen date with the new archiving logic.
This was generating problems anyway
https://github.com/FreshRSS/FreshRSS/issues/2154
* Draft drop index keep_history
Not needed anymore
* MySQL typo
Now properly tested with MySQL, PostgreSQL, SQLite
* More work for legacy values
Important to avoid overriding user's preference and risking deleting
data erroneously
* Fix PHP 7.3 / 7.4 warnings
@aledeg "Trying to use values of type null, bool, int, float or resource
as an array (such as $null["key"]) will now generate a notice. "
https://php.net/migration74.incompatible
* Reintroduce min articles and take care of legacy parameters
* A few changes forgotten
* Draft of migration + DROP of feed.keep_history
* Fix several errors
And give up using const for SQL to allow multiple database types (and we
cannot redefine a const)
* Add keep_min to categories + factorise archiving logic
* Legacy fix
* Fix bug yield from
* Minor: Use JSON_UNESCAPED_SLASHE for attributes
And make more uniform
* Fix sign and missing variable
* Fine tune the logic
2019-10-23 00:52:15 +02:00
|
|
|
|
$valuesTmp[$key] = is_string($valuesTmp[$key]) ? $valuesTmp[$key] : json_encode($valuesTmp[$key], JSON_UNESCAPED_SLASHES);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-05 01:52:41 +02:00
|
|
|
|
$set = substr($set, 0, -2);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$sql = 'UPDATE `_feed` SET ' . $set . ' WHERE id=?';
|
|
|
|
|
$stm = $this->pdo->prepare($sql);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
|
|
|
|
foreach ($valuesTmp as $v) {
|
|
|
|
|
$values[] = $v;
|
|
|
|
|
}
|
|
|
|
|
$values[] = $id;
|
|
|
|
|
|
2023-04-28 14:01:11 +02:00
|
|
|
|
if ($stm !== false && $stm->execute($values)) {
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return $stm->rowCount();
|
|
|
|
|
} else {
|
2019-10-06 00:14:19 +02:00
|
|
|
|
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
2018-05-01 17:02:11 +02:00
|
|
|
|
if ($this->autoUpdateDb($info)) {
|
2023-05-02 14:38:32 +02:00
|
|
|
|
return $this->updateFeed($id, $originalValues);
|
2018-05-01 17:02:11 +02:00
|
|
|
|
}
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info) . ' for feed ' . $id);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
/**
|
2023-12-18 17:59:16 +01:00
|
|
|
|
* @param non-empty-string $key
|
2023-04-15 22:04:37 +02:00
|
|
|
|
* @param string|array<mixed>|bool|int|null $value
|
|
|
|
|
* @return int|false
|
|
|
|
|
*/
|
2022-02-06 14:31:36 +01:00
|
|
|
|
public function updateFeedAttribute(FreshRSS_Feed $feed, string $key, $value) {
|
2023-12-18 17:59:16 +01:00
|
|
|
|
$feed->_attribute($key, $value);
|
2022-02-06 14:31:36 +01:00
|
|
|
|
return $this->updateFeed(
|
2023-07-07 22:36:27 +02:00
|
|
|
|
$feed->id(),
|
|
|
|
|
['attributes' => $feed->attributes()]
|
|
|
|
|
);
|
2018-05-01 17:02:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 00:52:24 +01:00
|
|
|
|
/**
|
2023-04-15 22:04:37 +02:00
|
|
|
|
* @return int|false
|
2022-01-05 00:52:24 +01:00
|
|
|
|
* @see updateCachedValue()
|
|
|
|
|
*/
|
2022-02-06 14:31:36 +01:00
|
|
|
|
public function updateLastUpdate(int $id, bool $inError = false, int $mtime = 0) {
|
2021-02-28 12:26:24 +01:00
|
|
|
|
$sql = 'UPDATE `_feed` SET `lastUpdate`=?, error=? WHERE id=?';
|
2023-07-07 22:36:27 +02:00
|
|
|
|
$values = [
|
2017-03-26 14:07:06 +02:00
|
|
|
|
$mtime <= 0 ? time() : $mtime,
|
2016-10-05 17:48:24 +02:00
|
|
|
|
$inError ? 1 : 0,
|
2013-12-15 03:30:24 +01:00
|
|
|
|
$id,
|
2023-07-07 22:36:27 +02:00
|
|
|
|
];
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$stm = $this->pdo->prepare($sql);
|
2014-02-01 14:04:37 +01:00
|
|
|
|
|
2023-04-28 14:01:11 +02:00
|
|
|
|
if ($stm !== false && $stm->execute($values)) {
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return $stm->rowCount();
|
|
|
|
|
} else {
|
2019-10-06 00:14:19 +02:00
|
|
|
|
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
2022-07-04 09:53:26 +02:00
|
|
|
|
Minz_Log::warning(__METHOD__ . ' error: ' . $sql . ' : ' . json_encode($info));
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
/** @return int|false */
|
2022-02-06 14:31:36 +01:00
|
|
|
|
public function mute(int $id, bool $value = true) {
|
2021-03-26 19:13:23 +01:00
|
|
|
|
$sql = 'UPDATE `_feed` SET ttl=' . ($value ? '-' : '') . 'ABS(ttl) WHERE id=' . intval($id);
|
|
|
|
|
return $this->pdo->exec($sql);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
/** @return int|false */
|
2022-02-06 14:31:36 +01:00
|
|
|
|
public function changeCategory(int $idOldCat, int $idNewCat) {
|
2018-09-29 20:47:17 +02:00
|
|
|
|
$catDAO = FreshRSS_Factory::createCategoryDao();
|
2014-07-05 01:52:41 +02:00
|
|
|
|
$newCat = $catDAO->searchById($idNewCat);
|
2023-05-02 14:38:32 +02:00
|
|
|
|
if ($newCat === null) {
|
2014-07-05 01:52:41 +02:00
|
|
|
|
$newCat = $catDAO->getDefault();
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|
2023-12-18 17:59:16 +01:00
|
|
|
|
if ($newCat === null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$sql = 'UPDATE `_feed` SET category=? WHERE category=?';
|
|
|
|
|
$stm = $this->pdo->prepare($sql);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2023-07-07 22:36:27 +02:00
|
|
|
|
$values = [
|
2014-07-05 01:52:41 +02:00
|
|
|
|
$newCat->id(),
|
2023-07-07 22:36:27 +02:00
|
|
|
|
$idOldCat,
|
|
|
|
|
];
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2023-04-28 14:01:11 +02:00
|
|
|
|
if ($stm !== false && $stm->execute($values)) {
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return $stm->rowCount();
|
|
|
|
|
} else {
|
2019-10-06 00:14:19 +02:00
|
|
|
|
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-04 09:53:26 +02:00
|
|
|
|
/** @return int|false */
|
2022-02-06 14:31:36 +01:00
|
|
|
|
public function deleteFeed(int $id) {
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$sql = 'DELETE FROM `_feed` WHERE id=?';
|
|
|
|
|
$stm = $this->pdo->prepare($sql);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2023-07-07 22:36:27 +02:00
|
|
|
|
$values = [$id];
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2023-04-28 14:01:11 +02:00
|
|
|
|
if ($stm !== false && $stm->execute($values)) {
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return $stm->rowCount();
|
|
|
|
|
} else {
|
2019-10-06 00:14:19 +02:00
|
|
|
|
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-04 09:53:26 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param bool|null $muted to include only muted feeds
|
|
|
|
|
* @return int|false
|
|
|
|
|
*/
|
2023-04-17 08:30:21 +02:00
|
|
|
|
public function deleteFeedByCategory(int $id, ?bool $muted = null) {
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$sql = 'DELETE FROM `_feed` WHERE category=?';
|
2022-07-04 09:53:26 +02:00
|
|
|
|
if ($muted) {
|
|
|
|
|
$sql .= ' AND ttl < 0';
|
|
|
|
|
}
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$stm = $this->pdo->prepare($sql);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2023-07-07 22:36:27 +02:00
|
|
|
|
$values = [$id];
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2023-04-28 14:01:11 +02:00
|
|
|
|
if ($stm !== false && $stm->execute($values)) {
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return $stm->rowCount();
|
|
|
|
|
} else {
|
2019-10-06 00:14:19 +02:00
|
|
|
|
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-02 14:38:32 +02:00
|
|
|
|
/** @return Traversable<array{'id':int,'url':string,'kind':int,'category':int,'name':string,'website':string,'description':string,'lastUpdate':int,'priority'?:int,
|
|
|
|
|
* 'pathEntries'?:string,'httpAuth':string,'error':int|bool,'ttl'?:int,'attributes'?:string}> */
|
2023-04-23 11:58:15 +02:00
|
|
|
|
public function selectAll(): Traversable {
|
2021-02-28 12:26:24 +01:00
|
|
|
|
$sql = <<<'SQL'
|
2022-02-28 20:22:43 +01:00
|
|
|
|
SELECT id, url, kind, category, name, website, description, `lastUpdate`,
|
2021-02-28 12:26:24 +01:00
|
|
|
|
priority, `pathEntries`, `httpAuth`, error, ttl, attributes
|
|
|
|
|
FROM `_feed`
|
|
|
|
|
SQL;
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$stm = $this->pdo->query($sql);
|
2023-05-02 14:38:32 +02:00
|
|
|
|
if ($stm === false) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-09-15 21:36:53 +02:00
|
|
|
|
while ($row = $stm->fetch(PDO::FETCH_ASSOC)) {
|
2023-12-18 17:59:16 +01:00
|
|
|
|
/** @var array{'id':int,'url':string,'kind':int,'category':int,'name':string,'website':string,'description':string,'lastUpdate':int,'priority'?:int,
|
|
|
|
|
* 'pathEntries'?:string,'httpAuth':string,'error':int|bool,'ttl'?:int,'attributes'?:string} $row */
|
2019-09-15 21:36:53 +02:00
|
|
|
|
yield $row;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-22 08:26:39 +01:00
|
|
|
|
public function searchById(int $id): ?FreshRSS_Feed {
|
2020-05-21 20:55:06 +02:00
|
|
|
|
$sql = 'SELECT * FROM `_feed` WHERE id=:id';
|
2023-04-28 14:01:11 +02:00
|
|
|
|
$res = $this->fetchAssoc($sql, [':id' => $id]);
|
|
|
|
|
if ($res == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2023-05-02 14:38:32 +02:00
|
|
|
|
/** @var array<int,array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int,
|
|
|
|
|
* 'priority'?:int,'pathEntries'?:string,'httpAuth':string,'error':int,'ttl'?:int,'attributes'?:string}> $res */
|
2024-02-26 09:01:03 +01:00
|
|
|
|
$feeds = self::daoToFeeds($res);
|
2023-05-02 14:38:32 +02:00
|
|
|
|
return $feeds[$id] ?? null;
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|
2022-02-06 14:31:36 +01:00
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
public function searchByUrl(string $url): ?FreshRSS_Feed {
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$sql = 'SELECT * FROM `_feed` WHERE url=:url';
|
|
|
|
|
$res = $this->fetchAssoc($sql, [':url' => $url]);
|
|
|
|
|
/** @var array<int,array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int,
|
|
|
|
|
* 'priority'?:int,'pathEntries'?:string,'httpAuth':string,'error':int,'ttl'?:int,'attributes'?:string}> $res */
|
2024-02-26 09:01:03 +01:00
|
|
|
|
return empty($res[0]) ? null : (current(self::daoToFeeds($res)) ?: null);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-02 14:38:32 +02:00
|
|
|
|
/** @return array<int> */
|
|
|
|
|
public function listFeedsIds(): array {
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$sql = 'SELECT id FROM `_feed`';
|
2023-05-02 14:38:32 +02:00
|
|
|
|
/** @var array<int> $res */
|
|
|
|
|
$res = $this->fetchColumn($sql, 0) ?? [];
|
|
|
|
|
return $res;
|
2016-10-23 13:37:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-06 14:31:36 +01:00
|
|
|
|
/**
|
2024-02-26 09:01:03 +01:00
|
|
|
|
* @return array<int,FreshRSS_Feed>
|
2022-02-06 14:31:36 +01:00
|
|
|
|
*/
|
|
|
|
|
public function listFeeds(): array {
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$sql = 'SELECT * FROM `_feed` ORDER BY name';
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$res = $this->fetchAssoc($sql);
|
|
|
|
|
/** @var array<array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int,
|
|
|
|
|
* 'priority':int,'pathEntries':string,'httpAuth':string,'error':int,'ttl':int,'attributes':string}>|null $res */
|
2024-02-26 09:01:03 +01:00
|
|
|
|
return $res == null ? [] : self::daoToFeeds($res);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
/** @return array<string,string> */
|
2023-04-17 08:30:21 +02:00
|
|
|
|
public function listFeedsNewestItemUsec(?int $id_feed = null): array {
|
2020-04-01 02:08:45 +02:00
|
|
|
|
$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);
|
|
|
|
|
}
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$res = $this->fetchAssoc($sql);
|
|
|
|
|
/** @var array<array{'id_feed':int,'newest_item_us':string}>|null $res */
|
|
|
|
|
if ($res == null) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
2020-04-01 02:08:45 +02:00
|
|
|
|
$newestItemUsec = [];
|
|
|
|
|
foreach ($res as $line) {
|
|
|
|
|
$newestItemUsec['f_' . $line['id_feed']] = $line['newest_item_us'];
|
|
|
|
|
}
|
|
|
|
|
return $newestItemUsec;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 00:39:54 +02:00
|
|
|
|
/**
|
2023-04-20 21:46:25 +02:00
|
|
|
|
* @param int $defaultCacheDuration Use -1 to return all feeds, without filtering them by TTL.
|
2024-02-26 09:01:03 +01:00
|
|
|
|
* @return array<int,FreshRSS_Feed>
|
2016-10-05 00:39:54 +02:00
|
|
|
|
*/
|
2023-04-17 08:30:21 +02:00
|
|
|
|
public function listFeedsOrderUpdate(int $defaultCacheDuration = 3600, int $limit = 0): array {
|
2023-12-15 23:04:29 +01:00
|
|
|
|
$sql = 'SELECT id, url, kind, category, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, ttl, attributes, `cache_nbEntries`, `cache_nbUnreads` '
|
2021-02-28 12:26:24 +01:00
|
|
|
|
. 'FROM `_feed` '
|
|
|
|
|
. ($defaultCacheDuration < 0 ? '' : 'WHERE ttl >= ' . FreshRSS_Feed::TTL_DEFAULT
|
2023-07-07 22:36:27 +02:00
|
|
|
|
. ' AND `lastUpdate` < (' . (time() + 60)
|
|
|
|
|
. '-(CASE WHEN ttl=' . FreshRSS_Feed::TTL_DEFAULT . ' THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) ')
|
2021-02-28 12:26:24 +01:00
|
|
|
|
. 'ORDER BY `lastUpdate` '
|
|
|
|
|
. ($limit < 1 ? '' : 'LIMIT ' . intval($limit));
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$stm = $this->pdo->query($sql);
|
|
|
|
|
if ($stm !== false) {
|
2024-02-26 09:01:03 +01:00
|
|
|
|
return self::daoToFeeds($stm->fetchAll(PDO::FETCH_ASSOC));
|
2018-05-01 17:02:11 +02:00
|
|
|
|
} else {
|
2022-06-19 20:08:42 +02:00
|
|
|
|
$info = $this->pdo->errorInfo();
|
2018-05-01 17:02:11 +02:00
|
|
|
|
if ($this->autoUpdateDb($info)) {
|
2022-07-04 09:53:26 +02:00
|
|
|
|
return $this->listFeedsOrderUpdate($defaultCacheDuration, $limit);
|
2018-05-01 17:02:11 +02:00
|
|
|
|
}
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
2023-07-07 22:36:27 +02:00
|
|
|
|
return [];
|
2018-05-01 17:02:11 +02:00
|
|
|
|
}
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-18 17:59:16 +01:00
|
|
|
|
/** @return array<int,string> */
|
2023-05-02 14:38:32 +02:00
|
|
|
|
public function listTitles(int $id, int $limit = 0): array {
|
2021-09-19 10:56:38 +02:00
|
|
|
|
$sql = 'SELECT title FROM `_entry` WHERE id_feed=:id_feed ORDER BY id DESC'
|
|
|
|
|
. ($limit < 1 ? '' : ' LIMIT ' . intval($limit));
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$res = $this->fetchColumn($sql, 0, [':id_feed' => $id]) ?? [];
|
2023-12-18 17:59:16 +01:00
|
|
|
|
/** @var array<int,string> $res */
|
2023-05-02 14:38:32 +02:00
|
|
|
|
return $res;
|
2021-09-19 10:56:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-12 22:15:10 +02:00
|
|
|
|
/**
|
2022-07-04 09:53:26 +02:00
|
|
|
|
* @param bool|null $muted to include only muted feeds
|
2024-02-26 09:01:03 +01:00
|
|
|
|
* @return array<int,FreshRSS_Feed>
|
2022-05-12 22:15:10 +02:00
|
|
|
|
*/
|
2023-04-17 08:30:21 +02:00
|
|
|
|
public function listByCategory(int $cat, ?bool $muted = null): array {
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$sql = 'SELECT * FROM `_feed` WHERE category=:category';
|
2022-07-04 09:53:26 +02:00
|
|
|
|
if ($muted) {
|
|
|
|
|
$sql .= ' AND ttl < 0';
|
|
|
|
|
}
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$res = $this->fetchAssoc($sql, [':category' => $cat]);
|
|
|
|
|
if ($res == null) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2023-07-07 22:36:27 +02:00
|
|
|
|
/**
|
|
|
|
|
* @var array<int,array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int,
|
|
|
|
|
* 'priority'?:int,'pathEntries'?:string,'httpAuth':string,'error':int,'ttl'?:int,'attributes'?:string}> $res
|
|
|
|
|
*/
|
2024-02-26 09:01:03 +01:00
|
|
|
|
$feeds = self::daoToFeeds($res);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2024-02-26 09:01:03 +01:00
|
|
|
|
uasort($feeds, static function (FreshRSS_Feed $a, FreshRSS_Feed $b) {
|
2020-08-05 09:40:58 +02:00
|
|
|
|
return strnatcasecmp($a->name(), $b->name());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return $feeds;
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
public function countEntries(int $id): int {
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=:id_feed';
|
|
|
|
|
$res = $this->fetchColumn($sql, 0, ['id_feed' => $id]);
|
|
|
|
|
return isset($res[0]) ? (int)($res[0]) : -1;
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|
2014-03-01 15:47:15 +01:00
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
public function countNotRead(int $id): int {
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=:id_feed AND is_read=0';
|
|
|
|
|
$res = $this->fetchColumn($sql, 0, ['id_feed' => $id]);
|
|
|
|
|
return isset($res[0]) ? (int)($res[0]) : -1;
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|
2014-03-01 15:47:15 +01:00
|
|
|
|
|
2022-02-06 14:31:36 +01:00
|
|
|
|
/**
|
|
|
|
|
* @return int|false
|
|
|
|
|
*/
|
|
|
|
|
public function updateCachedValues(int $id = 0) {
|
2019-09-29 16:22:50 +02:00
|
|
|
|
//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` '
|
2021-02-28 12:26:24 +01:00
|
|
|
|
. '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)'
|
2022-02-06 14:31:36 +01:00
|
|
|
|
. ($id != 0 ? ' WHERE id=:id' : '');
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$stm = $this->pdo->prepare($sql);
|
2023-04-28 14:01:11 +02:00
|
|
|
|
if ($stm !== false && $id != 0) {
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$stm->bindParam(':id', $id, PDO::PARAM_INT);
|
2017-03-26 14:07:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-28 14:01:11 +02:00
|
|
|
|
if ($stm !== false && $stm->execute()) {
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return $stm->rowCount();
|
|
|
|
|
} else {
|
2019-10-06 00:14:19 +02:00
|
|
|
|
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-06 14:31:36 +01:00
|
|
|
|
/**
|
2022-06-25 11:15:51 +02:00
|
|
|
|
* Remember to call updateCachedValues() after calling this function
|
|
|
|
|
* @return int|false number of lines affected or false in case of error
|
2022-02-06 14:31:36 +01:00
|
|
|
|
*/
|
2023-12-03 19:52:02 +01:00
|
|
|
|
public function markAsReadMaxUnread(int $id, int $n) {
|
2021-09-19 10:56:38 +02:00
|
|
|
|
//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;
|
|
|
|
|
|
2022-06-25 11:15:51 +02:00
|
|
|
|
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 {
|
2021-09-19 10:56:38 +02:00
|
|
|
|
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
2021-09-19 10:56:38 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-06-25 11:15:51 +02:00
|
|
|
|
}
|
2021-09-19 10:56:38 +02:00
|
|
|
|
|
2022-06-25 11:15:51 +02:00
|
|
|
|
/**
|
|
|
|
|
* 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
|
2023-04-20 21:46:25 +02:00
|
|
|
|
WHERE id_feed=:id_feed1 AND is_read=0 AND (
|
|
|
|
|
`lastSeen` + 60 < (SELECT s1.maxlastseen FROM (
|
|
|
|
|
SELECT MAX(e2.`lastSeen`) AS maxlastseen FROM `_entry` e2 WHERE e2.id_feed = :id_feed2
|
|
|
|
|
) s1)
|
|
|
|
|
)
|
2022-06-25 11:15:51 +02:00
|
|
|
|
SQL;
|
2021-09-19 10:56:38 +02:00
|
|
|
|
|
2022-06-25 11:15:51 +02:00
|
|
|
|
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();
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
2022-06-25 11:15:51 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-09-19 10:56:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-06 14:31:36 +01:00
|
|
|
|
/**
|
|
|
|
|
* @return int|false
|
|
|
|
|
*/
|
|
|
|
|
public function truncate(int $id) {
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$sql = 'DELETE FROM `_entry` WHERE id_feed=:id';
|
|
|
|
|
$stm = $this->pdo->prepare($sql);
|
|
|
|
|
$this->pdo->beginTransaction();
|
2023-05-02 14:38:32 +02:00
|
|
|
|
if (!($stm !== false &&
|
|
|
|
|
$stm->bindParam(':id', $id, PDO::PARAM_INT) &&
|
|
|
|
|
$stm->execute())) {
|
2019-10-06 00:14:19 +02:00
|
|
|
|
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$this->pdo->rollBack();
|
2014-07-03 21:26:30 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2013-12-15 03:30:24 +01:00
|
|
|
|
$affected = $stm->rowCount();
|
|
|
|
|
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$sql = 'UPDATE `_feed` SET `cache_nbEntries`=0, `cache_nbUnreads`=0, `lastUpdate`=0 WHERE id=:id';
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$stm = $this->pdo->prepare($sql);
|
2023-05-02 14:38:32 +02:00
|
|
|
|
if (!($stm !== false &&
|
|
|
|
|
$stm->bindParam(':id', $id, PDO::PARAM_INT) &&
|
|
|
|
|
$stm->execute())) {
|
2019-10-06 00:14:19 +02:00
|
|
|
|
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$this->pdo->rollBack();
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-29 16:22:50 +02:00
|
|
|
|
$this->pdo->commit();
|
2013-12-15 03:30:24 +01:00
|
|
|
|
return $affected;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
public function purge(): bool {
|
2019-12-03 22:32:17 +01:00
|
|
|
|
$sql = 'DELETE FROM `_entry`';
|
|
|
|
|
$stm = $this->pdo->prepare($sql);
|
|
|
|
|
$this->pdo->beginTransaction();
|
|
|
|
|
if (!($stm && $stm->execute())) {
|
|
|
|
|
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . ' A ' . json_encode($info));
|
2019-12-03 22:32:17 +01:00
|
|
|
|
$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();
|
2023-04-28 14:01:11 +02:00
|
|
|
|
Minz_Log::error('SQL error ' . __METHOD__ . ' B ' . json_encode($info));
|
2019-12-03 22:32:17 +01:00
|
|
|
|
$this->pdo->rollBack();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 22:04:37 +02:00
|
|
|
|
return $this->pdo->commit();
|
2019-12-03 22:32:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-06 14:31:36 +01:00
|
|
|
|
/**
|
2023-05-02 14:38:32 +02:00
|
|
|
|
* @param array<int,array{'id'?:int,'url'?:string,'kind'?:int,'category'?:int,'name'?:string,'website'?:string,'description'?:string,'lastUpdate'?:int,'priority'?:int,
|
|
|
|
|
* 'pathEntries'?:string,'httpAuth'?:string,'error'?:int|bool,'ttl'?:int,'attributes'?:string,'cache_nbUnreads'?:int,'cache_nbEntries'?:int}> $listDAO
|
2023-04-28 14:01:11 +02:00
|
|
|
|
* @return array<int,FreshRSS_Feed>
|
2022-02-06 14:31:36 +01:00
|
|
|
|
*/
|
2024-02-26 09:01:03 +01:00
|
|
|
|
public static function daoToFeeds(array $listDAO, ?int $catID = null): array {
|
2023-07-07 22:36:27 +02:00
|
|
|
|
$list = [];
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
|
|
|
|
foreach ($listDAO as $key => $dao) {
|
2023-11-18 23:21:20 +01:00
|
|
|
|
FreshRSS_DatabaseDAO::pdoInt($dao, ['id', 'kind', 'category', 'lastUpdate', 'priority', 'error', 'ttl', 'cache_nbUnreads', 'cache_nbEntries']);
|
2014-07-05 01:52:41 +02:00
|
|
|
|
if (!isset($dao['name'])) {
|
2013-12-15 03:30:24 +01:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2014-07-05 01:52:41 +02:00
|
|
|
|
if (isset($dao['id'])) {
|
2023-05-02 14:38:32 +02:00
|
|
|
|
$key = (int)$dao['id'];
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|
2013-12-25 19:53:09 +01:00
|
|
|
|
if ($catID === null) {
|
2023-04-15 22:04:37 +02:00
|
|
|
|
$category = $dao['category'] ?? 0;
|
2013-12-27 14:11:17 +01:00
|
|
|
|
} else {
|
2017-09-22 12:13:46 +02:00
|
|
|
|
$category = $catID;
|
2013-12-25 19:53:09 +01:00
|
|
|
|
}
|
2013-12-15 03:30:24 +01:00
|
|
|
|
|
2022-02-28 20:22:43 +01:00
|
|
|
|
$myFeed = new FreshRSS_Feed($dao['url'] ?? '', false);
|
|
|
|
|
$myFeed->_kind($dao['kind'] ?? FreshRSS_Feed::KIND_RSS);
|
2022-08-08 12:04:02 +02:00
|
|
|
|
$myFeed->_categoryId($category);
|
2013-12-25 19:53:09 +01:00
|
|
|
|
$myFeed->_name($dao['name']);
|
2022-02-28 20:22:43 +01:00
|
|
|
|
$myFeed->_website($dao['website'] ?? '', false);
|
|
|
|
|
$myFeed->_description($dao['description'] ?? '');
|
|
|
|
|
$myFeed->_lastUpdate($dao['lastUpdate'] ?? 0);
|
|
|
|
|
$myFeed->_priority($dao['priority'] ?? 10);
|
|
|
|
|
$myFeed->_pathEntries($dao['pathEntries'] ?? '');
|
2023-05-11 13:02:04 +02:00
|
|
|
|
$myFeed->_httpAuth(base64_decode($dao['httpAuth'] ?? '', true) ?: '');
|
2022-02-28 20:22:43 +01:00
|
|
|
|
$myFeed->_error($dao['error'] ?? 0);
|
|
|
|
|
$myFeed->_ttl($dao['ttl'] ?? FreshRSS_Feed::TTL_DEFAULT);
|
2023-12-18 17:59:16 +01:00
|
|
|
|
$myFeed->_attributes($dao['attributes'] ?? '');
|
2023-12-03 19:52:02 +01:00
|
|
|
|
$myFeed->_nbNotRead($dao['cache_nbUnreads'] ?? -1);
|
|
|
|
|
$myFeed->_nbEntries($dao['cache_nbEntries'] ?? -1);
|
2014-07-05 01:52:41 +02:00
|
|
|
|
if (isset($dao['id'])) {
|
|
|
|
|
$myFeed->_id($dao['id']);
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|
|
|
|
|
$list[$key] = $myFeed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $list;
|
|
|
|
|
}
|
2018-01-01 20:34:06 +01:00
|
|
|
|
|
2023-05-13 22:47:51 +02:00
|
|
|
|
public function count(): int {
|
2019-12-03 22:32:17 +01:00
|
|
|
|
$sql = 'SELECT COUNT(e.id) AS count FROM `_feed` e';
|
|
|
|
|
$stm = $this->pdo->query($sql);
|
|
|
|
|
if ($stm == false) {
|
2023-05-13 22:47:51 +02:00
|
|
|
|
return -1;
|
2019-12-03 22:32:17 +01:00
|
|
|
|
}
|
|
|
|
|
$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
2023-11-18 23:21:20 +01:00
|
|
|
|
return (int)($res[0] ?? 0);
|
2019-12-03 22:32:17 +01:00
|
|
|
|
}
|
2013-12-15 03:30:24 +01:00
|
|
|
|
}
|