Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/Controller/PageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public function index(): TemplateResponse
'oldestFirst',
'showAll',
'disableRefresh',
'titleFilterRegex',
'displaymode',
'splitmode',
'starredOpenState'
Expand Down
30 changes: 30 additions & 0 deletions lib/Service/FeedServiceV2.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\IAppConfig;
use OCP\Config\IUserConfig;

use OCA\News\Db\Feed;
use OCA\News\Db\Item;
Expand Down Expand Up @@ -84,6 +85,7 @@ class FeedServiceV2 extends Service
* @param HtmlSanitizer $purifier HTML Sanitizer
* @param LoggerInterface $logger Logger
* @param IAppConfig $config App config
* @param IUserConfig $userConfig User config
*/
public function __construct(
FeedMapperV2 $mapper,
Expand All @@ -93,6 +95,7 @@ public function __construct(
HtmlSanitizer $purifier,
LoggerInterface $logger,
IAppConfig $config,
IUserConfig $userConfig,
AppData $appData
) {
parent::__construct($mapper, $logger);
Expand All @@ -102,6 +105,7 @@ public function __construct(
$this->explorer = $explorer;
$this->purifier = $purifier;
$this->config = $config;
$this->userConfig = $userConfig;
$this->appData = $appData;
}

Expand Down Expand Up @@ -282,6 +286,25 @@ public function create(
return $this->mapper->insert($feed);
}

/**
* Get regex pattern for filtering article titles
*
* @param String $userId UserId for configuration access
*
* @return valid regex pattern or empty string if invalid
*/
private function getTitleFilterRegex(String $userId): String
{
$pattern = $this->userConfig->getValueString($userId, 'news', 'titleFilterRegex');
if (empty($pattern)) {
return '';
}
if (@preg_match($pattern, '') === false) {
$this->logger->warning('Pattern in titleFilterRegex is not valid: {pattern}', [ 'pattern' => $pattern ]);
return '';
}
return $pattern;
}

/**
* Update a feed
Expand Down Expand Up @@ -384,7 +407,14 @@ public function fetch(Entity $feed): Entity
$feed->setFaviconLink($fetchedFavicon);
}

$filterBy = $this->getTitleFilterRegex($feed->getUserId());
foreach (array_reverse($items) as &$item) {
if ($item->getTitle() !== null && !empty($filterBy) && preg_match($filterBy, $item->getTitle())) {
$this->logger->info('Item filtered: matched by = {filterBy} title = {title}', [ 'title' => $item->getTitle(), 'filterBy' => $filterBy ]);
continue;
}


$item->setFeedId($feed->getId())
->setBody($this->purifier->purify($item->getBody()));

Expand Down
16 changes: 16 additions & 0 deletions src/components/modals/AppSettingsDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
v-model="disableRefresh"
:label="t('news', 'Disable automatic refresh')" />
</NcFormBox>
<NcTextField
v-model="titleFilterRegex"
:label="t('news', 'Drop new articles with title matching regex')"
:placeholder="t('news', '/spam|ads/')" />
</NcAppSettingsSection>

<NcAppSettingsSection id="settings-display" :name="t('news', 'Appearance')">
Expand Down Expand Up @@ -218,6 +222,7 @@ import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
import NcRadioGroup from '@nextcloud/vue/components/NcRadioGroup'
import NcRadioGroupButton from '@nextcloud/vue/components/NcRadioGroupButton'
import NcTextField from '@nextcloud/vue/components/NcTextField'
import DownloadIcon from 'vue-material-design-icons/Download.vue'
import UploadIcon from 'vue-material-design-icons/Upload.vue'
import { DISPLAY_MODE, SPLIT_MODE } from '../../enums/index.ts'
Expand All @@ -240,6 +245,7 @@ export default defineComponent({
NcNoteCard,
NcRadioGroup,
NcRadioGroupButton,
NcTextField,
DownloadIcon,
UploadIcon,
},
Expand Down Expand Up @@ -353,6 +359,16 @@ export default defineComponent({
},
},
titleFilterRegex: {
get() {
return this.$store.getters.titleFilterRegex
},
set(newValue) {
this.saveSetting('titleFilterRegex', newValue)
},
},
uploadOpmlStatusMessage() {
return this.$store.getters.lastOpmlImportMessage?.message
},
Expand Down
11 changes: 11 additions & 0 deletions src/store/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type AppInfoState = {
preventReadOnScroll: boolean
showAll: boolean
disableRefresh: boolean
titleFilterRegex: string
lastViewedFeedId: string
lastViewedFeedType: string
starredOpenState: boolean
Expand All @@ -34,6 +35,7 @@ const state: AppInfoState = reactive({
preventReadOnScroll: loadState('news', 'preventReadOnScroll', null) === '1',
showAll: loadState('news', 'showAll', null) === '1',
disableRefresh: loadState('news', 'disableRefresh', null) === '1',
titleFilterRegex: loadState('news', 'titleFilterRegex', ''),
lastViewedFeedId: loadState('news', 'lastViewedFeedId', '0'),
lastViewedFeedType: loadState('news', 'lastViewedFeedType', '6'),
starredOpenState: loadState('news', 'starredOpenState', null) === '1',
Expand Down Expand Up @@ -71,6 +73,9 @@ const getters = {
disableRefresh(state: AppInfoState) {
return state.disableRefresh
},
titleFilterRegex(state: AppInfoState) {
return state.titleFilterRegex
},
lastViewedFeedId(state: AppInfoState) {
return state.lastViewedFeedId
},
Expand Down Expand Up @@ -149,6 +154,12 @@ export const mutations = {
) {
state.disableRefresh = value
},
titleFilterRegex(
state: AppInfoState,
{ value }: { value: string },
) {
state.titleFilterRegex = value
},
starredOpenState(
state: AppInfoState,
{ value }: { value: boolean },
Expand Down
8 changes: 7 additions & 1 deletion tests/Unit/Service/FeedServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use OCA\News\Utility\HtmlSanitizer;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\IAppConfig;
use OCP\IUserConfig;

use OCA\News\Db\Feed;
use OCA\News\Db\Item;
Expand Down Expand Up @@ -129,11 +130,15 @@ protected function setUp(): void
->getMockBuilder(IAppConfig::class)
->disableOriginalConstructor()
->getMock();
$this->userConfig = $this
->getMockBuilder(IUserConfig::class)
->disableOriginalConstructor()
->getMock();
$this->appData = $this
->getMockBuilder(AppData::class)
->disableOriginalConstructor()
->getMock();

$this->class = new FeedServiceV2(
$this->mapper,
$this->fetcher,
Expand All @@ -142,6 +147,7 @@ protected function setUp(): void
$this->purifier,
$this->logger,
$this->config,
$this->userConfig,
$this->appData
);
$this->uid = 'jack';
Expand Down
Loading