summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Brahmer <info@b-brahmer.de>2023-01-25 15:23:44 +0100
committerBenjamin Brahmer <info@b-brahmer.de>2023-02-14 11:18:00 +0100
commit69681d12cb3fb55762902192230edd87fd3215f9 (patch)
tree9a5a52cec0e514001c2a10c37502bd2c6b7e9f58
parente5f75d7a4c8096ef26fc14949ad4b4eaaa137f82 (diff)
Implement item search
The search result can only link to the feed. Signed-off-by: Benjamin Brahmer <info@b-brahmer.de>
-rw-r--r--CHANGELOG.md1
-rw-r--r--lib/AppInfo/Application.php3
-rw-r--r--lib/Search/FeedSearchProvider.php6
-rw-r--r--lib/Search/FolderSearchProvider.php4
-rw-r--r--lib/Search/ItemSearchProvider.php109
-rw-r--r--tests/Unit/Search/FeedSearchProviderTest.php2
-rw-r--r--tests/Unit/Search/FolderSearchProviderTest.php2
-rw-r--r--tests/Unit/Search/ItemSearchProviderTest.php147
8 files changed, 267 insertions, 7 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fbc6bd893..da8d1cfaa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ The format is mostly based on [Keep a Changelog](https://keepachangelog.com/en/1
### Changed
- Drop support for Nextcloud 23 (#2077 )
- Make the "open" keyboard shortcut work faster (#2080)
+- Implemented search for articles, results can only link to the feed. (#2075)
### Fixed
- Stop errors from the favicon library over empty values
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 950d44383..bc5e1b476 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -23,6 +23,7 @@ use OCA\News\Config\FetcherConfig;
use OCA\News\Hooks\UserDeleteHook;
use OCA\News\Search\FeedSearchProvider;
use OCA\News\Search\FolderSearchProvider;
+use OCA\News\Search\ItemSearchProvider;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
@@ -82,6 +83,8 @@ class Application extends App implements IBootstrap
$context->registerSearchProvider(FolderSearchProvider::class);
$context->registerSearchProvider(FeedSearchProvider::class);
+ $context->registerSearchProvider(ItemSearchProvider::class);
+
$context->registerEventListener(BeforeUserDeletedEvent::class, UserDeleteHook::class);
diff --git a/lib/Search/FeedSearchProvider.php b/lib/Search/FeedSearchProvider.php
index bbcd466e4..36b21eaba 100644
--- a/lib/Search/FeedSearchProvider.php
+++ b/lib/Search/FeedSearchProvider.php
@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace OCA\News\Search;
use OCA\News\Service\FeedServiceV2;
-use OCA\News\Service\FolderServiceV2;
+use OCA\News\AppInfo\Application;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser;
@@ -48,7 +48,7 @@ class FeedSearchProvider implements IProvider
public function getOrder(string $route, array $routeParameters): int
{
- if ($route === 'news.page.index') {
+ if (strpos($route, Application::NAME . '.') === 0) {
// Active app, prefer my results
return -1;
}
@@ -67,7 +67,7 @@ class FeedSearchProvider implements IProvider
}
$list[] = new SearchResultEntry(
- $this->urlGenerator->imagePath('core', 'filetypes/text.svg'),
+ $this->urlGenerator->imagePath('core', 'rss.svg'),
$feed->getTitle(),
$this->l10n->t('Unread articles') . ': ' . $feed->getUnreadCount(),
$this->urlGenerator->linkToRoute('news.page.index') . '#/items/feeds/' . $feed->getId()
diff --git a/lib/Search/FolderSearchProvider.php b/lib/Search/FolderSearchProvider.php
index 24439d73d..7f6783c68 100644
--- a/lib/Search/FolderSearchProvider.php
+++ b/lib/Search/FolderSearchProvider.php
@@ -49,9 +49,9 @@ class FolderSearchProvider implements IProvider
public function getOrder(string $route, array $routeParameters): int
{
- if ($route === 'news.page.index') {
+ if (strpos($route, Application::NAME . '.') === 0) {
// Active app, prefer my results
- return -1;
+ return 0;
}
return 55;
diff --git a/lib/Search/ItemSearchProvider.php b/lib/Search/ItemSearchProvider.php
new file mode 100644
index 000000000..29b4bf209
--- /dev/null
+++ b/lib/Search/ItemSearchProvider.php
@@ -0,0 +1,109 @@
+<?php
+declare(strict_types=1);
+
+namespace OCA\News\Search;
+
+use OCA\News\Service\ItemServiceV2;
+use OCA\News\AppInfo\Application;
+use OCA\News\Db\ListType;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\Search\IProvider;
+use OCP\Search\ISearchQuery;
+use OCP\Search\SearchResult;
+use OCP\Search\SearchResultEntry;
+
+/**
+ * Class ItemSearchProvider
+ *
+ * @package OCA\News\Search
+ */
+class ItemSearchProvider implements IProvider
+{
+ /** @var IL10N */
+ private $l10n;
+
+ /** @var IURLGenerator */
+ private $urlGenerator;
+
+ /** @var ItemServiceV2 */
+ private $service;
+
+ public function __construct(IL10N $l10n, IURLGenerator $urlGenerator, ItemServiceV2 $service)
+ {
+ $this->l10n = $l10n;
+ $this->urlGenerator = $urlGenerator;
+ $this->service = $service;
+ }
+
+ public function getId(): string
+ {
+ return 'news_item';
+ }
+
+ public function getName(): string
+ {
+ return $this->l10n->t('News articles');
+ }
+
+ public function getOrder(string $route, array $routeParameters): int
+ {
+ if (strpos($route, Application::NAME . '.') === 0) {
+ // Active app, prefer my results
+ return 1;
+ }
+
+ return 65;
+ }
+
+ private function stripTruncate(string $string, int $length = 50): string
+ {
+ $string = strip_tags(trim($string));
+
+ if (strlen($string) > $length) {
+ $string = wordwrap($string, $length);
+ $string = explode("\n", $string, 2);
+ $string = $string[0];
+ }
+
+ return $string;
+ }
+
+ public function search(IUser $user, ISearchQuery $query): SearchResult
+ {
+ $list = [];
+ $offset = (int) ($query->getCursor() ?? 0);
+ $limit = $query->getLimit();
+
+ $search_result = $this->service->findAllWithFilters(
+ $user->getUID(),
+ ListType::ALL_ITEMS,
+ $limit,
+ $offset,
+ false,
+ [$query->getTerm()]
+ );
+
+ $last = end($search_result);
+ if ($last === false) {
+ return SearchResult::complete(
+ $this->l10n->t('News'),
+ []
+ );
+ }
+
+ $icon = $this->urlGenerator->imagePath('core', 'filetypes/text.svg');
+
+ foreach ($search_result as $item) {
+ $list[] = new SearchResultEntry(
+ $icon,
+ $item->getTitle(),
+ $this->stripTruncate($item->getBody(), 50),
+ $this->urlGenerator->linkToRoute('news.page.index') . '#/items/feeds/' . $item->getFeedId()
+ );
+ }
+
+ return SearchResult::paginated($this->l10n->t('News'), $list, $last->getId());
+ }
+}
diff --git a/tests/Unit/Search/FeedSearchProviderTest.php b/tests/Unit/Search/FeedSearchProviderTest.php
index 97ec1f984..e1463a7a7 100644
--- a/tests/Unit/Search/FeedSearchProviderTest.php
+++ b/tests/Unit/Search/FeedSearchProviderTest.php
@@ -110,7 +110,7 @@ class FeedSearchProviderTest extends TestCase
$this->generator->expects($this->once())
->method('imagePath')
- ->with('core', 'filetypes/text.svg')
+ ->with('core', 'rss.svg')
->willReturn('folderpath.svg');
$this->generator->expects($this->once())
diff --git a/tests/Unit/Search/FolderSearchProviderTest.php b/tests/Unit/Search/FolderSearchProviderTest.php
index cf4ffd969..e06bf45f7 100644
--- a/tests/Unit/Search/FolderSearchProviderTest.php
+++ b/tests/Unit/Search/FolderSearchProviderTest.php
@@ -77,7 +77,7 @@ class FolderSearchProviderTest extends TestCase
public function testGetOrderInternal()
{
- $this->assertSame(-1, $this->class->getOrder('news.page.index', []));
+ $this->assertSame(0, $this->class->getOrder('news.page.index', []));
}
public function testSearch()
diff --git a/tests/Unit/Search/ItemSearchProviderTest.php b/tests/Unit/Search/ItemSearchProviderTest.php
new file mode 100644
index 000000000..062d52a77
--- /dev/null
+++ b/tests/Unit/Search/ItemSearchProviderTest.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace OCA\News\Search;
+
+use OCA\News\Db\Item;
+use OCA\News\Db\ListType;
+use OCA\News\Service\ItemServiceV2;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\Search\ISearchQuery;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+
+class ItemSearchProviderTest extends TestCase
+{
+
+ /**
+ * @var MockObject|ItemServiceV2
+ */
+ private $itemService;
+
+ /**
+ * @var MockObject|IL10N
+ */
+ private $l10n;
+
+ /**
+ * @var MockObject|IURLGenerator
+ */
+ private $generator;
+
+ /**
+ * @var ItemSearchProvider
+ */
+ private $class;
+
+ protected function setUp(): void
+ {
+ $this->l10n = $this->getMockBuilder(IL10N::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->generator = $this->getMockBuilder(IURLGenerator::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->itemService = $this->getMockBuilder(ItemServiceV2::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->class = new ItemSearchProvider(
+ $this->l10n,
+ $this->generator,
+ $this->itemService
+ );
+ }
+
+ public function testGetId()
+ {
+ $this->assertSame('news_item', $this->class->getId());
+ }
+
+ public function testGetName()
+ {
+ $this->l10n->expects($this->once())
+ ->method('t')
+ ->with('News articles')
+ ->willReturnArgument(0);
+
+ $this->assertSame('News articles', $this->class->getName());
+ }
+
+ public function testGetOrderExternal()
+ {
+ $this->assertSame(65, $this->class->getOrder('contacts.Page.index', []));
+ }
+
+ public function testGetOrderInternal()
+ {
+ $this->assertSame(1, $this->class->getOrder('news.page.index', []));
+ }
+
+ public function testSearch()
+ {
+ $user = $this->getMockBuilder(IUser::class)
+ ->getMock();
+ $query = $this->getMockBuilder(ISearchQuery::class)
+ ->getMock();
+
+ $query->expects($this->once())
+ ->method('getCursor')
+ ->willReturn(null);
+
+ $query->expects($this->once())
+ ->method('getLimit')
+ ->willReturn(10);
+
+ $user->expects($this->once())
+ ->method('getUID')
+ ->willReturn('user');
+
+ $query->expects($this->once())
+ ->method('getTerm')
+ ->willReturn('some text');
+
+
+ $items = [
+ Item::fromRow(['id' => 1,'title' => 'some_tErm', 'body' => 'some text', 'feedId' => 1]),
+ Item::fromRow(['id' => 2,'title' => 'nothing', 'body' => 'some text', 'feedId' => 1])
+ ];
+
+ $this->itemService->expects($this->once())
+ ->method('findAllWithFilters')
+ ->with(
+ 'user',
+ ListType::ALL_ITEMS,
+ 10,
+ 0,
+ false,
+ ['some text'])
+ ->willReturn($items);
+
+
+ $this->l10n->expects($this->once())
+ ->method('t')
+ ->with('News')
+ ->willReturnArgument(0);
+
+ $this->generator->expects($this->once())
+ ->method('imagePath')
+ ->with('core', 'filetypes/text.svg')
+ ->willReturn('folderpath.svg');
+
+ $this->generator->expects($this->exactly(2))
+ ->method('linkToRoute')
+ ->with('news.page.index')
+ ->willReturn('/news');
+
+
+ $result = $this->class->search($user, $query)->jsonSerialize();
+ $entry = $result['entries'][0]->jsonSerialize();
+ $this->assertSame('News', $result['name']);
+ $this->assertSame('some_tErm', $entry['title']);
+ $this->assertSame('folderpath.svg', $entry['thumbnailUrl']);
+ $this->assertSame('some text', $entry['subline']);
+ $this->assertSame('/news#/items/feeds/1', $entry['resourceUrl']);
+ }
+}