aboutsummaryrefslogtreecommitdiff
path: root/app/Models
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2023-12-15 23:04:29 +0100
committerGravatar GitHub <noreply@github.com> 2023-12-15 23:04:29 +0100
commit6bb45a87268157aab961a6a4a728d9a9bbe043b0 (patch)
treed1c36638d5ee61e2e663d214d724a71f07a89354 /app/Models
parenta3ed8269132303eebc03d3e6df822f1f101fa95b (diff)
Add filter actions (auto mark read) at category and global levels (#5942)
* Add filter actions (auto mark read) at category level fix https://github.com/FreshRSS/FreshRSS/issues/3497 * Add filter actions (auto mark read) at global level fix https://github.com/FreshRSS/FreshRSS/issues/2788 * Fix feed category ID * Minor comment
Diffstat (limited to 'app/Models')
-rw-r--r--app/Models/AttributesTrait.php40
-rw-r--r--app/Models/Category.php35
-rw-r--r--app/Models/CategoryDAO.php11
-rw-r--r--app/Models/Entry.php80
-rw-r--r--app/Models/Feed.php153
-rw-r--r--app/Models/FeedDAO.php2
-rw-r--r--app/Models/FilterActionsTrait.php147
-rw-r--r--app/Models/Tag.php33
-rw-r--r--app/Models/UserConfiguration.php21
9 files changed, 244 insertions, 278 deletions
diff --git a/app/Models/AttributesTrait.php b/app/Models/AttributesTrait.php
new file mode 100644
index 000000000..39154182b
--- /dev/null
+++ b/app/Models/AttributesTrait.php
@@ -0,0 +1,40 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * Logic to work with (JSON) attributes (for entries, feeds, categories, tags...).
+ */
+trait FreshRSS_AttributesTrait {
+ /**
+ * @var array<string,mixed>
+ */
+ private array $attributes = [];
+
+ /**
+ * @phpstan-return ($key is non-empty-string ? mixed : array<string,mixed>)
+ * @return array<string,mixed>|mixed|null
+ */
+ public function attributes(string $key = '') {
+ if ($key === '') {
+ return $this->attributes;
+ } else {
+ return $this->attributes[$key] ?? null;
+ }
+ }
+
+ /** @param string|array<mixed>|bool|int|null $value Value, not HTML-encoded */
+ public function _attributes(string $key, $value = null): void {
+ if ($key == '') {
+ if (is_string($value)) {
+ $value = json_decode($value, true);
+ }
+ if (is_array($value)) {
+ $this->attributes = $value;
+ }
+ } elseif ($value === null) {
+ unset($this->attributes[$key]);
+ } else {
+ $this->attributes[$key] = $value;
+ }
+ }
+}
diff --git a/app/Models/Category.php b/app/Models/Category.php
index ab08a5b74..b1e35650a 100644
--- a/app/Models/Category.php
+++ b/app/Models/Category.php
@@ -2,6 +2,7 @@
declare(strict_types=1);
class FreshRSS_Category extends Minz_Model {
+ use FreshRSS_AttributesTrait, FreshRSS_FilterActionsTrait;
/**
* Normal
@@ -22,21 +23,21 @@ class FreshRSS_Category extends Minz_Model {
private ?array $feeds = null;
/** @var bool|int */
private $hasFeedsWithError = false;
- /** @var array<string,mixed> */
- private array $attributes = [];
private int $lastUpdate = 0;
private bool $error = false;
/**
* @param array<FreshRSS_Feed>|null $feeds
*/
- public function __construct(string $name = '', ?array $feeds = null) {
+ public function __construct(string $name = '', int $id = 0, ?array $feeds = null) {
+ $this->_id($id);
$this->_name($name);
if ($feeds !== null) {
$this->_feeds($feeds);
$this->nbFeeds = 0;
$this->nbNotRead = 0;
foreach ($feeds as $feed) {
+ $feed->_category($this);
$this->nbFeeds++;
$this->nbNotRead += $feed->nbNotRead();
$this->hasFeedsWithError |= $feed->inError();
@@ -120,18 +121,6 @@ class FreshRSS_Category extends Minz_Model {
return (bool)($this->hasFeedsWithError);
}
- /**
- * @phpstan-return ($key is non-empty-string ? mixed : array<string,mixed>)
- * @return array<string,mixed>|mixed|null
- */
- public function attributes(string $key = '') {
- if ($key === '') {
- return $this->attributes;
- } else {
- return $this->attributes[$key] ?? null;
- }
- }
-
public function _id(int $id): void {
$this->id = $id;
if ($id === FreshRSS_CategoryDAO::DEFAULTCATEGORYID) {
@@ -169,22 +158,6 @@ class FreshRSS_Category extends Minz_Model {
$this->sortFeeds();
}
- /** @param string|array<mixed>|bool|int|null $value Value, not HTML-encoded */
- public function _attributes(string $key, $value): void {
- if ('' === $key) {
- if (is_string($value)) {
- $value = json_decode($value, true);
- }
- if (is_array($value)) {
- $this->attributes = $value;
- }
- } elseif (null === $value) {
- unset($this->attributes[$key]);
- } else {
- $this->attributes[$key] = $value;
- }
- }
-
/**
* @param array<string> $attributes
* @throws FreshRSS_Context_Exception
diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php
index 2477d0ea2..20347e4f2 100644
--- a/app/Models/CategoryDAO.php
+++ b/app/Models/CategoryDAO.php
@@ -351,8 +351,7 @@ SQL;
$def_cat = $this->searchById(self::DEFAULTCATEGORYID);
if ($def_cat == null) {
- $cat = new FreshRSS_Category(_t('gen.short.default_category'));
- $cat->_id(self::DEFAULTCATEGORYID);
+ $cat = new FreshRSS_Category(_t('gen.short.default_category'), self::DEFAULTCATEGORYID);
$sql = 'INSERT INTO `_category`(id, name) VALUES(?, ?)';
if ($this->pdo->dbType() === 'pgsql') {
@@ -441,9 +440,9 @@ SQL;
// End of the current category, we add it to the $list
$cat = new FreshRSS_Category(
$previousLine['c_name'],
+ $previousLine['c_id'],
$feedDao::daoToFeed($feedsDao, $previousLine['c_id'])
);
- $cat->_id($previousLine['c_id']);
$cat->_kind($previousLine['c_kind']);
$cat->_attributes('', $previousLine['c_attributes'] ?? '[]');
$list[(int)$previousLine['c_id']] = $cat;
@@ -459,9 +458,9 @@ SQL;
if ($previousLine != null) {
$cat = new FreshRSS_Category(
$previousLine['c_name'],
+ $previousLine['c_id'],
$feedDao::daoToFeed($feedsDao, $previousLine['c_id'])
);
- $cat->_id($previousLine['c_id']);
$cat->_kind($previousLine['c_kind']);
$cat->_lastUpdate($previousLine['c_last_update'] ?? 0);
$cat->_error($previousLine['c_error'] ?? 0);
@@ -482,9 +481,9 @@ SQL;
foreach ($listDAO as $dao) {
FreshRSS_DatabaseDAO::pdoInt($dao, ['id', 'kind', 'lastUpdate', 'error']);
$cat = new FreshRSS_Category(
- $dao['name']
+ $dao['name'],
+ $dao['id']
);
- $cat->_id($dao['id']);
$cat->_kind($dao['kind']);
$cat->_lastUpdate($dao['lastUpdate'] ?? 0);
$cat->_error($dao['error'] ?? 0);
diff --git a/app/Models/Entry.php b/app/Models/Entry.php
index 476c5a8cf..186b1f166 100644
--- a/app/Models/Entry.php
+++ b/app/Models/Entry.php
@@ -2,6 +2,8 @@
declare(strict_types=1);
class FreshRSS_Entry extends Minz_Model {
+ use FreshRSS_AttributesTrait;
+
public const STATE_READ = 1;
public const STATE_NOT_READ = 2;
public const STATE_ALL = 3;
@@ -26,8 +28,6 @@ class FreshRSS_Entry extends Minz_Model {
private ?FreshRSS_Feed $feed;
/** @var array<string> */
private array $tags = [];
- /** @var array<string,mixed> */
- private array $attributes = [];
/**
* @param int|string $pubdate
@@ -396,34 +396,6 @@ HTML;
}
}
- /**
- * @phpstan-return ($key is non-empty-string ? mixed : array<string,mixed>)
- * @return array<string,mixed>|mixed|null
- */
- public function attributes(string $key = '') {
- if ($key === '') {
- return $this->attributes;
- } else {
- return $this->attributes[$key] ?? null;
- }
- }
-
- /** @param string|array<mixed>|bool|int|null $value Value, not HTML-encoded */
- public function _attributes(string $key, $value): void {
- if ($key == '') {
- if (is_string($value)) {
- $value = json_decode($value, true);
- }
- if (is_array($value)) {
- $this->attributes = $value;
- }
- } elseif ($value === null) {
- unset($this->attributes[$key]);
- } else {
- $this->attributes[$key] = $value;
- }
- }
-
public function hash(): string {
if ($this->hash == '') {
//Do not include $this->date because it may be automatically generated when lacking
@@ -660,40 +632,26 @@ HTML;
/** @param array<string,bool> $titlesAsRead */
public function applyFilterActions(array $titlesAsRead = []): void {
- if ($this->feed != null) {
- if (!$this->isRead()) {
- if ($this->feed->attributes('read_upon_reception') ||
- ($this->feed->attributes('read_upon_reception') === null && FreshRSS_Context::$user_conf->mark_when['reception'])) {
- $this->_isRead(true);
- Minz_ExtensionManager::callHook('entry_auto_read', $this, 'upon_reception');
- }
- if (!empty($titlesAsRead[$this->title()])) {
- Minz_Log::debug('Mark title as read: ' . $this->title());
- $this->_isRead(true);
- Minz_ExtensionManager::callHook('entry_auto_read', $this, 'same_title_in_feed');
- }
+ if ($this->feed === null) {
+ return;
+ }
+ if (!$this->isRead()) {
+ if ($this->feed->attributes('read_upon_reception') ||
+ ($this->feed->attributes('read_upon_reception') === null && FreshRSS_Context::$user_conf->mark_when['reception'])) {
+ $this->_isRead(true);
+ Minz_ExtensionManager::callHook('entry_auto_read', $this, 'upon_reception');
}
- foreach ($this->feed->filterActions() as $filterAction) {
- if ($this->matches($filterAction->booleanSearch())) {
- foreach ($filterAction->actions() as $action) {
- switch ($action) {
- case 'read':
- if (!$this->isRead()) {
- $this->_isRead(true);
- Minz_ExtensionManager::callHook('entry_auto_read', $this, 'filter');
- }
- break;
- case 'star':
- $this->_isFavorite(true);
- break;
- case 'label':
- //TODO: Implement more actions
- break;
- }
- }
- }
+ if (!empty($titlesAsRead[$this->title()])) {
+ Minz_Log::debug('Mark title as read: ' . $this->title());
+ $this->_isRead(true);
+ Minz_ExtensionManager::callHook('entry_auto_read', $this, 'same_title_in_feed');
}
}
+ FreshRSS_Context::$user_conf->applyFilterActions($this);
+ if ($this->feed->category() !== null) {
+ $this->feed->category()->applyFilterActions($this);
+ }
+ $this->feed->applyFilterActions($this);
}
public function isDay(int $day, int $today): bool {
diff --git a/app/Models/Feed.php b/app/Models/Feed.php
index a379f5016..dbe6aaa73 100644
--- a/app/Models/Feed.php
+++ b/app/Models/Feed.php
@@ -2,6 +2,7 @@
declare(strict_types=1);
class FreshRSS_Feed extends Minz_Model {
+ use FreshRSS_AttributesTrait, FreshRSS_FilterActionsTrait;
/**
* Normal RSS or Atom feed
@@ -42,7 +43,7 @@ class FreshRSS_Feed extends Minz_Model {
private int $id = 0;
private string $url = '';
private int $kind = 0;
- private int $categoryId = 1;
+ private int $categoryId = 0;
private ?FreshRSS_Category $category;
private int $nbEntries = -1;
private int $nbNotRead = -1;
@@ -55,15 +56,11 @@ class FreshRSS_Feed extends Minz_Model {
private string $httpAuth = '';
private bool $error = false;
private int $ttl = self::TTL_DEFAULT;
- /** @var array<string,mixed> */
- private array $attributes = [];
private bool $mute = false;
private string $hash = '';
private string $lockPath = '';
private string $hubUrl = '';
private string $selfUrl = '';
- /** @var array<FreshRSS_FilterAction>|null $filterActions */
- private ?array $filterActions = null;
public function __construct(string $url, bool $validate = true) {
if ($validate) {
@@ -105,7 +102,7 @@ class FreshRSS_Feed extends Minz_Model {
}
public function category(): ?FreshRSS_Category {
- if ($this->category === null) {
+ if ($this->category === null && $this->categoryId > 0) {
$catDAO = FreshRSS_Factory::createCategoryDao();
$this->category = $catDAO->searchById($this->categoryId);
}
@@ -113,6 +110,9 @@ class FreshRSS_Feed extends Minz_Model {
}
public function categoryId(): int {
+ if ($this->category !== null) {
+ return $this->category->id() ?: $this->categoryId;
+ }
return $this->categoryId;
}
@@ -186,18 +186,6 @@ class FreshRSS_Feed extends Minz_Model {
return $this->ttl;
}
- /**
- * @phpstan-return ($key is non-empty-string ? mixed : array<string,mixed>)
- * @return array<string,mixed>|mixed|null
- */
- public function attributes(string $key = '') {
- if ($key === '') {
- return $this->attributes;
- } else {
- return $this->attributes[$key] ?? null;
- }
- }
-
public function mute(): bool {
return $this->mute;
}
@@ -325,22 +313,6 @@ class FreshRSS_Feed extends Minz_Model {
$this->mute = $value < self::TTL_DEFAULT;
}
- /** @param string|array<mixed>|bool|int|null $value Value, not HTML-encoded */
- public function _attributes(string $key, $value): void {
- if ($key == '') {
- if (is_string($value)) {
- $value = json_decode($value, true);
- }
- if (is_array($value)) {
- $this->attributes = $value;
- }
- } elseif ($value === null) {
- unset($this->attributes[$key]);
- } else {
- $this->attributes[$key] = $value;
- }
- }
-
public function _nbNotRead(int $value): void {
$this->nbNotRead = $value;
}
@@ -871,119 +843,6 @@ class FreshRSS_Feed extends Minz_Model {
return @unlink($this->lockPath);
}
- /**
- * @return array<FreshRSS_FilterAction>
- */
- public function filterActions(): array {
- if (empty($this->filterActions)) {
- $this->filterActions = [];
- $filters = $this->attributes('filters');
- if (is_array($filters)) {
- foreach ($filters as $filter) {
- $filterAction = FreshRSS_FilterAction::fromJSON($filter);
- if ($filterAction != null) {
- $this->filterActions[] = $filterAction;
- }
- }
- }
- }
- return $this->filterActions;
- }
-
- /**
- * @param array<FreshRSS_FilterAction>|null $filterActions
- */
- private function _filterActions(?array $filterActions): void {
- $this->filterActions = $filterActions;
- if (is_array($this->filterActions) && !empty($this->filterActions)) {
- $this->_attributes('filters', array_map(static function (?FreshRSS_FilterAction $af) {
- return $af == null ? null : $af->toJSON();
- }, $this->filterActions));
- } else {
- $this->_attributes('filters', null);
- }
- }
-
- /** @return array<FreshRSS_BooleanSearch> */
- public function filtersAction(string $action): array {
- $action = trim($action);
- if ($action == '') {
- return [];
- }
- $filters = [];
- $filterActions = $this->filterActions();
- for ($i = count($filterActions) - 1; $i >= 0; $i--) {
- $filterAction = $filterActions[$i];
- if ($filterAction != null && $filterAction->booleanSearch() != null &&
- $filterAction->actions() != null && in_array($action, $filterAction->actions(), true)) {
- $filters[] = $filterAction->booleanSearch();
- }
- }
- return $filters;
- }
-
- /**
- * @param array<string> $filters
- */
- public function _filtersAction(string $action, array $filters): void {
- $action = trim($action);
- if ($action == '') {
- return;
- }
- $filters = array_unique(array_map('trim', $filters));
- $filterActions = $this->filterActions();
-
- //Check existing filters
- for ($i = count($filterActions) - 1; $i >= 0; $i--) {
- $filterAction = $filterActions[$i];
- if ($filterAction == null || !is_array($filterAction->actions()) ||
- $filterAction->booleanSearch() == null || trim($filterAction->booleanSearch()->getRawInput()) == '') {
- array_splice($filterActions, $i, 1);
- continue;
- }
- $actions = $filterAction->actions();
- //Remove existing rules with same action
- for ($j = count($actions) - 1; $j >= 0; $j--) {
- if ($actions[$j] === $action) {
- array_splice($actions, $j, 1);
- }
- }
- //Update existing filter with new action
- for ($k = count($filters) - 1; $k >= 0; $k --) {
- $filter = $filters[$k];
- if ($filter === $filterAction->booleanSearch()->getRawInput()) {
- $actions[] = $action;
- array_splice($filters, $k, 1);
- }
- }
- //Save result
- if (empty($actions)) {
- array_splice($filterActions, $i, 1);
- } else {
- $filterAction->_actions($actions);
- }
- }
-
- //Add new filters
- for ($k = count($filters) - 1; $k >= 0; $k --) {
- $filter = $filters[$k];
- if ($filter != '') {
- $filterAction = FreshRSS_FilterAction::fromJSON([
- 'search' => $filter,
- 'actions' => [$action],
- ]);
- if ($filterAction != null) {
- $filterActions[] = $filterAction;
- }
- }
- }
-
- if (empty($filterActions)) {
- $filterActions = null;
- }
- $this->_filterActions($filterActions);
- }
-
//<WebSub>
public function pubSubHubbubEnabled(): bool {
diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php
index 055ec60e4..ac844217a 100644
--- a/app/Models/FeedDAO.php
+++ b/app/Models/FeedDAO.php
@@ -373,7 +373,7 @@ SQL;
* @return array<FreshRSS_Feed>
*/
public function listFeedsOrderUpdate(int $defaultCacheDuration = 3600, int $limit = 0): array {
- $sql = 'SELECT id, url, kind, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, ttl, attributes, `cache_nbEntries`, `cache_nbUnreads` '
+ $sql = 'SELECT id, url, kind, category, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, ttl, attributes, `cache_nbEntries`, `cache_nbUnreads` '
. 'FROM `_feed` '
. ($defaultCacheDuration < 0 ? '' : 'WHERE ttl >= ' . FreshRSS_Feed::TTL_DEFAULT
. ' AND `lastUpdate` < (' . (time() + 60)
diff --git a/app/Models/FilterActionsTrait.php b/app/Models/FilterActionsTrait.php
new file mode 100644
index 000000000..869992b21
--- /dev/null
+++ b/app/Models/FilterActionsTrait.php
@@ -0,0 +1,147 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * Logic to apply filter actions (for feeds, categories, user configuration...).
+ */
+trait FreshRSS_FilterActionsTrait {
+
+ /** @var array<FreshRSS_FilterAction>|null $filterActions */
+ private ?array $filterActions = null;
+
+ /**
+ * @return array<FreshRSS_FilterAction>
+ */
+ private function filterActions(): array {
+ if (empty($this->filterActions)) {
+ $this->filterActions = [];
+ $filters = $this->attributes('filters');
+ if (is_array($filters)) {
+ foreach ($filters as $filter) {
+ $filterAction = FreshRSS_FilterAction::fromJSON($filter);
+ if ($filterAction != null) {
+ $this->filterActions[] = $filterAction;
+ }
+ }
+ }
+ }
+ return $this->filterActions;
+ }
+
+ /**
+ * @param array<FreshRSS_FilterAction>|null $filterActions
+ */
+ private function _filterActions(?array $filterActions): void {
+ $this->filterActions = $filterActions;
+ if (is_array($this->filterActions) && !empty($this->filterActions)) {
+ $this->_attributes('filters', array_map(static function (?FreshRSS_FilterAction $af) {
+ return $af == null ? null : $af->toJSON();
+ }, $this->filterActions));
+ } else {
+ $this->_attributes('filters', null);
+ }
+ }
+
+ /** @return array<FreshRSS_BooleanSearch> */
+ public function filtersAction(string $action): array {
+ $action = trim($action);
+ if ($action == '') {
+ return [];
+ }
+ $filters = [];
+ $filterActions = $this->filterActions();
+ for ($i = count($filterActions) - 1; $i >= 0; $i--) {
+ $filterAction = $filterActions[$i];
+ if ($filterAction != null && $filterAction->booleanSearch() != null &&
+ $filterAction->actions() != null && in_array($action, $filterAction->actions(), true)) {
+ $filters[] = $filterAction->booleanSearch();
+ }
+ }
+ return $filters;
+ }
+
+ /**
+ * @param array<string> $filters
+ */
+ public function _filtersAction(string $action, array $filters): void {
+ $action = trim($action);
+ if ($action === '') {
+ return;
+ }
+ $filters = array_unique(array_map('trim', $filters), SORT_STRING);
+ $filterActions = $this->filterActions();
+
+ //Check existing filters
+ for ($i = count($filterActions) - 1; $i >= 0; $i--) {
+ $filterAction = $filterActions[$i];
+ if ($filterAction == null || !is_array($filterAction->actions()) ||
+ $filterAction->booleanSearch() == null || trim($filterAction->booleanSearch()->getRawInput()) == '') {
+ array_splice($filterActions, $i, 1);
+ continue;
+ }
+ $actions = $filterAction->actions();
+ //Remove existing rules with same action
+ for ($j = count($actions) - 1; $j >= 0; $j--) {
+ if ($actions[$j] === $action) {
+ array_splice($actions, $j, 1);
+ }
+ }
+ //Update existing filter with new action
+ for ($k = count($filters) - 1; $k >= 0; $k --) {
+ $filter = $filters[$k];
+ if ($filter === $filterAction->booleanSearch()->getRawInput()) {
+ $actions[] = $action;
+ array_splice($filters, $k, 1);
+ }
+ }
+ //Save result
+ if (empty($actions)) {
+ array_splice($filterActions, $i, 1);
+ } else {
+ $filterAction->_actions($actions);
+ }
+ }
+
+ //Add new filters
+ for ($k = count($filters) - 1; $k >= 0; $k --) {
+ $filter = $filters[$k];
+ if ($filter != '') {
+ $filterAction = FreshRSS_FilterAction::fromJSON([
+ 'search' => $filter,
+ 'actions' => [$action],
+ ]);
+ if ($filterAction != null) {
+ $filterActions[] = $filterAction;
+ }
+ }
+ }
+
+ if (empty($filterActions)) {
+ $filterActions = null;
+ }
+ $this->_filterActions($filterActions);
+ }
+
+ public function applyFilterActions(FreshRSS_Entry $entry): void {
+ foreach ($this->filterActions() as $filterAction) {
+ if ($entry->matches($filterAction->booleanSearch())) {
+ foreach ($filterAction->actions() as $action) {
+ switch ($action) {
+ case 'read':
+ if (!$entry->isRead()) {
+ $entry->_isRead(true);
+ Minz_ExtensionManager::callHook('entry_auto_read', $entry, 'filter');
+ }
+ break;
+ case 'star':
+ $entry->_isFavorite(true);
+ break;
+ case 'label':
+ //TODO: Implement more actions
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/Models/Tag.php b/app/Models/Tag.php
index 0e50763d0..cedc4647d 100644
--- a/app/Models/Tag.php
+++ b/app/Models/Tag.php
@@ -2,13 +2,10 @@
declare(strict_types=1);
class FreshRSS_Tag extends Minz_Model {
+ use FreshRSS_AttributesTrait;
private int $id = 0;
private string $name;
- /**
- * @var array<string,mixed>
- */
- private array $attributes = [];
private int $nbEntries = -1;
private int $nbUnread = -1;
@@ -35,34 +32,6 @@ class FreshRSS_Tag extends Minz_Model {
$this->name = trim($value);
}
- /**
- * @phpstan-return ($key is non-empty-string ? mixed : array<string,mixed>)
- * @return array<string,mixed>|mixed|null
- */
- public function attributes(string $key = '') {
- if ($key === '') {
- return $this->attributes;
- } else {
- return $this->attributes[$key] ?? null;
- }
- }
-
- /** @param string|array<mixed>|bool|int|null $value Value, not HTML-encoded */
- public function _attributes(string $key, $value = null): void {
- if ($key == '') {
- if (is_string($value)) {
- $value = json_decode($value, true);
- }
- if (is_array($value)) {
- $this->attributes = $value;
- }
- } elseif ($value === null) {
- unset($this->attributes[$key]);
- } else {
- $this->attributes[$key] = $value;
- }
- }
-
public function nbEntries(): int {
if ($this->nbEntries < 0) {
$tagDAO = FreshRSS_Factory::createTagDao();
diff --git a/app/Models/UserConfiguration.php b/app/Models/UserConfiguration.php
index d75c76bcb..0aec3a05f 100644
--- a/app/Models/UserConfiguration.php
+++ b/app/Models/UserConfiguration.php
@@ -72,10 +72,31 @@ declare(strict_types=1);
* @property array<string,mixed> $volatile
*/
final class FreshRSS_UserConfiguration extends Minz_Configuration {
+ use FreshRSS_FilterActionsTrait;
/** @throws Minz_ConfigurationNamespaceException */
public static function init(string $config_filename, ?string $default_filename = null): FreshRSS_UserConfiguration {
parent::register('user', $config_filename, $default_filename);
return parent::get('user');
}
+
+ /**
+ * @phpstan-return ($key is non-empty-string ? mixed : array<string,mixed>)
+ * @return array<string,mixed>|mixed|null
+ */
+ public function attributes(string $key = '') {
+ if ($key === '') {
+ return []; // Not implemented for user configuration
+ } else {
+ return parent::param($key, null);
+ }
+ }
+
+ /** @param string|array<mixed>|bool|int|null $value Value, not HTML-encoded */
+ public function _attributes(string $key, $value = null): void {
+ if ($key == '') {
+ return; // Not implemented for user configuration
+ }
+ parent::_param($key, $value);
+ }
}