aboutsummaryrefslogtreecommitdiff
path: root/app/Models/EntryDAO.php
diff options
context:
space:
mode:
Diffstat (limited to 'app/Models/EntryDAO.php')
-rw-r--r--app/Models/EntryDAO.php90
1 files changed, 56 insertions, 34 deletions
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php
index d7a9a2cbc..707002875 100644
--- a/app/Models/EntryDAO.php
+++ b/app/Models/EntryDAO.php
@@ -119,6 +119,7 @@ SQL;
$this->pdo->commit();
}
Minz_Log::warning(__METHOD__ . ': ' . $name);
+ require APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php';
try {
if ($name === 'attributes') { //v1.20.0
$sql = <<<'SQL'
@@ -127,6 +128,13 @@ ALTER TABLE `_entrytmp` ADD COLUMN attributes TEXT;
SQL;
return $this->pdo->exec($sql) !== false;
}
+ if ($name === 'lastUserModified') { //v1.28.0
+ $sql = $GLOBALS['ALTER_TABLE_ENTRY_LAST_USER_MODIFIED'];
+ if (!is_string($sql)) {
+ throw new Exception('ALTER_TABLE_ENTRY_LAST_USER_MODIFIED is not a string!');
+ }
+ return $this->pdo->exec($sql) !== false;
+ }
} catch (Exception $e) {
Minz_Log::error(__METHOD__ . ' error: ' . $e->getMessage());
}
@@ -261,8 +269,11 @@ SQL;
private PDOStatement|null|false $updateEntryPrepared = null;
- /** @param array{'id':string,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int,'lastSeen':int,'hash':string,
- * 'is_read':bool|int|null,'is_favorite':bool|int|null,'id_feed':int,'tags':string,'attributes':array<string,mixed>} $valuesTmp */
+ /**
+ * @param array{id:string,guid:string,title:string,author:string,content:string,link:string,
+ * date:int,lastSeen:int,lastUserModified?:int,hash:string,
+ * is_read:bool|int|null,is_favorite:bool|int|null,id_feed:int,tags:string,attributes:array<string,mixed>} $valuesTmp
+ */
public function updateEntry(array $valuesTmp): bool {
if (!isset($valuesTmp['is_read'])) {
$valuesTmp['is_read'] = null;
@@ -270,12 +281,16 @@ SQL;
if (!isset($valuesTmp['is_favorite'])) {
$valuesTmp['is_favorite'] = null;
}
+ if (empty($valuesTmp['lastUserModified'])) {
+ $valuesTmp['lastUserModified'] = 0;
+ }
if ($this->updateEntryPrepared == null) {
$sql = 'UPDATE `_entry` '
. 'SET title=:title, author=:author, '
. (static::isCompressed() ? 'content_bin=COMPRESS(:content)' : 'content=:content')
. ', link=:link, date=:date, `lastSeen`=:last_seen'
+ . ', `lastUserModified`=MAX(:last_user_modified, `lastUserModified`)'
. ', hash=' . static::sqlHexDecode(':hash')
. ', is_read=COALESCE(:is_read, is_read)'
. ', is_favorite=COALESCE(:is_favorite, is_favorite)'
@@ -300,6 +315,7 @@ SQL;
$this->updateEntryPrepared->bindParam(':link', $valuesTmp['link']);
$this->updateEntryPrepared->bindParam(':date', $valuesTmp['date'], PDO::PARAM_INT);
$this->updateEntryPrepared->bindParam(':last_seen', $valuesTmp['lastSeen'], PDO::PARAM_INT);
+ $this->updateEntryPrepared->bindParam(':last_user_modified', $valuesTmp['lastUserModified'], PDO::PARAM_INT);
if ($valuesTmp['is_read'] === null) {
$this->updateEntryPrepared->bindValue(':is_read', null, PDO::PARAM_NULL);
} else {
@@ -332,7 +348,8 @@ SQL;
return true;
} else {
$info = $this->updateEntryPrepared == false ? $this->pdo->errorInfo() : $this->updateEntryPrepared->errorInfo();
- /** @var array{id:string,guid:string,title:string,author:string,content:string,link:string,date:int,lastSeen:int,hash:string,
+ /** @var array{id:string,guid:string,title:string,author:string,content:string,link:string,
+ * date:int,lastSeen:int,lastUserModified:int,hash:string,
* is_read:bool|int|null,is_favorite:bool|int|null,id_feed:int,tags:string,attributes:array<string,mixed>} $valuesTmp */
/** @var array{0:string,1:int,2:string} $info */
if ($this->autoUpdateDb($info)) {
@@ -381,9 +398,10 @@ SQL;
return $affected;
}
$sql = 'UPDATE `_entry` '
- . 'SET is_favorite=? '
+ . 'SET is_favorite=?, `lastUserModified`=? '
. 'WHERE id IN (' . str_repeat('?,', count($ids) - 1) . '?)';
$values = [$is_favorite ? 1 : 0];
+ $values[] = time();
$values = array_merge($values, $ids);
$stm = $this->pdo->prepare($sql);
if ($stm !== false && $stm->execute($values)) {
@@ -462,9 +480,9 @@ SQL;
FreshRSS_UserDAO::touch();
$sql = 'UPDATE `_entry` '
- . 'SET is_read=? '
- . 'WHERE id IN (' . str_repeat('?,', count($ids) - 1) . '?)';
- $values = [$is_read ? 1 : 0];
+ . 'SET is_read=?, `lastUserModified`=? '
+ . 'WHERE is_read<>? AND id IN (' . str_repeat('?,', count($ids) - 1) . '?)';
+ $values = [$is_read ? 1 : 0, time(), $is_read ? 1 : 0];
$values = array_merge($values, $ids);
$stm = $this->pdo->prepare($sql);
if ($stm === false || !$stm->execute($values)) {
@@ -480,10 +498,10 @@ SQL;
} else {
FreshRSS_UserDAO::touch();
$sql = 'UPDATE `_entry` e INNER JOIN `_feed` f ON e.id_feed=f.id '
- . 'SET e.is_read=?,'
+ . 'SET e.is_read=?,`lastUserModified`=?,'
. 'f.`cache_nbUnreads`=f.`cache_nbUnreads`' . ($is_read ? '-' : '+') . '1 '
. 'WHERE e.id=? AND e.is_read=?';
- $values = [$is_read ? 1 : 0, $ids, $is_read ? 0 : 1];
+ $values = [$is_read ? 1 : 0, time(), $ids, $is_read ? 0 : 1];
$stm = $this->pdo->prepare($sql);
if ($stm !== false && $stm->execute($values)) {
return $stm->rowCount();
@@ -516,8 +534,8 @@ SQL;
Minz_Log::debug('Calling markReadEntries(0) is deprecated!');
}
- $sql = 'UPDATE `_entry` SET is_read = ? WHERE is_read <> ? AND id <= ?';
- $values = [$is_read ? 1 : 0, $is_read ? 1 : 0, $idMax];
+ $sql = 'UPDATE `_entry` SET is_read = ?, `lastUserModified`=? WHERE is_read <> ? AND id <= ?';
+ $values = [$is_read ? 1 : 0, time(), $is_read ? 1 : 0, $idMax];
if ($onlyFavorites) {
$sql .= ' AND is_favorite=1';
}
@@ -569,11 +587,11 @@ SQL;
$sql = <<<'SQL'
UPDATE `_entry`
-SET is_read = ?
+SET is_read = ?, `lastUserModified` = ?
WHERE is_read <> ? AND id <= ?
AND id_feed IN (SELECT f.id FROM `_feed` f WHERE f.category=? AND f.priority >= ? AND f.priority < ?)
SQL;
- $values = [$is_read ? 1 : 0, $is_read ? 1 : 0, $idMax, $id, FreshRSS_Feed::PRIORITY_CATEGORY, FreshRSS_Feed::PRIORITY_IMPORTANT];
+ $values = [$is_read ? 1 : 0, time(), $is_read ? 1 : 0, $idMax, $id, FreshRSS_Feed::PRIORITY_CATEGORY, FreshRSS_Feed::PRIORITY_IMPORTANT];
[$searchValues, $search] = $this->sqlListEntriesWhere(alias: '', state: $state, filters: $filters);
@@ -613,9 +631,9 @@ SQL;
}
$sql = 'UPDATE `_entry` '
- . 'SET is_read=? '
+ . 'SET is_read=?, `lastUserModified`=? '
. 'WHERE id_feed=? AND is_read <> ? AND id <= ?';
- $values = [$is_read ? 1 : 0, $id_feed, $is_read ? 1 : 0, $idMax];
+ $values = [$is_read ? 1 : 0, time(), $id_feed, $is_read ? 1 : 0, $idMax];
[$searchValues, $search] = $this->sqlListEntriesWhere(alias: '', state: $state, filters: $filters);
@@ -664,11 +682,11 @@ SQL;
}
$sql = 'UPDATE `_entry` e INNER JOIN `_entrytag` et ON et.id_entry = e.id '
- . 'SET e.is_read = ? '
+ . 'SET e.is_read = ?, `lastUserModified` = ? '
. 'WHERE '
. ($id == 0 ? '' : 'et.id_tag = ? AND ')
. 'e.is_read <> ? AND e.id <= ?';
- $values = [$is_read ? 1 : 0];
+ $values = [$is_read ? 1 : 0, time()];
if ($id != 0) {
$values[] = $id;
}
@@ -756,8 +774,9 @@ SQL;
/**
* @param 'ASC'|'DESC' $order
- * @return Traversable<array{id:string,guid:string,title:string,author:string,content:string,link:string,date:int,lastSeen:int,
- * hash:string,is_read:bool,is_favorite:bool,id_feed:int,tags:string,attributes:?string}>
+ * @return Traversable<array{id:string,guid:string,title:string,author:string,content:string,link:string,
+ * date:int,lastSeen:int,lastUserModified:int,
+ * hash:string,is_read:bool,is_favorite:bool,id_feed:int,tags:string,attributes:?string}>
*/
public function selectAll(string $order = 'ASC', int $limit = -1, int $offset = 0): Traversable {
$content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content';
@@ -765,14 +784,14 @@ SQL;
$order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'ASC';
$sqlLimit = static::sqlLimit($limit, $offset);
$sql = <<<SQL
-SELECT id, guid, title, author, {$content}, link, date, `lastSeen`, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes
+SELECT id, guid, title, author, {$content}, link, date, `lastSeen`, `lastUserModified`, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes
FROM `_entry`
ORDER BY id {$order} {$sqlLimit}
SQL;
$stm = $this->pdo->query($sql);
if ($stm !== false) {
while (is_array($row = $stm->fetch(PDO::FETCH_ASSOC))) {
- /** @var array{id:string,guid:string,title:string,author:string,content:string,link:string,date:int,lastSeen:int,
+ /** @var array{id:string,guid:string,title:string,author:string,content:string,link:string,date:int,lastSeen:int,lastUserModified:int,
* hash:string,is_read:bool,is_favorite:bool,id_feed:int,tags:string,attributes:?string} $row */
yield $row;
}
@@ -791,7 +810,7 @@ SQL;
$content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content';
$hash = static::sqlHexEncode('hash');
$sql = <<<SQL
-SELECT id, guid, title, author, link, date, is_read, is_favorite, {$hash} AS hash, id_feed, tags, attributes, {$content}
+SELECT id, guid, title, author, {$content}, link, date, `lastSeen`, `lastUserModified`, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes
FROM `_entry` WHERE id_feed=:id_feed AND guid=:guid
SQL;
$res = $this->fetchAssoc($sql, [':id_feed' => $id_feed, ':guid' => $guid]);
@@ -804,7 +823,7 @@ SQL;
$content = static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content';
$hash = static::sqlHexEncode('hash');
$sql = <<<SQL
-SELECT id, guid, title, author, link, date, is_read, is_favorite, {$hash} AS hash, id_feed, tags, attributes, {$content}
+SELECT id, guid, title, author, {$content}, link, date, `lastSeen`, `lastUserModified`, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes
FROM `_entry` WHERE id=:id
SQL;
$res = $this->fetchAssoc($sql, [':id' => $id]);
@@ -1210,7 +1229,7 @@ SQL;
/**
* @param numeric-string $id_min
* @param numeric-string $id_max
- * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand' $sort
+ * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified' $sort
* @param 'ASC'|'DESC' $order
* @param numeric-string $continuation_id
* @param list<string|int> $continuation_values
@@ -1278,11 +1297,12 @@ SQL;
$values[] = $id_min;
}
- if ($continuation_id !== '0' && in_array($sort, ['c.name', 'date', 'f.name', 'link', 'title'], true)) {
+ if ($continuation_id !== '0' && in_array($sort, ['c.name', 'date', 'f.name', 'link', 'title', 'lastUserModified'], true)) {
$sign = $order === 'ASC' ? '>' : '<';
$orderBy = match ($sort) {
'c.name' => 'c.name',
'f.name' => 'f.name',
+ 'lastUserModified' => $alias . '`lastUserModified`',
default => $alias . $sort,
};
// Keyset pagination (Compatibility syntax due to poor performance of tuple syntax in MySQL https://bugs.mysql.com/bug.php?id=104128)
@@ -1320,7 +1340,7 @@ SQL;
* @param int $id category/feed/tag ID
* @param numeric-string $id_min
* @param numeric-string $id_max
- * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand' $sort
+ * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified' $sort
* @param 'ASC'|'DESC' $order
* @param numeric-string $continuation_id
* @param list<string|int> $continuation_values
@@ -1379,10 +1399,11 @@ SQL;
}
$order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC';
- $sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand'], true) ? $sort : 'id';
+ $sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand', 'lastUserModified'], true) ? $sort : 'id';
$orderBy = match ($sort) {
'c.name' => 'c.name',
'f.name' => 'f.name',
+ 'lastUserModified' => 'e.`lastUserModified`',
'rand' => static::sqlRandom(),
default => 'e.' . $sort,
};
@@ -1412,7 +1433,7 @@ SQL;
* @param int $id category/feed/tag ID
* @param numeric-string $id_min
* @param numeric-string $id_max
- * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand' $sort
+ * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified' $sort
* @param 'ASC'|'DESC' $order
* @param numeric-string $continuation_id
* @param list<string|int> $continuation_values
@@ -1422,7 +1443,7 @@ SQL;
string $id_min = '0', string $id_max = '0', string $sort = 'id', string $order = 'DESC',
string $continuation_id = '0', array $continuation_values = [], int $limit = 1, int $offset = 0): PDOStatement|false {
$order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC';
- $sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand'], true) ? $sort : 'id';
+ $sort = in_array($sort, ['id', 'c.name', 'date', 'f.name', 'link', 'title', 'rand', 'lastUserModified'], true) ? $sort : 'id';
[$values, $sql] = $this->sqlListWhere($type, $id, $state, $filters, id_min: $id_min, id_max: $id_max, sort: $sort, order: $order,
continuation_id: $continuation_id, continuation_values: $continuation_values, limit: $limit, offset: $offset);
@@ -1430,15 +1451,16 @@ SQL;
$orderBy = match ($sort) {
'c.name' => 'c0.name',
'f.name' => 'f0.name',
+ 'lastUserModified' => 'e0.`lastUserModified`',
'rand' => static::sqlRandom(),
default => 'e0.' . $sort,
};
$content = static::isCompressed() ? 'UNCOMPRESS(e0.content_bin) AS content' : 'e0.content';
$hash = static::sqlHexEncode('e0.hash');
$sql = <<<SQL
-SELECT e0.id, e0.guid, e0.title, e0.author, {$content}, e0.link, e0.date, {$hash} AS hash, e0.is_read, e0.is_favorite, e0.id_feed, e0.tags, e0.attributes
-FROM `_entry` e0
-INNER JOIN ({$sql}) e2 ON e2.id=e0.id
+SELECT e0.id, e0.guid, e0.title, e0.author, {$content}, e0.link,
+ e0.date, e0.`lastSeen`, e0.`lastUserModified`, {$hash} AS hash, e0.is_read, e0.is_favorite, e0.id_feed, e0.tags, e0.attributes
+FROM `_entry` e0 INNER JOIN ({$sql}) e2 ON e2.id=e0.id
SQL;
if ($sort === 'f.name' || $sort === 'c.name') {
$sql .= ' INNER JOIN `_feed` f0 ON f0.id = e0.id_feed ';
@@ -1474,7 +1496,7 @@ SQL;
* @param int $id category/feed/tag ID
* @param numeric-string $id_min
* @param numeric-string $id_max
- * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand' $sort
+ * @param 'id'|'c.name'|'date'|'f.name'|'link'|'title'|'rand'|'lastUserModified' $sort
* @param 'ASC'|'DESC' $order
* @param numeric-string $continuation_id
* @param list<string|int> $continuation_values
@@ -1520,7 +1542,7 @@ SQL;
$hash = static::sqlHexEncode('hash');
$repeats = str_repeat('?,', count($ids) - 1) . '?';
$sql = <<<SQL
-SELECT id, guid, title, author, link, date, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes, {$content}
+SELECT id, guid, title, author, link, date, {$hash} AS hash, is_read, is_favorite, id_feed, tags, attributes, {$content}, lastUserModified
FROM `_entry`
WHERE id IN ({$repeats})
ORDER BY id {$order}