Skip to content

Commit

Permalink
Add filter actions (auto mark read) at category and global levels (#5942
Browse files Browse the repository at this point in the history
)

* Add filter actions (auto mark read) at category level
fix #3497

* Add filter actions (auto mark read) at global level
fix #2788

* Fix feed category ID

* Minor comment
  • Loading branch information
Alkarex committed Dec 15, 2023
1 parent a3ed826 commit 6bb45a8
Show file tree
Hide file tree
Showing 20 changed files with 356 additions and 378 deletions.
79 changes: 57 additions & 22 deletions app/Controllers/categoryController.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,45 +80,80 @@ public function createAction() :void {

/**
* This action updates the given category.
* @todo Check whether this function is used at all
* @see FreshRSS_subscription_Controller::categoryAction() (consider merging)
*
* Request parameters are:
* - id
* - name
*/
public function updateAction(): void {
$catDAO = FreshRSS_Factory::createCategoryDao();
$url_redirect = ['c' => 'subscription', 'a' => 'index'];
if (Minz_Request::paramBoolean('ajax')) {
$this->view->_layout(null);
}

$categoryDAO = FreshRSS_Factory::createCategoryDao();

$id = Minz_Request::paramInt('id');
$category = $categoryDAO->searchById($id);
if ($id === 0 || null === $category) {
Minz_Error::error(404);
return;
}
$this->view->category = $category;

FreshRSS_View::prependTitle($category->name() . ' · ' . _t('sub.title') . ' · ');

if (Minz_Request::isPost()) {
invalidateHttpCache();
$category->_filtersAction('read', Minz_Request::paramTextToArray('filteractions_read'));

$id = Minz_Request::paramInt('id');
$name = Minz_Request::paramString('name');
if (strlen($name) <= 0) {
Minz_Request::bad(_t('feedback.sub.category.no_name'), $url_redirect);
if (Minz_Request::paramBoolean('use_default_purge_options')) {
$category->_attributes('archiving', null);
} else {
if (!Minz_Request::paramBoolean('enable_keep_max')) {
$keepMax = false;
} elseif (($keepMax = Minz_Request::paramInt('keep_max')) !== 0) {
$keepMax = FreshRSS_Feed::ARCHIVING_RETENTION_COUNT_LIMIT;
}
if (Minz_Request::paramBoolean('enable_keep_period')) {
$keepPeriod = FreshRSS_Feed::ARCHIVING_RETENTION_PERIOD;
if (is_numeric(Minz_Request::paramString('keep_period_count')) && preg_match('/^PT?1[YMWDH]$/', Minz_Request::paramString('keep_period_unit'))) {
$keepPeriod = str_replace('1', Minz_Request::paramString('keep_period_count'), Minz_Request::paramString('keep_period_unit'));
}
} else {
$keepPeriod = false;
}
$category->_attributes('archiving', [
'keep_period' => $keepPeriod,
'keep_max' => $keepMax,
'keep_min' => Minz_Request::paramInt('keep_min'),
'keep_favourites' => Minz_Request::paramBoolean('keep_favourites'),
'keep_labels' => Minz_Request::paramBoolean('keep_labels'),
'keep_unreads' => Minz_Request::paramBoolean('keep_unreads'),
]);
}

$cat = $catDAO->searchById($id);
if ($cat === null) {
Minz_Request::bad(_t('feedback.sub.category.not_exist'), $url_redirect);
$position = Minz_Request::paramInt('position') ?: null;
$category->_attributes('position', $position);

$opml_url = checkUrl(Minz_Request::paramString('opml_url'));
if ($opml_url != '') {
$category->_kind(FreshRSS_Category::KIND_DYNAMIC_OPML);
$category->_attributes('opml_url', $opml_url);
} else {
$category->_kind(FreshRSS_Category::KIND_NORMAL);
$category->_attributes('opml_url', null);
}

$values = [
'name' => $cat->name(),
'kind' => $cat->kind(),
'attributes' => $cat->attributes(),
'kind' => $category->kind(),
'name' => Minz_Request::paramString('name'),
'attributes' => $category->attributes(),
];

if ($catDAO->updateCategory($id, $values)) {
invalidateHttpCache();

$url_redirect = ['c' => 'subscription', 'params' => ['id' => $id, 'type' => 'category']];
if (false !== $categoryDAO->updateCategory($id, $values)) {
Minz_Request::good(_t('feedback.sub.category.updated'), $url_redirect);
} else {
Minz_Request::bad(_t('feedback.sub.category.error'), $url_redirect);
}
}

Minz_Request::forward($url_redirect, true);
}

/**
Expand Down
1 change: 1 addition & 0 deletions app/Controllers/configureController.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ public function readingAction(): void {
'site' => Minz_Request::paramBoolean('mark_open_site'),
'focus' => Minz_Request::paramBoolean('mark_focus'),
];
FreshRSS_Context::$user_conf->_filtersAction('read', Minz_Request::paramTextToArray('filteractions_read'));
FreshRSS_Context::$user_conf->save();
invalidateHttpCache();

Expand Down
13 changes: 13 additions & 0 deletions app/Controllers/feedController.php
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,19 @@ public static function actualizeFeeds(?int $feed_id = null, ?string $feed_url =
}
} else {
$feeds = $feedDAO->listFeedsOrderUpdate(-1);

// Hydrate category for each feed to avoid that each feed has to make an SQL request
$categories = [];
$catDAO = FreshRSS_Factory::createCategoryDao();
foreach ($catDAO->listCategories(false, false) as $category) {
$categories[$category->id()] = $category;
}
foreach ($feeds as $feed) {
$category = $categories[$feed->categoryId()] ?? null;
if ($category !== null) {
$feed->_category($category);
}
}
}

// WebSub (PubSubHubbub) support
Expand Down
75 changes: 1 addition & 74 deletions app/Controllers/subscriptionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ public function feedAction(): void {
]);
}

$feed->_filtersAction('read', preg_split('/[\n\r]+/', Minz_Request::paramString('filteractions_read')) ?: []);
$feed->_filtersAction('read', Minz_Request::paramTextToArray('filteractions_read'));

$feed->_kind(Minz_Request::paramInt('feed_kind') ?: FreshRSS_Feed::KIND_RSS);
if ($feed->kind() === FreshRSS_Feed::KIND_HTML_XPATH || $feed->kind() === FreshRSS_Feed::KIND_XML_XPATH) {
Expand Down Expand Up @@ -279,79 +279,6 @@ public function feedAction(): void {
}
}

public function categoryAction(): void {
if (Minz_Request::paramBoolean('ajax')) {
$this->view->_layout(null);
}

$categoryDAO = FreshRSS_Factory::createCategoryDao();

$id = Minz_Request::paramInt('id');
$category = $categoryDAO->searchById($id);
if ($id === 0 || null === $category) {
Minz_Error::error(404);
return;
}
$this->view->category = $category;

FreshRSS_View::prependTitle($category->name() . ' · ' . _t('sub.title') . ' · ');

if (Minz_Request::isPost()) {
if (Minz_Request::paramBoolean('use_default_purge_options')) {
$category->_attributes('archiving', null);
} else {
if (!Minz_Request::paramBoolean('enable_keep_max')) {
$keepMax = false;
} elseif (($keepMax = Minz_Request::paramInt('keep_max')) !== 0) {
$keepMax = FreshRSS_Feed::ARCHIVING_RETENTION_COUNT_LIMIT;
}
if (Minz_Request::paramBoolean('enable_keep_period')) {
$keepPeriod = FreshRSS_Feed::ARCHIVING_RETENTION_PERIOD;
if (is_numeric(Minz_Request::paramString('keep_period_count')) && preg_match('/^PT?1[YMWDH]$/', Minz_Request::paramString('keep_period_unit'))) {
$keepPeriod = str_replace('1', Minz_Request::paramString('keep_period_count'), Minz_Request::paramString('keep_period_unit'));
}
} else {
$keepPeriod = false;
}
$category->_attributes('archiving', [
'keep_period' => $keepPeriod,
'keep_max' => $keepMax,
'keep_min' => Minz_Request::paramInt('keep_min'),
'keep_favourites' => Minz_Request::paramBoolean('keep_favourites'),
'keep_labels' => Minz_Request::paramBoolean('keep_labels'),
'keep_unreads' => Minz_Request::paramBoolean('keep_unreads'),
]);
}

$position = Minz_Request::paramInt('position') ?: null;
$category->_attributes('position', $position);

$opml_url = checkUrl(Minz_Request::paramString('opml_url'));
if ($opml_url != '') {
$category->_kind(FreshRSS_Category::KIND_DYNAMIC_OPML);
$category->_attributes('opml_url', $opml_url);
} else {
$category->_kind(FreshRSS_Category::KIND_NORMAL);
$category->_attributes('opml_url', null);
}

$values = [
'kind' => $category->kind(),
'name' => Minz_Request::paramString('name'),
'attributes' => $category->attributes(),
];

invalidateHttpCache();

$url_redirect = ['c' => 'subscription', 'params' => ['id' => $id, 'type' => 'category']];
if (false !== $categoryDAO->updateCategory($id, $values)) {
Minz_Request::good(_t('feedback.sub.category.updated'), $url_redirect);
} else {
Minz_Request::bad(_t('feedback.sub.category.error'), $url_redirect);
}
}
}

/**
* This action displays the bookmarklet page.
*/
Expand Down
40 changes: 40 additions & 0 deletions app/Models/AttributesTrait.php
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
35 changes: 4 additions & 31 deletions app/Models/Category.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
declare(strict_types=1);

class FreshRSS_Category extends Minz_Model {
use FreshRSS_AttributesTrait, FreshRSS_FilterActionsTrait;

/**
* Normal
Expand All @@ -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();
Expand Down Expand Up @@ -120,18 +121,6 @@ public function hasFeedsWithError(): bool {
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) {
Expand Down Expand Up @@ -169,22 +158,6 @@ public function addFeed(FreshRSS_Feed $feed): void {
$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
Expand Down
11 changes: 5 additions & 6 deletions app/Models/CategoryDAO.php
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,7 @@ public function checkDefault() {
$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') {
Expand Down Expand Up @@ -441,9 +440,9 @@ private static function daoToCategoryPrepopulated(array $listDAO) {
// 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;
Expand All @@ -459,9 +458,9 @@ private static function daoToCategoryPrepopulated(array $listDAO) {
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);
Expand All @@ -482,9 +481,9 @@ private static function daoToCategory(array $listDAO): array {
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);
Expand Down

0 comments on commit 6bb45a8

Please sign in to comment.