Files
FreshRSS/app/Models/FilterActionsTrait.php

155 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
/**
* Logic to apply filter actions (for feeds, categories, user configuration...).
*/
trait FreshRSS_FilterActionsTrait {
/** @var list<FreshRSS_FilterAction>|null $filterActions */
private ?array $filterActions = null;
/**
* @return list<FreshRSS_FilterAction>
*/
private function filterActions(): array {
if (empty($this->filterActions)) {
$this->filterActions = [];
$filters = $this->attributeArray('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 = is_array($filterActions) ? array_values($filterActions) : null;
if ($this->filterActions !== null && !empty($this->filterActions)) {
$this->_attribute('filters', array_map(
static fn(?FreshRSS_FilterAction $af) => $af == null ? null : $af->toJSON(),
$this->filterActions));
} else {
$this->_attribute('filters', null);
}
}
/** @return list<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 (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_values(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()->toString() === '') {
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()->toString()) {
$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);
}
/**
* @param bool $applyLabel Parameter by reference, which will be set to true if the callers needs to apply a label to the article entry.
* @param-out bool $applyLabel
*/
public function applyFilterActions(FreshRSS_Entry $entry, ?bool &$applyLabel = null): void {
$applyLabel = false;
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(Minz_HookType::EntryAutoRead, $entry, 'filter');
}
break;
case 'star':
if (!$entry->isUpdated()) {
// Do not apply to updated articles, to avoid overruling a user manual action
$entry->_isFavorite(true);
}
break;
case 'label':
if (!$entry->isUpdated()) {
// Do not apply to updated articles, to avoid overruling a user manual action
$applyLabel = true;
}
break;
}
}
}
}
}
}