diff --git a/.gitignore b/.gitignore index 5e399d91532..e9beba66d19 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ tests/php/coverage-html tests/php/clover.xml tests/integration/vendor tests/integration/output + +\.idea/ diff --git a/appinfo/info.xml b/appinfo/info.xml index cd9b0dd1e12..e63017954e9 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -99,4 +99,9 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m OCA\Spreed\Collaboration\Collaborators\RoomPlugin + + + OCA\Spreed\FullTextSearch\TalkProvider + + diff --git a/css/fulltextsearch.css b/css/fulltextsearch.css new file mode 100644 index 00000000000..ad2efd3e344 --- /dev/null +++ b/css/fulltextsearch.css @@ -0,0 +1,3 @@ +.icon-fts-talk { + background-image: url('/apps/spreed/img/app-dark.svg'); +} diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index d75233a14bf..882a1dc6bf8 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -30,6 +30,7 @@ use OCA\Spreed\Config; use OCA\Spreed\Files\Listener as FilesListener; use OCA\Spreed\Files\TemplateLoader as FilesTemplateLoader; +use OCA\Spreed\FullTextSearch\FullTextSearchService; use OCA\Spreed\Listener; use OCA\Spreed\Notification\Listener as NotificationListener; use OCA\Spreed\Notification\Notifier; @@ -136,4 +137,17 @@ protected function extendDefaultContentSecurityPolicy(): void { $cspManager = $this->getContainer()->getServer()->getContentSecurityPolicyManager(); $cspManager->addDefaultPolicy($csp); } + + /** + * @param EventDispatcherInterface $dispatcher + */ + protected function registerFullTextSearch(EventDispatcherInterface $dispatcher) { + $dispatcher->addListener( + ChatManager::class . '::sendMessage', function(GenericEvent $e) { + $fullTextSearchService = $this->getContainer() + ->query(FullTextSearchService::class); + $fullTextSearchService->onSendMessage($e); + } + ); + } } diff --git a/lib/FullTextSearch/FullTextSearchService.php b/lib/FullTextSearch/FullTextSearchService.php new file mode 100644 index 00000000000..9e1851f7c11 --- /dev/null +++ b/lib/FullTextSearch/FullTextSearchService.php @@ -0,0 +1,208 @@ + + * @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 . + * + */ + + +namespace OCA\Spreed\FullTextSearch; + + +use OC\FullTextSearch\Model\DocumentAccess; +use OC\FullTextSearch\Model\IndexDocument; +use OCA\Spreed\Chat\ChatManager; +use OCA\Spreed\Exceptions\RoomNotFoundException; +use OCA\Spreed\Manager; +use OCA\Spreed\Participant; +use OCP\Comments\IComment; +use OCP\FullTextSearch\IFullTextSearchManager; +use OCP\FullTextSearch\Model\IDocumentAccess; +use OCP\FullTextSearch\Model\IIndex; +use OCP\FullTextSearch\Model\IIndexDocument; +use OCP\FullTextSearch\Model\ISearchResult; +use OCP\IURLGenerator; +use Symfony\Component\EventDispatcher\GenericEvent; + + +/** + * Class FullTextSearchService + * + * @package OCA\Spreed\FullTextSearch + */ +class FullTextSearchService { + + + /** @var IURLGenerator */ + private $urlGenerator; + + /** @var Manager */ + private $roomManager; + + /** @var ChatManager */ + private $chatManager; + + /** @var IFullTextSearchManager */ + private $fullTextSearchManager; + + + /** + * FullTextSearchService constructor. + * + * @param IURLGenerator $urlGenerator + * @param Manager $roomManager + * @param ChatManager $chatManager + * @param IFullTextSearchManager $fullTextSearchManager + */ + public function __construct( + IUrlGenerator $urlGenerator, Manager $roomManager, ChatManager $chatManager, + IFullTextSearchManager $fullTextSearchManager + ) { + $this->urlGenerator = $urlGenerator; + $this->roomManager = $roomManager; + $this->chatManager = $chatManager; + $this->fullTextSearchManager = $fullTextSearchManager; + } + + + public function onSendMessage(GenericEvent $e) { + /** @var IComment $comment */ + $comment = $e->getArgument('comment'); + if ($comment->getObjectType() !== 'chat') { + return; + } + + $this->fullTextSearchManager->createIndex( + TalkProvider::SPREED_PROVIDER_ID, $comment->getObjectId(), $comment->getActorId(), + IIndex::INDEX_FULL + ); + } + + + /** + * @param IIndexDocument $document + */ + public function generateDocument(IIndexDocument $document) { + $document->setAccess($this->generateDocumentAccessForRoom($document->getId())); + + $this->fillContent($document); + } + + + /** + * @param IIndex $index + * + * @return IIndexDocument + */ + public function updateDocument(IIndex $index): IIndexDocument { + $document = new IndexDocument(TalkProvider::SPREED_PROVIDER_ID, $index->getDocumentId()); + + $this->generateDocument($document); + $document->setIndex($index); + + return $document; + } + + + /** + * @param ISearchResult $searchResult + */ + public function improveSearchResult(ISearchResult $searchResult) { + foreach ($searchResult->getDocuments() as $document) { + try { + $board = + $this->roomManager->getRoomById((int)$document->getId()); + $document->setLink( + $this->urlGenerator->linkToRoute( + 'spreed.pagecontroller.showCall', ['token' => $board->getToken()] + ) + ); + } catch (RoomNotFoundException $e) { + } + } + } + + + /** + * @param IIndexDocument $document + */ + private function fillContent(IIndexDocument $document) { + $room = $this->roomManager->getRoomById((int)$document->getId()); + $document->setTitle($room->getName()); + + $all = $this->chatManager->getHistory($room, 0, 100); + + $comments = []; + foreach ($all as $comment) { + if ($comment->getVerb() !== 'comment') { + continue; + } + + $dTime = $comment->getCreationDateTime(); + $date = $dTime->format('ymd'); + if (!array_key_exists($date, $comments)) { + $comments[$date] = []; + } + + $comments[$date][] = '<' . $comment->getActorId() . '> ' . $comment->getMessage(); + } + + foreach (array_keys($comments) as $day) { + $document->addPart((string)$day, implode(" \n ", $comments[$day])); + } + } + + + /** + * @param string $roomId + * + * @return IDocumentAccess + */ + private function generateDocumentAccessForRoom(string $roomId): IDocumentAccess { + $room = $this->roomManager->getRoomById((int)$roomId); + + $ownerId = ''; + $users = []; + $participants = $room->getParticipants(); + foreach ($participants as $participant) { + switch ($participant->getParticipantType()) { + case Participant::OWNER: + $ownerId = $participant->getUser(); + break; + + case Participant::USER: + case Participant::MODERATOR: + case Participant::USER_SELF_JOINED: + $users[] = $participant->getUser(); + break; + } + } + + $access = new DocumentAccess($ownerId); + $access->addUsers($users); + + return $access; + } + +} + diff --git a/lib/FullTextSearch/TalkProvider.php b/lib/FullTextSearch/TalkProvider.php new file mode 100644 index 00000000000..471ddc6e6ff --- /dev/null +++ b/lib/FullTextSearch/TalkProvider.php @@ -0,0 +1,271 @@ + + * @copyright 2019 + * @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 . + * + */ + + +namespace OCA\Spreed\FullTextSearch; + + +use OC\FullTextSearch\Model\IndexDocument; +use OC\FullTextSearch\Model\SearchTemplate; +use OCA\Spreed\Manager; +use OCP\FullTextSearch\IFullTextSearchPlatform; +use OCP\FullTextSearch\IFullTextSearchProvider; +use OCP\FullTextSearch\Model\IIndex; +use OCP\FullTextSearch\Model\IIndexDocument; +use OCP\FullTextSearch\Model\IIndexOptions; +use OCP\FullTextSearch\Model\IRunner; +use OCP\FullTextSearch\Model\ISearchRequest; +use OCP\FullTextSearch\Model\ISearchResult; +use OCP\FullTextSearch\Model\ISearchTemplate; +use OCP\IL10N; + + +class TalkProvider implements IFullTextSearchProvider { + + + const SPREED_PROVIDER_ID = 'spreed'; + + + /** @var IL10N */ + private $l10n; + + /** @var Manager */ + private $roomManager; + + /** @var FullTextSearchService */ + private $fullTextSearchService; + + + /** @var IRunner */ + private $runner; + + /** @var IIndexOptions */ + private $indexOptions = []; + + + public function __construct( + IL10N $l10n, Manager $roomManager, FullTextSearchService $fullTextSearchService + ) { + $this->l10n = $l10n; + $this->roomManager = $roomManager; + $this->fullTextSearchService = $fullTextSearchService; + } + + + /** + * return unique id of the provider + */ + public function getId(): string { + return self::SPREED_PROVIDER_ID; + } + + + /** + * return name of the provider + */ + public function getName(): string { + return 'Talk'; + } + + + /** + * @return array + */ + public function getConfiguration(): array { + return []; + } + + + /** + * @param IRunner $runner + */ + public function setRunner(IRunner $runner) { + $this->runner = $runner; + } + + + /** + * @param IIndexOptions $options + */ + public function setIndexOptions(IIndexOptions $options) { + $this->indexOptions = $options; + } + + + /** + * @return ISearchTemplate + */ + public function getSearchTemplate(): ISearchTemplate { + $template = new SearchTemplate('icon-fts-talk', 'fulltextsearch'); + + return $template; + } + + + /** + * + */ + public function loadProvider() { + } + + + /** + * @param string $userId + * + * @return string[] + */ + public function generateChunks(string $userId): array { + return []; + } + + + /** + * @param string $userId + * + * @param string $chunk + * + * @return IIndexDocument[] + */ + public function generateIndexableDocuments(string $userId, string $chunk): array { + $documents = []; + $rooms = $this->roomManager->getRoomsForParticipant($userId); + foreach ($rooms as $room) { + $document = new IndexDocument(self::SPREED_PROVIDER_ID, (string)$room->getId()); + $document->setTitle($room->getName()); + + $documents[] = $document; + } + + return $documents; + } + + + /** + * @param IIndexDocument $document + */ + public function fillIndexDocument(IIndexDocument $document) { + $this->updateRunnerInfoArray( + [ + 'info' => $document->getId(), + 'title' => $document->getTitle() + ] + ); + + $this->fullTextSearchService->generateDocument($document); + } + + + /** + * @param IIndexDocument $document + * + * @return bool + */ + public function isDocumentUpToDate(IIndexDocument $document): bool { + return false; +// return $this->filesService->isDocumentUpToDate($document); + } + + + /** + * @param IIndex $index + * + * @return IIndexDocument + */ + public function updateDocument(IIndex $index): IIndexDocument { + $document = $this->fullTextSearchService->updateDocument($index); + $this->updateRunnerInfo('info', $document->getId()); + + return $document; + } + + + /** + * @param IFullTextSearchPlatform $platform + */ + public function onInitializingIndex(IFullTextSearchPlatform $platform) { + } + + + /** + * @param IFullTextSearchPlatform $platform + */ + public function onResettingIndex(IFullTextSearchPlatform $platform) { + } + + + /** + * not used yet + */ + public function unloadProvider() { + } + + + /** + * before a search, improve the request + * + * @param ISearchRequest $request + */ + public function improveSearchRequest(ISearchRequest $request) { + $request->addPart('*'); + } + + + /** + * after a search, improve results + * + * @param ISearchResult $searchResult + */ + public function improveSearchResult(ISearchResult $searchResult) { + $this->fullTextSearchService->improveSearchResult($searchResult); + } + + + /** + * @param string $info + * @param string $value + */ + private function updateRunnerInfo(string $info, string $value) { + if ($this->runner === null) { + return; + } + + $this->runner->setInfo($info, $value); + } + + /** + * @param array $info + */ + private function updateRunnerInfoArray(array $info) { + if ($this->runner === null) { + return; + } + + $this->runner->setInfoArray($info); + } + + +}