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);
+ }
+
+
+}
+