diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | appinfo/app.php | 45 | ||||
-rw-r--r-- | appinfo/autoload.php | 39 | ||||
-rw-r--r-- | appinfo/info.xml | 4 | ||||
-rw-r--r-- | composer/autoload.php | 7 | ||||
-rw-r--r-- | composer/composer.json (renamed from composer.json) | 11 | ||||
-rw-r--r-- | composer/composer.lock (renamed from composer.lock) | 8 | ||||
-rw-r--r-- | lib/AppInfo/Application.php | 61 | ||||
-rw-r--r-- | lib/Controller/NavigationController.php | 8 | ||||
-rw-r--r-- | lib/Provider/TestProvider.php | 4 | ||||
-rw-r--r-- | lib/Search/UnifiedSearchProvider.php | 207 | ||||
-rw-r--r-- | lib/Search/UnifiedSearchResult.php | 178 | ||||
-rw-r--r-- | lib/Service/SearchService.php | 3 |
14 files changed, 465 insertions, 127 deletions
@@ -1,3 +1,6 @@ \.idea/ -vendor/ +composer/* +!composer/autoload.php +!composer/composer.json +!composer/composer.lock @@ -36,9 +36,9 @@ clean: rm -rf $(build_dir) rm -rf node_modules -composer: - composer install --prefer-dist - composer update --prefer-dist +composer-update: + composer install --prefer-dist --working-dir composer + composer update --prefer-dist --working-dir composer test: SHELL:=/bin/bash test: @@ -47,7 +47,7 @@ test: bash <(curl -s https://codecov.io/bash) -t @$(codecov_token_dir)/$(app_name) ; \ fi -appstore: composer clean +appstore: composer-update clean mkdir -p $(sign_dir) rsync -a \ --exclude=/build \ @@ -57,8 +57,8 @@ appstore: composer clean --exclude=/tests \ --exclude=.git \ --exclude=/.github \ - --exclude=/composer.json \ - --exclude=/composer.lock \ + --exclude=/composer/composer.json \ + --exclude=/composer/composer.lock \ --exclude=/l10n/l10n.pl \ --exclude=/CONTRIBUTING.md \ --exclude=/issue_template.md \ diff --git a/appinfo/app.php b/appinfo/app.php deleted file mode 100644 index d5c699d..0000000 --- a/appinfo/app.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php -declare(strict_types=1); - - -/** - * FullTextSearch - Full text search framework for Nextcloud - * - * This file is licensed under the Affero General Public License version 3 or - * later. See the COPYING file. - * - * @author Maxence Lange <maxence@artificial-owl.com> - * @copyright 2018 - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - - -namespace OCA\FullTextSearch\AppInfo; - - -use OCP\AppFramework\QueryException; - - -require_once __DIR__ . '/autoload.php'; - -try { - $app = new Application(); - $app->registerServices(); - $app->registerNavigation(); -} catch (QueryException $e) { - /** we do nothing */ -} diff --git a/appinfo/autoload.php b/appinfo/autoload.php deleted file mode 100644 index 094b758..0000000 --- a/appinfo/autoload.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php -declare(strict_types=1); - - -/** - * FullTextSearch - Full text search framework for Nextcloud - * - * This file is licensed under the Affero General Public License version 3 or - * later. See the COPYING file. - * - * @author Maxence Lange <maxence@artificial-owl.com> - * @copyright 2018 - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - - -namespace OCA\FullTextSearch\AppInfo; - - -$composerDir = __DIR__ . '/../vendor/'; - -if (is_dir($composerDir) && file_exists($composerDir . 'autoload.php')) { - require_once $composerDir . 'autoload.php'; -} - diff --git a/appinfo/info.xml b/appinfo/info.xml index 1506553..2231874 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -10,7 +10,7 @@ Core App of the full-text search framework for your Nextcloud. ]]> </description> - <version>1.4.2</version> + <version>2.0.0</version> <licence>agpl</licence> <author>Maxence Lange</author> <namespace>FullTextSearch</namespace> @@ -23,7 +23,7 @@ Core App of the full-text search framework for your Nextcloud. <repository>https://github.com/nextcloud/fulltextsearch.git</repository> <screenshot>https://raw.githubusercontent.com/nextcloud/fulltextsearch/master/screenshots/0.3.0.png</screenshot> <dependencies> - <nextcloud min-version="18" max-version="19"/> + <nextcloud min-version="20" max-version="20"/> </dependencies> <background-jobs> diff --git a/composer/autoload.php b/composer/autoload.php new file mode 100644 index 0000000..633d47b --- /dev/null +++ b/composer/autoload.php @@ -0,0 +1,7 @@ +<?php + +// autoload.php @generated by Composer + +require_once __DIR__ . '/composer/autoload_real.php'; + +return ComposerAutoloaderInitFullTextSearch::getLoader(); diff --git a/composer.json b/composer/composer.json index 89d0c6d..3e47522 100644 --- a/composer.json +++ b/composer/composer.json @@ -3,12 +3,23 @@ "description": "Full text search framework for Nextcloud", "minimum-stability": "stable", "license": "agpl", + "config": { + "vendor-dir": ".", + "optimize-autoloader": true, + "classmap-authoritative": true, + "autoloader-suffix": "FullTextSearch" + }, "authors": [ { "name": "Maxence Lange", "email": "maxence@artificial-owl.com" } ], + "autoload": { + "psr-4": { + "OCA\\FullTextSearch\\": "../lib/" + } + }, "require": { "daita/my-small-php-tools": "dev-master" } diff --git a/composer.lock b/composer/composer.lock index 9276f32..7359e65 100644 --- a/composer.lock +++ b/composer/composer.lock @@ -12,12 +12,12 @@ "source": { "type": "git", "url": "https://github.com/daita/my-small-php-tools.git", - "reference": "a253279a181ae6c09be80d786377fdb7afd37741" + "reference": "4e602526c3afbba7255ae4764037562075ef030f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/a253279a181ae6c09be80d786377fdb7afd37741", - "reference": "a253279a181ae6c09be80d786377fdb7afd37741", + "url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/4e602526c3afbba7255ae4764037562075ef030f", + "reference": "4e602526c3afbba7255ae4764037562075ef030f", "shasum": "" }, "require": { @@ -40,7 +40,7 @@ } ], "description": "My small PHP Tools", - "time": "2020-07-07T11:06:29+00:00" + "time": "2020-08-06T13:26:28+00:00" } ], "packages-dev": [], diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 1901226..5d264b0 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -31,26 +31,29 @@ declare(strict_types=1); namespace OCA\FullTextSearch\AppInfo; +use Closure; use OC; use OCA\FullTextSearch\Capabilities; +use OCA\FullTextSearch\Search\UnifiedSearchProvider; use OCA\FullTextSearch\Service\ConfigService; use OCA\FullTextSearch\Service\IndexService; use OCA\FullTextSearch\Service\ProviderService; use OCA\FullTextSearch\Service\SearchService; use OCP\AppFramework\App; -use OCP\AppFramework\IAppContainer; -use OCP\AppFramework\QueryException; +use OCP\AppFramework\Bootstrap\IBootContext; +use OCP\AppFramework\Bootstrap\IBootstrap; +use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\FullTextSearch\IFullTextSearchManager; +use OCP\INavigationManager; +use OCP\IServerContainer; +use Throwable; -class Application extends App { +class Application extends App implements IBootstrap { const APP_NAME = 'fulltextsearch'; - /** @var IAppContainer */ - private $container; - /** * Application constructor. @@ -59,24 +62,40 @@ class Application extends App { */ public function __construct(array $params = []) { parent::__construct(self::APP_NAME, $params); + } + - $this->container = $this->getContainer(); - $this->container->registerCapability(Capabilities::class); + /** + * @param IRegistrationContext $context + */ + public function register(IRegistrationContext $context): void { + $context->registerCapability(Capabilities::class); + $context->registerSearchProvider(UnifiedSearchProvider::class); + } + + /** + * @param IBootContext $context + * + * @throws Throwable + */ + public function boot(IBootContext $context): void { + $context->injectFn(Closure::fromCallable([$this, 'registerServices'])); + $context->injectFn(Closure::fromCallable([$this, 'registerNavigation'])); } /** * Register Navigation Tab * - * @throws QueryException + * @param IServerContainer $container */ - public function registerServices() { + protected function registerServices(IServerContainer $container) { /** @var IFullTextSearchManager $fullTextSearchManager */ - $fullTextSearchManager = $this->container->query(IFullTextSearchManager::class); + $fullTextSearchManager = $container->get(IFullTextSearchManager::class); - $providerService = $this->container->query(ProviderService::class); - $indexService = $this->container->query(IndexService::class); - $searchService = $this->container->query(SearchService::class); + $providerService = $container->get(ProviderService::class); + $indexService = $container->get(IndexService::class); + $searchService = $container->get(SearchService::class); $fullTextSearchManager->registerProviderService($providerService); $fullTextSearchManager->registerIndexService($indexService); @@ -87,18 +106,17 @@ class Application extends App { /** * Register Navigation Tab * - * @throws QueryException + * @param IServerContainer $container */ - public function registerNavigation() { + protected function registerNavigation(IServerContainer $container) { /** @var ConfigService $configService */ - $configService = $this->container->query(ConfigService::class); + $configService = $container->get(ConfigService::class); if ($configService->getAppValue(ConfigService::APP_NAVIGATION) !== '1') { return; } - $this->container->getServer() - ->getNavigationManager() - ->add($this->fullTextSearchNavigation()); + $container->get(INavigationManager::class) + ->add($this->fullTextSearchNavigation()); } @@ -108,7 +126,7 @@ class Application extends App { private function fullTextSearchNavigation(): array { $urlGen = OC::$server->getURLGenerator(); $navName = OC::$server->getL10N(self::APP_NAME) - ->t('Search'); + ->t('Search'); return [ 'id' => self::APP_NAME, @@ -119,6 +137,5 @@ class Application extends App { ]; } - } diff --git a/lib/Controller/NavigationController.php b/lib/Controller/NavigationController.php index 1fbf705..cded213 100644 --- a/lib/Controller/NavigationController.php +++ b/lib/Controller/NavigationController.php @@ -91,17 +91,13 @@ class NavigationController extends Controller { * @return TemplateResponse */ public function navigate(): TemplateResponse { - $themingName = $this->config->getAppValue('theming', 'name', 'Nextcloud'); - - $data = [ - 'themingName' => $themingName - ]; + $data = ['themingName' => $themingName]; $this->fullTextSearchManager->addJavascriptAPI(); return new TemplateResponse(Application::APP_NAME, 'navigate', $data); } - } + diff --git a/lib/Provider/TestProvider.php b/lib/Provider/TestProvider.php index 5fb7967..78ffa3b 100644 --- a/lib/Provider/TestProvider.php +++ b/lib/Provider/TestProvider.php @@ -31,6 +31,7 @@ declare(strict_types=1); namespace OCA\FullTextSearch\Provider; +use OC\FullTextSearch\Model\IndexDocument; use OC\FullTextSearch\Model\SearchTemplate; use OCA\FullTextSearch\Model\IndexOptions; use OCA\FullTextSearch\Model\Runner; @@ -58,6 +59,7 @@ class TestProvider implements IFullTextSearchProvider { const TEST_PROVIDER_ID = 'test_provider'; + /** @var ConfigService */ private $configService; @@ -195,7 +197,7 @@ class TestProvider implements IFullTextSearchProvider { * @return IIndexDocument */ public function updateDocument(IIndex $index): IIndexDocument { - return null; + return new IndexDocument($index->getProviderId(), $index->getDocumentId()); } diff --git a/lib/Search/UnifiedSearchProvider.php b/lib/Search/UnifiedSearchProvider.php new file mode 100644 index 0000000..9347410 --- /dev/null +++ b/lib/Search/UnifiedSearchProvider.php @@ -0,0 +1,207 @@ +<?php +declare(strict_types=1); + + +/** + * FullTextSearch - Full text search framework for Nextcloud + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2018 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +namespace OCA\FullTextSearch\Search; + + +use daita\MySmallPhpTools\Traits\TArrayTools; +use Exception; +use OCA\FullTextSearch\Model\SearchRequest; +use OCA\FullTextSearch\Service\ConfigService; +use OCA\FullTextSearch\Service\MiscService; +use OCA\FullTextSearch\Service\SearchService; +use OCP\FullTextSearch\Model\ISearchRequest; +use OCP\FullTextSearch\Model\ISearchResult; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\IUser; +use OCP\Search\IProvider; +use OCP\Search\ISearchQuery; +use OCP\Search\SearchResult; + +/** + * Class UnifiedSearchProvider + * + * @package OCA\FullTextSearch\Search + */ +class UnifiedSearchProvider implements IProvider { + + + const PROVIDER_ID = 'fulltextsearch'; + const ORDER = 1; + + + use TArrayTools; + + + /** @var IL10N */ + private $l10n; + + /** @var IURLGenerator */ + private $urlGenerator; + + /** @var SearchService */ + private $searchService; + + /** @var ConfigService */ + private $configService; + + /** @var MiscService */ + private $miscService; + + + /** + * UnifiedSearchProvider constructor. + * + * @param IL10N $l10n + * @param IURLGenerator $urlGenerator + * @param SearchService $searchService + * @param ConfigService $configService + * @param MiscService $miscService + */ + public function __construct( + IL10N $l10n, IURLGenerator $urlGenerator, SearchService $searchService, ConfigService $configService, + MiscService $miscService + ) { + $this->l10n = $l10n; + $this->urlGenerator = $urlGenerator; + $this->searchService = $searchService; + $this->configService = $configService; + $this->miscService = $miscService; + } + + + /** + * return unique id of the provider + */ + public function getId(): string { + return self::PROVIDER_ID; + } + + + /** + * @return string + */ + public function getName(): string { + return $this->l10n->t('Full Text Search'); + } + + + /** + * @param string $route + * @param array $routeParameters + * + * @return int + */ + public function getOrder(string $route, array $routeParameters): int { + return self::ORDER; + } + + + /** + * @param IUser $user + * @param ISearchQuery $query + * + * @return SearchResult + */ + public function search(IUser $user, ISearchQuery $query): SearchResult { + $result = []; + + $searchRequest = $this->generateSearchRequest($query); + try { + $ftsResult = $this->searchService->search($user->getUID(), $searchRequest); + $result = $this->convertSearchResult($ftsResult); + } catch (Exception $e) { + } + + return SearchResult::paginated( + $this->l10n->t('Full Text Search'), $result, ($query->getCursor() ?? 0) + $query->getLimit() + ); + } + + + /** + * @param $query + * + * @return ISearchRequest + */ + private function generateSearchRequest(ISearchQuery $query): ISearchRequest { + $searchRequest = new SearchRequest(); + + list($app, $controller, $method) = explode('.', $query->getRoute()); + + $searchRequest->setProviders([$app]); + $searchRequest->setSearch($query->getTerm()); + $searchRequest->setPage((int)floor(($query->getCursor() ?? 0) / $query->getLimit()) + 1); + $searchRequest->setParts([]); + $searchRequest->setSize($query->getLimit()); + $searchRequest->setOptions([]); + $searchRequest->setTags([]); + $searchRequest->setSubTags([]); + $searchRequest->setSize($query->getLimit()); + + return $searchRequest; + } + + + /** + * @param ISearchResult[] $searchResult + * + * @return UnifiedSearchResult[] + */ + private function convertSearchResult(array $searchResult): array { + $result = []; + foreach ($searchResult as $ftsSearch) { + foreach ($ftsSearch->getDocuments() as $document) { + $excerpts = $document->getExcerpts(); + if (empty($excerpts)) { + $title = $document->getTitle(); + $subline = ''; + } else { + $title = (sizeof($excerpts) > 0) ? $excerpts[0]['excerpt'] : ''; + $subline = $document->getTitle(); + } + + $unified = $document->getInfoArray('unified'); + $result[] = new UnifiedSearchResult( + $this->get('thumbUrl', $unified, ''), + $this->get('title', $unified, $title), + $this->get('subline', $unified, $subline), + $this->get('link', $unified, $document->getLink()), + $this->get('icon', $unified, '') + ); + } + } + + return $result; + } + +} + diff --git a/lib/Search/UnifiedSearchResult.php b/lib/Search/UnifiedSearchResult.php new file mode 100644 index 0000000..de47a23 --- /dev/null +++ b/lib/Search/UnifiedSearchResult.php @@ -0,0 +1,178 @@ +<?php +declare(strict_types=1); + + +/** + * FullTextSearch - Full text search framework for Nextcloud + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2018 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +namespace OCA\FullTextSearch\Search; + + +use OCP\Search\SearchResultEntry; + + +/** + * Class SearchResultEntry + * + * @package OCA\FullTextSearch\Search + */ +class UnifiedSearchResult extends SearchResultEntry { + + + /** + * UnifiedSearchResult constructor. + * + * @param string $thumbnailUrl + * @param string $title + * @param string $subline + * @param string $resourceUrl + * @param string $icon + * @param bool $rounded + */ + public function __construct( + string $thumbnailUrl = '', string $title = '', string $subline = '', string $resourceUrl = '', + string $icon = '', + bool $rounded = false + ) { + parent::__construct($thumbnailUrl, $title, $subline, $resourceUrl, $icon, $rounded); + } + + + /** + * @return string + */ + public function getThumbnailUrl(): string { + return $this->thumbnailUrl; + } + + /** + * @param string $thumbnailUrl + * + * @return UnifiedSearchResult + */ + public function setThumbnailUrl(string $thumbnailUrl): self { + $this->thumbnailUrl = $thumbnailUrl; + + return $this; + } + + + /** + * @return string + */ + public function getTitle(): string { + return $this->title; + } + + /** + * @param string $title + * + * @return UnifiedSearchResult + */ + public function setTitle(string $title): self { + $this->title = $title; + + return $this; + } + + + /** + * @return string + */ + public function getSubline(): string { + return $this->subline; + } + + /** + * @param string $subline + * + * @return UnifiedSearchResult + */ + public function setSubline(string $subline): self { + $this->subline = $subline; + + return $this; + } + + + /** + * @return string + */ + public function getResourceUrl(): string { + return $this->resourceUrl; + } + + /** + * @param string $resourceUrl + * + * @return UnifiedSearchResult + */ + public function setResourceUrl(string $resourceUrl): self { + $this->resourceUrl = $resourceUrl; + + return $this; + } + + + /** + * @return string + */ + public function getIcon(): string { + return $this->icon; + } + + /** + * @param string $icon + * + * @return UnifiedSearchResult + */ + public function setIcon(string $icon): self { + $this->icon = $icon; + + return $this; + } + + + /** + * @return bool + */ + public function isRounded(): bool { + return $this->rounded; + } + + /** + * @param bool $rounded + * + * @return UnifiedSearchResult + */ + public function setRounded(bool $rounded): self { + $this->rounded = $rounded; + + return $this; + } + +} + diff --git a/lib/Service/SearchService.php b/lib/Service/SearchService.php index 2f21523..8cdfe61 100644 --- a/lib/Service/SearchService.php +++ b/lib/Service/SearchService.php @@ -157,6 +157,7 @@ class SearchService implements ISearchService { $platform = $wrapper->getPlatform(); $access = $this->getDocumentAccessFromUser($user); + return $this->searchFromProviders($platform, $providers, $access, $request); } @@ -186,7 +187,7 @@ class SearchService implements ISearchService { SearchRequest $request ): array { $result = []; - foreach ($providers AS $provider) { + foreach ($providers as $provider) { $provider->improveSearchRequest($request); $searchResult = new SearchResult($request); |