Skip to content

Commit

Permalink
Implement item search
Browse files Browse the repository at this point in the history
The search result can only link to the feed.

Signed-off-by: Benjamin Brahmer <info@b-brahmer.de>
  • Loading branch information
Grotax committed Feb 14, 2023
1 parent e5f75d7 commit 69681d1
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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;
Expand Down Expand Up @@ -82,6 +83,8 @@ public function register(IRegistrationContext $context): void

$context->registerSearchProvider(FolderSearchProvider::class);
$context->registerSearchProvider(FeedSearchProvider::class);
$context->registerSearchProvider(ItemSearchProvider::class);


$context->registerEventListener(BeforeUserDeletedEvent::class, UserDeleteHook::class);

Expand Down
6 changes: 3 additions & 3 deletions lib/Search/FeedSearchProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
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;
Expand Down Expand Up @@ -48,7 +48,7 @@ public function getName(): string

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;
}
Expand All @@ -67,7 +67,7 @@ public function search(IUser $user, ISearchQuery $query): SearchResult
}

$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()
Expand Down
4 changes: 2 additions & 2 deletions lib/Search/FolderSearchProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ public function getName(): string

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;
Expand Down
109 changes: 109 additions & 0 deletions lib/Search/ItemSearchProvider.php
Original file line number Diff line number Diff line change
@@ -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());
}
}
2 changes: 1 addition & 1 deletion tests/Unit/Search/FeedSearchProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public function testSearch()

$this->generator->expects($this->once())
->method('imagePath')
->with('core', 'filetypes/text.svg')
->with('core', 'rss.svg')
->willReturn('folderpath.svg');

$this->generator->expects($this->once())
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Search/FolderSearchProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public function testGetOrderExternal()

public function testGetOrderInternal()
{
$this->assertSame(-1, $this->class->getOrder('news.page.index', []));
$this->assertSame(0, $this->class->getOrder('news.page.index', []));
}

public function testSearch()
Expand Down
147 changes: 147 additions & 0 deletions tests/Unit/Search/ItemSearchProviderTest.php
Original file line number Diff line number Diff line change
@@ -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']);
}
}

0 comments on commit 69681d1

Please sign in to comment.