Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>tags/v20.0.0beta1
@@ -47,6 +47,8 @@ return array( | |||
'OCA\\Files\\Listener\\LoadSidebarListener' => $baseDir . '/../lib/Listener/LoadSidebarListener.php', | |||
'OCA\\Files\\Migration\\Version11301Date20191205150729' => $baseDir . '/../lib/Migration/Version11301Date20191205150729.php', | |||
'OCA\\Files\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php', | |||
'OCA\\Files\\Search\\FilesSearchProvider' => $baseDir . '/../lib/Search/FilesSearchProvider.php', | |||
'OCA\\Files\\Search\\FilesSearchResultEntry' => $baseDir . '/../lib/Search/FilesSearchResultEntry.php', | |||
'OCA\\Files\\Service\\DirectEditingService' => $baseDir . '/../lib/Service/DirectEditingService.php', | |||
'OCA\\Files\\Service\\OwnershipTransferService' => $baseDir . '/../lib/Service/OwnershipTransferService.php', | |||
'OCA\\Files\\Service\\TagService' => $baseDir . '/../lib/Service/TagService.php', |
@@ -62,6 +62,8 @@ class ComposerStaticInitFiles | |||
'OCA\\Files\\Listener\\LoadSidebarListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarListener.php', | |||
'OCA\\Files\\Migration\\Version11301Date20191205150729' => __DIR__ . '/..' . '/../lib/Migration/Version11301Date20191205150729.php', | |||
'OCA\\Files\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php', | |||
'OCA\\Files\\Search\\FilesSearchProvider' => __DIR__ . '/..' . '/../lib/Search/FilesSearchProvider.php', | |||
'OCA\\Files\\Search\\FilesSearchResultEntry' => __DIR__ . '/..' . '/../lib/Search/FilesSearchResultEntry.php', | |||
'OCA\\Files\\Service\\DirectEditingService' => __DIR__ . '/..' . '/../lib/Service/DirectEditingService.php', | |||
'OCA\\Files\\Service\\OwnershipTransferService' => __DIR__ . '/..' . '/../lib/Service/OwnershipTransferService.php', | |||
'OCA\\Files\\Service\\TagService' => __DIR__ . '/..' . '/../lib/Service/TagService.php', |
@@ -44,6 +44,7 @@ use OCA\Files\Event\LoadSidebar; | |||
use OCA\Files\Listener\LegacyLoadAdditionalScriptsAdapter; | |||
use OCA\Files\Listener\LoadSidebarListener; | |||
use OCA\Files\Notification\Notifier; | |||
use OCA\Files\Search\FilesSearchProvider; | |||
use OCA\Files\Service\TagService; | |||
use OCP\AppFramework\App; | |||
use OCP\AppFramework\Bootstrap\IBootContext; | |||
@@ -106,6 +107,8 @@ class Application extends App implements IBootstrap { | |||
$context->registerEventListener(LoadAdditionalScriptsEvent::class, LegacyLoadAdditionalScriptsAdapter::class); | |||
$context->registerEventListener(LoadSidebar::class, LoadSidebarListener::class); | |||
$context->registerSearchProvider(FilesSearchProvider::class); | |||
} | |||
public function boot(IBootContext $context): void { |
@@ -25,7 +25,10 @@ declare(strict_types=1); | |||
namespace OCA\Files\Search; | |||
use OC\Search\Provider\File; | |||
use OC\Search\Result\File as FileResult; | |||
use OCP\IL10N; | |||
use OCP\IURLGenerator; | |||
use OCP\IUser; | |||
use OCP\Search\IProvider; | |||
use OCP\Search\ISearchQuery; | |||
@@ -33,11 +36,21 @@ use OCP\Search\SearchResult; | |||
class FilesSearchProvider implements IProvider { | |||
/** @var File */ | |||
private $fileSearch; | |||
/** @var IL10N */ | |||
private $l10n; | |||
public function __construct(IL10N $l10n) { | |||
/** @var IURLGenerator */ | |||
private $urlGenerator; | |||
public function __construct(File $fileSearch, | |||
IL10N $l10n, | |||
IURLGenerator $urlGenerator) { | |||
$this->l10n = $l10n; | |||
$this->fileSearch = $fileSearch; | |||
$this->urlGenerator = $urlGenerator; | |||
} | |||
public function getId(): string { | |||
@@ -47,26 +60,14 @@ class FilesSearchProvider implements IProvider { | |||
public function search(IUser $user, ISearchQuery $query): SearchResult { | |||
return SearchResult::complete( | |||
$this->l10n->t('Files'), | |||
[ | |||
new FilesSearchResultEntry( | |||
"path/to/icon.png", | |||
"cute cats.jpg", | |||
"/Cats", | |||
"/f/21156" | |||
), | |||
new FilesSearchResultEntry( | |||
"path/to/icon.png", | |||
"cat 1.jpg", | |||
"/Cats", | |||
"/f/21192" | |||
), | |||
new FilesSearchResultEntry( | |||
"path/to/icon.png", | |||
"cat 2.jpg", | |||
"/Cats", | |||
"/f/25942" | |||
), | |||
] | |||
array_map(function (FileResult $result) { | |||
return new FilesSearchResultEntry( | |||
$this->urlGenerator->linkToRoute('core.Preview.getPreviewByFileId', ['x' => 32, 'y' => 32, 'fileId' => $result->id]), | |||
$result->name, | |||
$result->path, | |||
$result->link | |||
); | |||
}, $this->fileSearch->search($query->getTerm())) | |||
); | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* | |||
* @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\Files\Search; | |||
use OCP\Search\ASearchResultEntry; | |||
class FilesSearchResultEntry extends ASearchResultEntry { | |||
public function __construct(string $thumbnailUrl, | |||
string $filename, | |||
string $path, | |||
string $url) { | |||
parent::__construct($thumbnailUrl, $filename, $path, $url); | |||
} | |||
} |
@@ -25,6 +25,8 @@ declare(strict_types=1); | |||
namespace OC\Search; | |||
use InvalidArgumentException; | |||
use OCP\AppFramework\Bootstrap\IRegistrationContext; | |||
use OCP\AppFramework\QueryException; | |||
use OCP\ILogger; | |||
use OCP\IServerContainer; | |||
@@ -37,6 +39,21 @@ use function array_map; | |||
/** | |||
* Queries individual \OCP\Search\IProvider implementations and composes a | |||
* unified search result for the user's search term | |||
* | |||
* The search process is generally split into two steps | |||
* | |||
* 1. Get a list of provider (`getProviders`) | |||
* 2. Get search results of each provider (`search`) | |||
* | |||
* The reasoning behind this is that the runtime complexity of a combined search | |||
* result would be O(n) and linearly grow with each provider added. This comes | |||
* from the nature of php where we can't concurrently fetch the search results. | |||
* So we offload the concurrency the client application (e.g. JavaScript in the | |||
* browser) and let it first get the list of providers to then fetch all results | |||
* concurrently. The client is free to decide whether all concurrent search | |||
* results are awaited or shown as they come in. | |||
* | |||
* @see IProvider::search() for the arguments of the individual search requests | |||
*/ | |||
class SearchComposer { | |||
@@ -58,6 +75,17 @@ class SearchComposer { | |||
$this->logger = $logger; | |||
} | |||
/** | |||
* Register a search provider lazily | |||
* | |||
* Registers the fully-qualified class name of an implementation of an | |||
* IProvider. The service will only be queried on demand. Apps will register | |||
* the providers through the registration context object. | |||
* | |||
* @see IRegistrationContext::registerSearchProvider() | |||
* | |||
* @param string $class | |||
*/ | |||
public function registerProvider(string $class): void { | |||
$this->lazyProviders[] = $class; | |||
} | |||
@@ -85,6 +113,11 @@ class SearchComposer { | |||
$this->lazyProviders = []; | |||
} | |||
/** | |||
* Get a list of all provider IDs for the consecutive calls to `search` | |||
* | |||
* @return string[] | |||
*/ | |||
public function getProviders(): array { | |||
$this->loadLazyProviders(); | |||
@@ -97,11 +130,25 @@ class SearchComposer { | |||
}, $this->providers)); | |||
} | |||
/** | |||
* Query an individual search provider for results | |||
* | |||
* @param IUser $user | |||
* @param string $providerId one of the IDs received by `getProviders` | |||
* @param ISearchQuery $query | |||
* | |||
* @return SearchResult | |||
* @throws InvalidArgumentException when the $providerId does not correspond to a registered provider | |||
*/ | |||
public function search(IUser $user, | |||
string $providerId, | |||
ISearchQuery $query): SearchResult { | |||
$this->loadLazyProviders(); | |||
return $this->providers[$providerId]->search($user, $query); | |||
$provider = $this->providers[$providerId] ?? null; | |||
if ($provider === null) { | |||
throw new InvalidArgumentException("Provider $providerId is unknown"); | |||
} | |||
return $provider->search($user, $query); | |||
} | |||
} |
@@ -28,7 +28,7 @@ namespace OCP\Search; | |||
use OCP\IUser; | |||
/** | |||
* Interface for an app search providers | |||
* Interface for search providers | |||
* | |||
* These providers will be implemented in apps, so they can participate in the | |||
* global search results of Nextcloud. If an app provides more than one type of |