diff --git a/lib/Provider/DeckProvider.php b/lib/Provider/DeckProvider.php new file mode 100644 index 000000000..fcec2b456 --- /dev/null +++ b/lib/Provider/DeckProvider.php @@ -0,0 +1,281 @@ + + * @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\Deck\Provider; + + +use OC\FullTextSearch\Model\IndexDocument; +use OC\FullTextSearch\Model\SearchTemplate; +use OCA\Deck\Service\FullTextSearchService; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +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; +use OCP\IURLGenerator; + + +/** + * Class DeckProvider + * + * @package OCA\Deck\Provider + */ +class DeckProvider implements IFullTextSearchProvider { + + + const DECK_PROVIDER_ID = 'deck'; + + + /** @var IL10N */ + private $l10n; + + /** @var IUrlGenerator */ + private $urlGenerator; + + /** @var FullTextSearchService */ + private $fullTextSearchService; + + + /** @var IRunner */ + private $runner; + + /** @var IIndexOptions */ + private $indexOptions = []; + + + /** + * DeckProvider constructor. + * + * @param IL10N $l10n + * @param IUrlGenerator $urlGenerator + * @param FullTextSearchService $fullTextSearchService + */ + public function __construct( + IL10N $l10n, IUrlGenerator $urlGenerator, FullTextSearchService $fullTextSearchService + ) { + $this->l10n = $l10n; + $this->urlGenerator = $urlGenerator; + $this->fullTextSearchService = $fullTextSearchService; + } + + + /** + * return unique id of the provider + */ + public function getId(): string { + return self::DECK_PROVIDER_ID; + } + + + /** + * return name of the provider + */ + public function getName(): string { + return 'Deck'; + } + + + /** + * @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-deck', 'icons'); + + 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 { + $cards = $this->fullTextSearchService->getCardsFromUser($userId); + + $documents = []; + foreach ($cards as $card) { + $documents[] = $this->fullTextSearchService->generateIndexDocumentFromCard($card); + } + + return $documents; + } + + + /** + * @param IIndexDocument $document + * + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + */ + public function fillIndexDocument(IIndexDocument $document) { + $this->fullTextSearchService->fillIndexDocument($document); + $this->updateRunnerInfo('info', $document->getTitle()); + } + + + /** + * @param IIndexDocument $document + * + * @return bool + */ + public function isDocumentUpToDate(IIndexDocument $document): bool { + return false; + } + + + /** + * @param IIndex $index + * + * @return IIndexDocument + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + */ + public function updateDocument(IIndex $index): IIndexDocument { + $document = new IndexDocument(DeckProvider::DECK_PROVIDER_ID, $index->getDocumentId()); + $document->setIndex($index); + + $this->fullTextSearchService->fillIndexDocument($document); + + 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) { + } + + + /** + * after a search, improve results + * + * @param ISearchResult $searchResult + */ + public function improveSearchResult(ISearchResult $searchResult) { + foreach ($searchResult->getDocuments() as $document) { + try { + $board = + $this->fullTextSearchService->getBoardFromCardId((int)$document->getId()); + $path = '#!/board/' . $board->getId() . '//card/' . $document->getId(); + $document->setLink($this->urlGenerator->linkToRoute('deck.page.index') . $path); + } catch (DoesNotExistException $e) { + } catch (MultipleObjectsReturnedException $e) { + } + } + } + + + /** + * @param string $info + * @param string $value + */ + private function updateRunnerInfo(string $info, string $value) { + if ($this->runner === null) { + return; + } + + $this->runner->setInfo($info, $value); + } + +} + diff --git a/lib/Service/FullTextSearchService.php b/lib/Service/FullTextSearchService.php new file mode 100644 index 000000000..f8e8b0ad8 --- /dev/null +++ b/lib/Service/FullTextSearchService.php @@ -0,0 +1,273 @@ + + * @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\Deck\Service; + + +use OC\FullTextSearch\Model\DocumentAccess; +use OC\FullTextSearch\Model\IndexDocument; +use OCA\Deck\Db\Board; +use OCA\Deck\Db\BoardMapper; +use OCA\Deck\Db\Card; +use OCA\Deck\Db\CardMapper; +use OCA\Deck\Db\Stack; +use OCA\Deck\Db\StackMapper; +use OCA\Deck\Provider\DeckProvider; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\FullTextSearch\IFullTextSearchManager; +use OCP\FullTextSearch\Model\IDocumentAccess; +use OCP\FullTextSearch\Model\IIndex; +use OCP\FullTextSearch\Model\IIndexDocument; +use Symfony\Component\EventDispatcher\GenericEvent; + + +/** + * Class FullTextSearchService + * + * @package OCA\Deck\Service + */ +class FullTextSearchService { + + + /** @var BoardMapper */ + private $boardMapper; + + /** @var StackMapper */ + private $stackMapper; + + /** @var CardMapper */ + private $cardMapper; + + /** @var IFullTextSearchManager */ + private $fullTextSearchManager; + + + /** + * FullTextSearchService constructor. + * + * @param BoardMapper $boardMapper + * @param StackMapper $stackMapper + * @param CardMapper $cardMapper + * @param IFullTextSearchManager $fullTextSearchManager + */ + public function __construct( + BoardMapper $boardMapper, StackMapper $stackMapper, CardMapper $cardMapper, + IFullTextSearchManager $fullTextSearchManager + ) { + $this->boardMapper = $boardMapper; + $this->stackMapper = $stackMapper; + $this->cardMapper = $cardMapper; + + $this->fullTextSearchManager = $fullTextSearchManager; + } + + + /** + * @param GenericEvent $e + */ + public function onCardCreated(GenericEvent $e) { + $cardId = $e->getArgument('id'); + $userId = $e->getArgument('userId'); + + $this->fullTextSearchManager->createIndex( + DeckProvider::DECK_PROVIDER_ID, (string)$cardId, $userId, IIndex::INDEX_FULL + ); + } + + + /** + * @param GenericEvent $e + */ + public function onCardUpdated(GenericEvent $e) { + $cardId = $e->getArgument('id'); + + $this->fullTextSearchManager->updateIndexStatus( + DeckProvider::DECK_PROVIDER_ID, (string)$cardId, IIndex::INDEX_CONTENT + ); + } + + + /** + * @param GenericEvent $e + */ + public function onCardDeleted(GenericEvent $e) { + $cardId = $e->getArgument('id'); + + $this->fullTextSearchManager->updateIndexStatus( + DeckProvider::DECK_PROVIDER_ID, (string)$cardId, IIndex::INDEX_REMOVE + ); + } + + + /** + * @param GenericEvent $e + */ + public function onBoardShares(GenericEvent $e) { + $boardId = $e->getArgument('boardId'); + + $cards = array_map( + function(Card $item) { + return $item->getId(); + }, + $this->getCardsFromBoard($boardId) + ); + $this->fullTextSearchManager->updateIndexesStatus( + DeckProvider::DECK_PROVIDER_ID, $cards, IIndex::INDEX_META + ); + } + + + /** + * @param Card $card + * + * @return IIndexDocument + */ + public function generateIndexDocumentFromCard(Card $card): IIndexDocument { + $document = new IndexDocument(DeckProvider::DECK_PROVIDER_ID, (string)$card->getId()); + + return $document; + } + + + /** + * @param IIndexDocument $document + * + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + */ + public function fillIndexDocument(IIndexDocument $document) { + /** @var Card $card */ + $card = $this->cardMapper->find((int)$document->getId()); + + $document->setTitle($card->getTitle()); + $document->setContent($card->getDescription()); + $document->setAccess($this->generateDocumentAccessFromCardId($card->getId())); + } + + + /** + * @param int $cardId + * + * @return IDocumentAccess + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + */ + public function generateDocumentAccessFromCardId(int $cardId): IDocumentAccess { + $board = $this->getBoardFromCardId($cardId); + + $access = new DocumentAccess($board->getOwner()); + + return $access; + } + + + /** + * @param string $userId + * + * @return Card[] + */ + public function getCardsFromUser(string $userId): array { + $cards = []; + $boards = $this->getBoardsFromUser($userId); + foreach ($boards as $board) { + $stacks = $this->getStacksFromBoard($board->getId()); + foreach ($stacks as $stack) { + $cards = array_merge($cards, $this->getCardsFromStack($stack->getId())); + } + } + + return $cards; + } + + + /** + * @param int $boardId + * + * @return Card[] + */ + public function getCardsFromBoard(int $boardId): array { + $cards = []; + $stacks = $this->getStacksFromBoard($boardId); + foreach ($stacks as $stack) { + $cards = array_merge($cards, $this->getCardsFromStack($stack->getId())); + } + + return $cards; + } + + + /** + * @param int $cardId + * + * @return Board + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + */ + public function getBoardFromCardId(int $cardId): Board { + $boardId = (int)$this->cardMapper->findBoardId($cardId); + /** @var Board $board */ + $board = $this->boardMapper->find($boardId); + + return $board; + } + + + /** + * @param int $stackId + * + * @return Card[] + */ + private function getCardsFromStack(int $stackId): array { + return $this->cardMapper->findAll($stackId, null, null); + } + + + /** + * @param int $boardId + * + * @return Stack[] + */ + private function getStacksFromBoard(int $boardId): array { + return $this->stackMapper->findAll($boardId, null, null); + + } + + + /** + * @param string $userId + * + * @return Board[] + */ + private function getBoardsFromUser(string $userId): array { + return $this->boardMapper->findAllByUser($userId, null, null, -1); + } + + +} +