Search: add feed search

Signed-off-by: Sean Molenaar <sean@seanmolenaar.eu>
This commit is contained in:
Sean Molenaar 2021-06-08 18:05:27 +02:00 committed by Benjamin Brahmer
parent be45dd9830
commit fd01e9ad7b
4 changed files with 212 additions and 26 deletions

View File

@ -7,6 +7,7 @@ The format is almost based on [Keep a Changelog](https://keepachangelog.com/en/1
## [16.x.x]
### Changed
- added feed search (#1402)
### Fixed
- removed reference for deleted repair-steps (#1399)
## [15.x.x]

View File

@ -19,9 +19,9 @@ use HTMLPurifier;
use HTMLPurifier_Config;
use Favicon\Favicon;
use OCA\News\Config\LegacyConfig;
use OCA\News\Config\FetcherConfig;
use OCA\News\Hooks\UserDeleteHook;
use OCA\News\Search\FeedSearchProvider;
use OCA\News\Search\FolderSearchProvider;
use OCP\AppFramework\Bootstrap\IBootContext;
@ -29,8 +29,6 @@ use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\ITempManager;
use OCP\AppFramework\App;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCA\News\Fetcher\FeedFetcher;
use OCA\News\Fetcher\Fetcher;
@ -83,12 +81,12 @@ class Application extends App implements IBootstrap
});
$context->registerSearchProvider(FolderSearchProvider::class);
$context->registerSearchProvider(FeedSearchProvider::class);
$context->registerEventListener(BeforeUserDeletedEvent::class, UserDeleteHook::class);
// parameters
$context->registerParameter('exploreDir', __DIR__ . '/../Explore/feeds');
$context->registerParameter('configFile', 'config.ini');
$context->registerService(HTMLPurifier::class, function (ContainerInterface $c): HTMLPurifier {
$directory = $c->get(ITempManager::class)->getTempBaseDir() . '/news/cache/purifier';
@ -142,28 +140,6 @@ class Application extends App implements IBootstrap
$favicon->cache(['dir' => $c->get(ITempManager::class)->getTempBaseDir()]);
return $favicon;
});
//TODO: Remove code after 15.1
$context->registerService('ConfigFolder', function (ContainerInterface $c): ?Node {
/** @var IRootFolder $fs */
$fs = $c->get(IRootFolder::class);
$path = 'news/config';
if ($fs->nodeExists($path)) {
return $fs->get($path);
} else {
return null;
}
});
//TODO: Remove code after 15.1
$context->registerService(LegacyConfig::class, function (ContainerInterface $c): LegacyConfig {
$config = new LegacyConfig(
$c->get('ConfigFolder'),
$c->get(LoggerInterface::class)
);
$config->read($c->get('configFile'), false);
return $config;
});
}
public function boot(IBootContext $context): void

View File

@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace OCA\News\Search;
use OCA\News\Service\FeedServiceV2;
use OCA\News\Service\FolderServiceV2;
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 FeedSearchProvider
*
* @package OCA\News\Search
*/
class FeedSearchProvider implements IProvider
{
/** @var IL10N */
private $l10n;
/** @var IURLGenerator */
private $urlGenerator;
/** @var FeedServiceV2 */
private $service;
public function __construct(IL10N $l10n, IURLGenerator $urlGenerator, FeedServiceV2 $service)
{
$this->l10n = $l10n;
$this->urlGenerator = $urlGenerator;
$this->service = $service;
}
public function getId(): string
{
return 'news_feed';
}
public function getName(): string
{
return $this->l10n->t('News feeds');
}
public function getOrder(string $route, array $routeParameters): int
{
if ($route === 'news.page.index') {
// Active app, prefer my results
return -1;
}
return 60;
}
public function search(IUser $user, ISearchQuery $query): SearchResult
{
$list = [];
$term = strtolower($query->getTerm());
foreach ($this->service->findAllForUser($user->getUID()) as $feed) {
if (strpos(strtolower($feed->getTitle()), $term) === false) {
continue;
}
$list[] = new SearchResultEntry(
$this->urlGenerator->imagePath('core', 'filetypes/text.svg'),
$feed->getTitle(),
$this->l10n->t('Unread articles') . ': ' . $feed->getUnreadCount(),
$this->urlGenerator->linkToRoute('news.page.index') . '#/items/feeds/' . $feed->getId()
);
}
return SearchResult::complete($this->l10n->t('News'), $list);
}
}

View File

@ -0,0 +1,130 @@
<?php
namespace OCA\News\Search;
use OCA\News\Db\Feed;
use OCA\News\Service\FeedServiceV2;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\Search\ISearchQuery;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class FeedSearchProviderTest extends TestCase
{
/**
* @var MockObject|FeedServiceV2
*/
private $folderService;
/**
* @var MockObject|IL10N
*/
private $l10n;
/**
* @var MockObject|IURLGenerator
*/
private $generator;
/**
* @var FeedSearchProvider
*/
private $class;
protected function setUp(): void
{
$this->l10n = $this->getMockBuilder(IL10N::class)
->disableOriginalConstructor()
->getMock();
$this->generator = $this->getMockBuilder(IURLGenerator::class)
->disableOriginalConstructor()
->getMock();
$this->folderService = $this->getMockBuilder(FeedServiceV2::class)
->disableOriginalConstructor()
->getMock();
$this->class = new FeedSearchProvider(
$this->l10n,
$this->generator,
$this->folderService
);
}
public function testGetId()
{
$this->assertSame('news_feed', $this->class->getId());
}
public function testGetName()
{
$this->l10n->expects($this->once())
->method('t')
->with('News feeds')
->willReturnArgument(0);
$this->assertSame('News feeds', $this->class->getName());
}
public function testGetOrderExternal()
{
$this->assertSame(60, $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();
$user->expects($this->once())
->method('getUID')
->willReturn('user');
$query->expects($this->once())
->method('getTerm')
->willReturn('Term');
$folders = [
Feed::fromRow(['id' => 1,'title' => 'some_tErm', 'unread_count'=> 1]),
Feed::fromRow(['id' => 2,'title' => 'nothing', 'unread_count'=> 1])
];
$this->folderService->expects($this->once())
->method('findAllForUser')
->with('user')
->willReturn($folders);
$this->l10n->expects($this->exactly(2))
->method('t')
->withConsecutive(['Unread articles'], ['News'])
->willReturnArgument(0);
$this->generator->expects($this->once())
->method('imagePath')
->with('core', 'filetypes/text.svg')
->willReturn('folderpath.svg');
$this->generator->expects($this->once())
->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('Unread articles: 1', $entry['subline']);
$this->assertSame('/news#/items/feeds/1', $entry['resourceUrl']);
}
}