X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=application%2Fbookmark%2FBookmarkFileService.php;h=e64eeafbd98a30f2a0b50ace69357f268d9e2343;hb=refs%2Fheads%2Fmaster;hp=7439d8d8d830bf7de9e56d9c342085c923f894e5;hpb=c4d5be53c2ae503c00da3cfe6b28d0ce9d2ca7f5;p=github%2Fshaarli%2FShaarli.git diff --git a/application/bookmark/BookmarkFileService.php b/application/bookmark/BookmarkFileService.php index 7439d8d8..e64eeafb 100644 --- a/application/bookmark/BookmarkFileService.php +++ b/application/bookmark/BookmarkFileService.php @@ -1,17 +1,21 @@ conf = $conf; $this->history = $history; + $this->mutex = $mutex; $this->pageCacheManager = new PageCacheManager($this->conf->get('resource.page_cache'), $isLoggedIn); - $this->bookmarksIO = new BookmarkIO($this->conf); + $this->bookmarksIO = new BookmarkIO($this->conf, $this->mutex); $this->isLoggedIn = $isLoggedIn; if (!$this->isLoggedIn && $this->conf->get('privacy.hide_public_links', false)) { @@ -62,44 +78,55 @@ class BookmarkFileService implements BookmarkServiceInterface } else { try { $this->bookmarks = $this->bookmarksIO->read(); - } catch (EmptyDataStoreException $e) { + } catch (EmptyDataStoreException | DatastoreNotInitializedException $e) { $this->bookmarks = new BookmarkArray(); - if ($isLoggedIn) { - $this->save(); + + if ($this->isLoggedIn) { + // Datastore file does not exists, we initialize it with default bookmarks. + if ($e instanceof DatastoreNotInitializedException) { + $this->initialize(); + } else { + $this->save(); + } } } if (! $this->bookmarks instanceof BookmarkArray) { $this->migrate(); exit( - 'Your data store has been migrated, please reload the page.'. PHP_EOL . + 'Your data store has been migrated, please reload the page.' . PHP_EOL . 'If this message keeps showing up, please delete data/updates.txt file.' ); } } - $this->bookmarkFilter = new BookmarkFilter($this->bookmarks); + $this->pluginManager = $pluginManager; + $this->bookmarkFilter = new BookmarkFilter($this->bookmarks, $this->conf, $this->pluginManager); } /** * @inheritDoc */ - public function findByHash($hash) + public function findByHash(string $hash, string $privateKey = null): Bookmark { $bookmark = $this->bookmarkFilter->filter(BookmarkFilter::$FILTER_HASH, $hash); // PHP 7.3 introduced array_key_first() to avoid this hack $first = reset($bookmark); - if (! $this->isLoggedIn && $first->isPrivate()) { - throw new Exception('Not authorized'); + if ( + !$this->isLoggedIn + && $first->isPrivate() + && (empty($privateKey) || $privateKey !== $first->getAdditionalContentEntry('private_key')) + ) { + throw new BookmarkNotFoundException(); } - return $bookmark; + return $first; } /** * @inheritDoc */ - public function findByUrl($url) + public function findByUrl(string $url): ?Bookmark { return $this->bookmarks->getByUrl($url); } @@ -107,29 +134,46 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function search($request = [], $visibility = null, $caseSensitive = false, $untaggedOnly = false) - { + public function search( + array $request = [], + string $visibility = null, + bool $caseSensitive = false, + bool $untaggedOnly = false, + bool $ignoreSticky = false, + array $pagination = [] + ): SearchResult { if ($visibility === null) { $visibility = $this->isLoggedIn ? BookmarkFilter::$ALL : BookmarkFilter::$PUBLIC; } // Filter bookmark database according to parameters. - $searchtags = isset($request['searchtags']) ? $request['searchtags'] : ''; - $searchterm = isset($request['searchterm']) ? $request['searchterm'] : ''; + $searchTags = isset($request['searchtags']) ? $request['searchtags'] : ''; + $searchTerm = isset($request['searchterm']) ? $request['searchterm'] : ''; + + if ($ignoreSticky) { + $this->bookmarks->reorder('DESC', true); + } - return $this->bookmarkFilter->filter( + $bookmarks = $this->bookmarkFilter->filter( BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT, - [$searchtags, $searchterm], + [$searchTags, $searchTerm], $caseSensitive, $visibility, $untaggedOnly ); + + return SearchResult::getSearchResult( + $bookmarks, + $pagination['offset'] ?? 0, + $pagination['limit'] ?? null, + $pagination['allowOutOfBounds'] ?? false + ); } /** * @inheritDoc */ - public function get($id, $visibility = null) + public function get(int $id, string $visibility = null): Bookmark { if (! isset($this->bookmarks[$id])) { throw new BookmarkNotFoundException(); @@ -140,7 +184,8 @@ class BookmarkFileService implements BookmarkServiceInterface } $bookmark = $this->bookmarks[$id]; - if (($bookmark->isPrivate() && $visibility != 'all' && $visibility != 'private') + if ( + ($bookmark->isPrivate() && $visibility != 'all' && $visibility != 'private') || (! $bookmark->isPrivate() && $visibility != 'all' && $visibility != 'public') ) { throw new Exception('Unauthorized'); @@ -152,20 +197,17 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function set($bookmark, $save = true) + public function set(Bookmark $bookmark, bool $save = true): Bookmark { - if ($this->isLoggedIn !== true) { + if (true !== $this->isLoggedIn) { throw new Exception(t('You\'re not authorized to alter the datastore')); } - if (! $bookmark instanceof Bookmark) { - throw new Exception(t('Provided data is invalid')); - } if (! isset($this->bookmarks[$bookmark->getId()])) { throw new BookmarkNotFoundException(); } $bookmark->validate(); - $bookmark->setUpdated(new \DateTime()); + $bookmark->setUpdated(new DateTime()); $this->bookmarks[$bookmark->getId()] = $bookmark; if ($save === true) { $this->save(); @@ -177,15 +219,12 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function add($bookmark, $save = true) + public function add(Bookmark $bookmark, bool $save = true): Bookmark { - if ($this->isLoggedIn !== true) { + if (true !== $this->isLoggedIn) { throw new Exception(t('You\'re not authorized to alter the datastore')); } - if (! $bookmark instanceof Bookmark) { - throw new Exception(t('Provided data is invalid')); - } - if (! empty($bookmark->getId())) { + if (!empty($bookmark->getId())) { throw new Exception(t('This bookmarks already exists')); } $bookmark->setId($this->bookmarks->getNextId()); @@ -202,14 +241,11 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function addOrSet($bookmark, $save = true) + public function addOrSet(Bookmark $bookmark, bool $save = true): Bookmark { - if ($this->isLoggedIn !== true) { + if (true !== $this->isLoggedIn) { throw new Exception(t('You\'re not authorized to alter the datastore')); } - if (! $bookmark instanceof Bookmark) { - throw new Exception('Provided data is invalid'); - } if ($bookmark->getId() === null) { return $this->add($bookmark, $save); } @@ -219,14 +255,11 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function remove($bookmark, $save = true) + public function remove(Bookmark $bookmark, bool $save = true): void { - if ($this->isLoggedIn !== true) { + if (true !== $this->isLoggedIn) { throw new Exception(t('You\'re not authorized to alter the datastore')); } - if (! $bookmark instanceof Bookmark) { - throw new Exception(t('Provided data is invalid')); - } if (! isset($this->bookmarks[$bookmark->getId()])) { throw new BookmarkNotFoundException(); } @@ -241,7 +274,7 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function exists($id, $visibility = null) + public function exists(int $id, string $visibility = null): bool { if (! isset($this->bookmarks[$id])) { return false; @@ -252,7 +285,8 @@ class BookmarkFileService implements BookmarkServiceInterface } $bookmark = $this->bookmarks[$id]; - if (($bookmark->isPrivate() && $visibility != 'all' && $visibility != 'private') + if ( + ($bookmark->isPrivate() && $visibility != 'all' && $visibility != 'private') || (! $bookmark->isPrivate() && $visibility != 'all' && $visibility != 'public') ) { return false; @@ -264,20 +298,21 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function count($visibility = null) + public function count(string $visibility = null): int { - return count($this->search([], $visibility)); + return $this->search([], $visibility)->getResultCount(); } /** * @inheritDoc */ - public function save() + public function save(): void { - if (!$this->isLoggedIn) { + if (true !== $this->isLoggedIn) { // TODO: raise an Exception instead die('You are not authorized to change the database.'); } + $this->bookmarks->reorder(); $this->bookmarksIO->write($this->bookmarks); $this->pageCacheManager->invalidateCaches(); @@ -286,14 +321,15 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function bookmarksCountPerTag($filteringTags = [], $visibility = null) + public function bookmarksCountPerTag(array $filteringTags = [], string $visibility = null): array { - $bookmarks = $this->search(['searchtags' => $filteringTags], $visibility); + $searchResult = $this->search(['searchtags' => $filteringTags], $visibility); $tags = []; $caseMapping = []; - foreach ($bookmarks as $bookmark) { + foreach ($searchResult->getBookmarks() as $bookmark) { foreach ($bookmark->getTags() as $tag) { - if (empty($tag) + if ( + empty($tag) || (! $this->isLoggedIn && startsWith($tag, '.')) || $tag === BookmarkMarkdownFormatter::NO_MD_TAG || in_array($tag, $filteringTags, true) @@ -322,45 +358,68 @@ class BookmarkFileService implements BookmarkServiceInterface $keys = array_keys($tags); $tmpTags = array_combine($keys, $keys); array_multisort($tags, SORT_DESC, $tmpTags, SORT_ASC, $tags); + return $tags; } /** * @inheritDoc */ - public function days() - { - $bookmarkDays = []; - foreach ($this->search() as $bookmark) { - $bookmarkDays[$bookmark->getCreated()->format('Ymd')] = 0; + public function findByDate( + \DateTimeInterface $from, + \DateTimeInterface $to, + ?\DateTimeInterface &$previous, + ?\DateTimeInterface &$next + ): array { + $out = []; + $previous = null; + $next = null; + + foreach ($this->search([], null, false, false, true)->getBookmarks() as $bookmark) { + if ($to < $bookmark->getCreated()) { + $next = $bookmark->getCreated(); + } elseif ($from < $bookmark->getCreated() && $to > $bookmark->getCreated()) { + $out[] = $bookmark; + } else { + if ($previous !== null) { + break; + } + $previous = $bookmark->getCreated(); + } } - $bookmarkDays = array_keys($bookmarkDays); - sort($bookmarkDays); - return $bookmarkDays; + return $out; } /** * @inheritDoc */ - public function filterDay($request) + public function getLatest(): ?Bookmark { - return $this->bookmarkFilter->filter(BookmarkFilter::$FILTER_DAY, $request); + foreach ($this->search([], null, false, false, true)->getBookmarks() as $bookmark) { + return $bookmark; + } + + return null; } /** * @inheritDoc */ - public function initialize() + public function initialize(): void { $initializer = new BookmarkInitializer($this); $initializer->initialize(); + + if (true === $this->isLoggedIn) { + $this->save(); + } } /** * Handles migration to the new database format (BookmarksArray). */ - protected function migrate() + protected function migrate(): void { $bookmarkDb = new LegacyLinkDB( $this->conf->get('resource.datastore'), @@ -368,14 +427,14 @@ class BookmarkFileService implements BookmarkServiceInterface false ); $updater = new LegacyUpdater( - UpdaterUtils::read_updates_file($this->conf->get('resource.updates')), + UpdaterUtils::readUpdatesFile($this->conf->get('resource.updates')), $bookmarkDb, $this->conf, true ); $newUpdates = $updater->update(); if (! empty($newUpdates)) { - UpdaterUtils::write_updates_file( + UpdaterUtils::writeUpdatesFile( $this->conf->get('resource.updates'), $updater->getDoneUpdates() );