diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 0cec2593039..22187ae55b1 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -17,6 +17,17 @@ return array( 'NCU\\Config\\Lexicon\\Preset' => $baseDir . '/lib/unstable/Config/Lexicon/Preset.php', 'NCU\\Config\\ValueType' => $baseDir . '/lib/unstable/Config/ValueType.php', 'NCU\\Federation\\ISignedCloudFederationProvider' => $baseDir . '/lib/unstable/Federation/ISignedCloudFederationProvider.php', + 'NCU\\FullTextSearch\\Exceptions\\ServiceNotFoundException' => $baseDir . '/lib/unstable/FullTextSearch/Exceptions/ServiceNotFoundException.php', + 'NCU\\FullTextSearch\\IContentProvider' => $baseDir . '/lib/unstable/FullTextSearch/IContentProvider.php', + 'NCU\\FullTextSearch\\IContentProviderImprovedSearch' => $baseDir . '/lib/unstable/FullTextSearch/IContentProviderImprovedSearch.php', + 'NCU\\FullTextSearch\\IContentProviderSyncIndex' => $baseDir . '/lib/unstable/FullTextSearch/IContentProviderSyncIndex.php', + 'NCU\\FullTextSearch\\IIndexQueryHelper' => $baseDir . '/lib/unstable/FullTextSearch/IIndexQueryHelper.php', + 'NCU\\FullTextSearch\\ILoggerService' => $baseDir . '/lib/unstable/FullTextSearch/ILoggerService.php', + 'NCU\\FullTextSearch\\IManager' => $baseDir . '/lib/unstable/FullTextSearch/IManager.php', + 'NCU\\FullTextSearch\\IService' => $baseDir . '/lib/unstable/FullTextSearch/IService.php', + 'NCU\\FullTextSearch\\Model\\Document' => $baseDir . '/lib/unstable/FullTextSearch/Model/Document.php', + 'NCU\\FullTextSearch\\Model\\DocumentAccess' => $baseDir . '/lib/unstable/FullTextSearch/Model/DocumentAccess.php', + 'NCU\\FullTextSearch\\Model\\UnindexedDocument' => $baseDir . '/lib/unstable/FullTextSearch/Model/UnindexedDocument.php', 'NCU\\Security\\Signature\\Enum\\DigestAlgorithm' => $baseDir . '/lib/unstable/Security/Signature/Enum/DigestAlgorithm.php', 'NCU\\Security\\Signature\\Enum\\SignatoryStatus' => $baseDir . '/lib/unstable/Security/Signature/Enum/SignatoryStatus.php', 'NCU\\Security\\Signature\\Enum\\SignatoryType' => $baseDir . '/lib/unstable/Security/Signature/Enum/SignatoryType.php', @@ -1831,6 +1842,7 @@ return array( 'OC\\Files\\View' => $baseDir . '/lib/private/Files/View.php', 'OC\\ForbiddenException' => $baseDir . '/lib/private/ForbiddenException.php', 'OC\\FullTextSearch\\FullTextSearchManager' => $baseDir . '/lib/private/FullTextSearch/FullTextSearchManager.php', + 'OC\\FullTextSearch\\Manager' => $baseDir . '/lib/private/FullTextSearch/Manager.php', 'OC\\FullTextSearch\\Model\\DocumentAccess' => $baseDir . '/lib/private/FullTextSearch/Model/DocumentAccess.php', 'OC\\FullTextSearch\\Model\\IndexDocument' => $baseDir . '/lib/private/FullTextSearch/Model/IndexDocument.php', 'OC\\FullTextSearch\\Model\\SearchOption' => $baseDir . '/lib/private/FullTextSearch/Model/SearchOption.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index c07b6d127cc..18a784bab88 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -58,6 +58,17 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'NCU\\Config\\Lexicon\\Preset' => __DIR__ . '/../../..' . '/lib/unstable/Config/Lexicon/Preset.php', 'NCU\\Config\\ValueType' => __DIR__ . '/../../..' . '/lib/unstable/Config/ValueType.php', 'NCU\\Federation\\ISignedCloudFederationProvider' => __DIR__ . '/../../..' . '/lib/unstable/Federation/ISignedCloudFederationProvider.php', + 'NCU\\FullTextSearch\\Exceptions\\ServiceNotFoundException' => __DIR__ . '/../../..' . '/lib/unstable/FullTextSearch/Exceptions/ServiceNotFoundException.php', + 'NCU\\FullTextSearch\\IContentProvider' => __DIR__ . '/../../..' . '/lib/unstable/FullTextSearch/IContentProvider.php', + 'NCU\\FullTextSearch\\IContentProviderImprovedSearch' => __DIR__ . '/../../..' . '/lib/unstable/FullTextSearch/IContentProviderImprovedSearch.php', + 'NCU\\FullTextSearch\\IContentProviderSyncIndex' => __DIR__ . '/../../..' . '/lib/unstable/FullTextSearch/IContentProviderSyncIndex.php', + 'NCU\\FullTextSearch\\IIndexQueryHelper' => __DIR__ . '/../../..' . '/lib/unstable/FullTextSearch/IIndexQueryHelper.php', + 'NCU\\FullTextSearch\\ILoggerService' => __DIR__ . '/../../..' . '/lib/unstable/FullTextSearch/ILoggerService.php', + 'NCU\\FullTextSearch\\IManager' => __DIR__ . '/../../..' . '/lib/unstable/FullTextSearch/IManager.php', + 'NCU\\FullTextSearch\\IService' => __DIR__ . '/../../..' . '/lib/unstable/FullTextSearch/IService.php', + 'NCU\\FullTextSearch\\Model\\Document' => __DIR__ . '/../../..' . '/lib/unstable/FullTextSearch/Model/Document.php', + 'NCU\\FullTextSearch\\Model\\DocumentAccess' => __DIR__ . '/../../..' . '/lib/unstable/FullTextSearch/Model/DocumentAccess.php', + 'NCU\\FullTextSearch\\Model\\UnindexedDocument' => __DIR__ . '/../../..' . '/lib/unstable/FullTextSearch/Model/UnindexedDocument.php', 'NCU\\Security\\Signature\\Enum\\DigestAlgorithm' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Enum/DigestAlgorithm.php', 'NCU\\Security\\Signature\\Enum\\SignatoryStatus' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Enum/SignatoryStatus.php', 'NCU\\Security\\Signature\\Enum\\SignatoryType' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Enum/SignatoryType.php', @@ -1872,6 +1883,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Files\\View' => __DIR__ . '/../../..' . '/lib/private/Files/View.php', 'OC\\ForbiddenException' => __DIR__ . '/../../..' . '/lib/private/ForbiddenException.php', 'OC\\FullTextSearch\\FullTextSearchManager' => __DIR__ . '/../../..' . '/lib/private/FullTextSearch/FullTextSearchManager.php', + 'OC\\FullTextSearch\\Manager' => __DIR__ . '/../../..' . '/lib/private/FullTextSearch/Manager.php', 'OC\\FullTextSearch\\Model\\DocumentAccess' => __DIR__ . '/../../..' . '/lib/private/FullTextSearch/Model/DocumentAccess.php', 'OC\\FullTextSearch\\Model\\IndexDocument' => __DIR__ . '/../../..' . '/lib/private/FullTextSearch/Model/IndexDocument.php', 'OC\\FullTextSearch\\Model\\SearchOption' => __DIR__ . '/../../..' . '/lib/private/FullTextSearch/Model/SearchOption.php', diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index 8bd1ff35610..18d22c8d4c6 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace OC\AppFramework\Bootstrap; use Closure; +use NCU\FullTextSearch\IService as IFullTextSearchService; use OC\Support\CrashReport\Registry; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IRegistrationContext; @@ -145,6 +146,11 @@ class RegistrationContext { /** @var array */ private array $configLexiconClasses = []; + /** @var null|ServiceRegistration */ + private ?ServiceRegistration $fullTextSearchService = null; + /** @var array */ + private array $fullTextSearchContentProviders = []; + /** @var ServiceRegistration[] */ private array $teamResourceProviders = []; @@ -443,6 +449,20 @@ class RegistrationContext { $configLexiconClass ); } + + public function registerFullTextSearchService(string $service): void { + $this->context->registerFullTextSearchService( + $this->appId, + $service + ); + } + + public function registerFullTextSearchContentProvider(string $contentProviderClass): void { + $this->context->registerFullTextSearchContentProvider( + $this->appId, + $contentProviderClass + ); + } }; } @@ -657,6 +677,24 @@ class RegistrationContext { $this->configLexiconClasses[$appId] = $configLexiconClass; } + public function registerFullTextSearchService(string $appId, string $service) { + if ($appId !== 'fulltextsearch') { + throw new RuntimeException('Only the FullTextSearch app is allowed to register a fts service'); + } + if ($this->fullTextSearchService !== null) { + throw new RuntimeException('There can only be one FullTextSearch service'); + } + + $this->fullTextSearchService = new ServiceRegistration($appId, $service); + } + + /** + * @psalm-param class-string $contentProviderClass + */ + public function registerFullTextSearchContentProvider(string $appId, string $contentProviderClass): void { + $this->fullTextSearchContentProviders[$appId] = $contentProviderClass; + } + /** * @param App[] $apps */ @@ -1031,4 +1069,18 @@ class RegistrationContext { return \OCP\Server::get($this->configLexiconClasses[$appId]); } + + /** + * @return ServiceRegistration|null + */ + public function getFullTextSearchService(): ?ServiceRegistration { + return $this->fullTextSearchService; + } + + /** + * @return ServiceRegistration<\NCU\FullTextSearch\IContentProvider>[] + */ + public function getFullTextSearchContentProviders(): array { + return $this->fullTextSearchContentProviders; + } } diff --git a/lib/private/FullTextSearch/Manager.php b/lib/private/FullTextSearch/Manager.php new file mode 100644 index 00000000000..540d960fd9a --- /dev/null +++ b/lib/private/FullTextSearch/Manager.php @@ -0,0 +1,49 @@ +service === null) { + $registration = $this->coordinator->getRegistrationContext()?->getFullTextSearchService(); + if ($registration === null) { + throw new ServiceNotFoundException('fts service not found'); + } + $this->service = $this->container->get($registration->getService()); + } + + return $this->service; + } + + /** + * @return ServiceRegistration[] + */ + public function getContentProviders(): array { + return $this->coordinator->getRegistrationContext()?->getFullTextSearchContentProviders() ?? []; + } +} diff --git a/lib/private/Server.php b/lib/private/Server.php index a14ff622647..a1627870093 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -1208,6 +1208,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerAlias(\OCP\Dashboard\IManager::class, \OC\Dashboard\Manager::class); $this->registerAlias(IFullTextSearchManager::class, FullTextSearchManager::class); + $this->registerAlias(\NCU\FullTextSearch\IManager::class, \OC\FullTextSearch\Manager::class); $this->registerAlias(IFilesMetadataManager::class, FilesMetadataManager::class); $this->registerAlias(ISubAdmin::class, SubAdmin::class); diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index 70b35228c87..9f7129c8bf3 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -447,4 +447,21 @@ interface IRegistrationContext { * @since 31.0.0 */ public function registerConfigLexicon(string $configLexiconClass): void; + + /** + * allow FullTextSearch to register its service + * @since 33.0.0 + */ + public function registerFullTextSearchService(string $service): void; + + /** + * Register an implementation of \NCU\FullTextSearch\IContentProvider that + * will handle the getaway with full text search to extract app's content + * + * @param string $contentProviderClass + * + * @psalm-param class-string<\NCU\FullTextSearch\IContentProvider> $contentProviderClass + * @since 33.0.0 + */ + public function registerFullTextSearchContentProvider(string $contentProviderClass): void; } diff --git a/lib/unstable/FullTextSearch/Exceptions/ServiceNotFoundException.php b/lib/unstable/FullTextSearch/Exceptions/ServiceNotFoundException.php new file mode 100644 index 00000000000..c60bc2bb18e --- /dev/null +++ b/lib/unstable/FullTextSearch/Exceptions/ServiceNotFoundException.php @@ -0,0 +1,14 @@ +notNeeded() + * + * @return Generator + */ + public function getUnindexedDocuments(IIndexQueryHelper $qh): Generator; +} diff --git a/lib/unstable/FullTextSearch/IIndexQueryHelper.php b/lib/unstable/FullTextSearch/IIndexQueryHelper.php new file mode 100644 index 00000000000..c92edfd63db --- /dev/null +++ b/lib/unstable/FullTextSearch/IIndexQueryHelper.php @@ -0,0 +1,13 @@ +[] + * @since 33.0.0 + */ + public function getContentProviders(): array; +} diff --git a/lib/unstable/FullTextSearch/IService.php b/lib/unstable/FullTextSearch/IService.php new file mode 100644 index 00000000000..c7a7bb6be10 --- /dev/null +++ b/lib/unstable/FullTextSearch/IService.php @@ -0,0 +1,16 @@ +id = $id; + } + + public function getId(): string { + return $this->id; + } + + public function setFlags(int $flags): Document { + $this->flags = $flags; + $this->checksum = ''; + return $this; + } + + public function getFlags(): int { + return $this->flags; + } + + public function setAccess(?DocumentAccess $access): Document { + $this->access = $access; + $this->checksum = ''; + return $this; + } + + public function getAccess(): ?DocumentAccess { + return $this->access; + } + + public function setTitle(string $title): Document { + $this->title = $title; + $this->checksum = ''; + return $this; + } + + public function getTitle(): string { + return $this->title; + } + + public function setContent(string $content, bool $encoded = false): Document { + $this->content = $content; + $this->contentSize = strlen($content); + $this->contentEncoded = $encoded; + $this->checksum = ''; + return $this; + } + + public function getContent(): string { + return $this->content; + } + + public function isContentEncoded(): bool { + return $this->contentEncoded; + } + + public function getContentSize(): int { + return $this->contentSize; + } + + public function setLastModificationTime(int $lastModificationTime): Document { + $this->lastModificationTime = $lastModificationTime; + return $this; + } + + public function getLastModificationTime(): int { + return $this->lastModificationTime; + } + + public function setTags(array $tags): Document { + $this->tags = $tags; + $this->checksum = ''; + return $this; + } + + public function getTags(): array { + return $this->tags; + } + + public function setDocumentTags(array $documentTags): Document { + $this->documentTags = $documentTags; + $this->checksum = ''; + return $this; + } + + public function getDocumentTags(): array { + return $this->documentTags; + } + + public function addPart(string $key, string $part): Document { + $this->parts[$key] = $part; + $this->checksum = ''; + return $this; + } + + public function setParts(array $parts): Document { + $this->parts = $parts; + $this->checksum = ''; + return $this; + } + + public function getParts(): array { + return $this->parts; + } + + public function getChecksum(): string { + if ($this->checksum === '') { + $data = [ + 'title' => $this->getTitle(), + 'access' => $this->getAccess(), + 'flags' => $this->getFlags(), + 'tags' => $this->getTags(), + 'documentTags' => $this->getDocumentTags(), + 'parts' => $this->getParts(), + 'content' => $this->getContent() + ]; + + try { + $this->checksum = hash('xxh3', json_encode($data, JSON_THROW_ON_ERROR)); + } catch (JsonException $e) { + \OCP\Server::get(LoggerInterface::class)->warning('issue while generating checksum', ['exception' => $e]); + } + } + + return $this->checksum; + } + + public function jsonSerialize(): array { + return [ + 'title' => $this->getTitle(), + 'access' => $this->getAccess(), + 'flags' => $this->getFlags(), + 'tags' => $this->getTags(), + 'documentTags' => $this->getDocumentTags(), + 'lastModificationTime' => $this->getLastModificationTime(), + 'contentSize' => $this->getContentSize(), + 'contentEncoded' => $this->isContentEncoded(), + 'checksum' => $this->getChecksum(), + ]; + } +} diff --git a/lib/unstable/FullTextSearch/Model/DocumentAccess.php b/lib/unstable/FullTextSearch/Model/DocumentAccess.php new file mode 100644 index 00000000000..dc229a40b5c --- /dev/null +++ b/lib/unstable/FullTextSearch/Model/DocumentAccess.php @@ -0,0 +1,86 @@ +viewerId; + } + + public function setViewerId(string $viewerId): DocumentAccess { + $this->viewerId = $viewerId; + return $this; + } + + public function getLinks(): array { + return $this->links; + } + + public function setLinks(array $links): DocumentAccess { + $this->links = $links; + return $this; + } + + public function getCircles(): array { + return $this->circles; + } + + public function setCircles(array $circles): DocumentAccess { + $this->circles = $circles; + return $this; + } + + public function getGroups(): array { + return $this->groups; + } + + public function setGroups(array $groups): DocumentAccess { + $this->groups = $groups; + return $this; + } + + public function getUsers(): array { + return $this->users; + } + + public function setUsers(array $users): DocumentAccess { + $this->users = $users; + return $this; + } + + public function getOwnerId(): string { + return $this->ownerId; + } + + public function setOwnerId(string $ownerId): DocumentAccess { + $this->ownerId = $ownerId; + return $this; + } + + public function jsonSerialize(): array { + return [ + 'ownerId' => $this->getOwnerId(), + 'users' => $this->getUsers(), + 'groups' => $this->getGroups(), + 'circles' => $this->getCircles(), + 'links' => $this->getLinks(), + ]; + } +} diff --git a/lib/unstable/FullTextSearch/Model/UnindexedDocument.php b/lib/unstable/FullTextSearch/Model/UnindexedDocument.php new file mode 100644 index 00000000000..17256153aa8 --- /dev/null +++ b/lib/unstable/FullTextSearch/Model/UnindexedDocument.php @@ -0,0 +1,26 @@ +id; + } + + public function getLastModified(): int { + return $this->lastModified; + } +}