]> git.immae.eu Git - github/shaarli/Shaarli.git/blobdiff - application/bookmark/BookmarkFileService.php
Feature: support any tag separator
[github/shaarli/Shaarli.git] / application / bookmark / BookmarkFileService.php
index e3a611461a7aae9ecd3f0070a3341aa872aa737d..85efeea60097860bf96066e4c712a35c3d1d20d6 100644 (file)
@@ -1,10 +1,12 @@
 <?php
 
+declare(strict_types=1);
 
 namespace Shaarli\Bookmark;
 
-
+use DateTime;
 use Exception;
+use malkusch\lock\mutex\Mutex;
 use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
 use Shaarli\Bookmark\Exception\DatastoreNotInitializedException;
 use Shaarli\Bookmark\Exception\EmptyDataStoreException;
@@ -47,15 +49,19 @@ class BookmarkFileService implements BookmarkServiceInterface
     /** @var bool true for logged in users. Default value to retrieve private bookmarks. */
     protected $isLoggedIn;
 
+    /** @var Mutex */
+    protected $mutex;
+
     /**
      * @inheritDoc
      */
-    public function __construct(ConfigManager $conf, History $history, $isLoggedIn)
+    public function __construct(ConfigManager $conf, History $history, Mutex $mutex, bool $isLoggedIn)
     {
         $this->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)) {
@@ -85,19 +91,22 @@ class BookmarkFileService implements BookmarkServiceInterface
             }
         }
 
-        $this->bookmarkFilter = new BookmarkFilter($this->bookmarks);
+        $this->bookmarkFilter = new BookmarkFilter($this->bookmarks, $this->conf);
     }
 
     /**
      * @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 $first;
@@ -106,7 +115,7 @@ class BookmarkFileService implements BookmarkServiceInterface
     /**
      * @inheritDoc
      */
-    public function findByUrl($url)
+    public function findByUrl(string $url): ?Bookmark
     {
         return $this->bookmarks->getByUrl($url);
     }
@@ -115,10 +124,10 @@ class BookmarkFileService implements BookmarkServiceInterface
      * @inheritDoc
      */
     public function search(
-        $request = [],
-        $visibility = null,
-        $caseSensitive = false,
-        $untaggedOnly = false,
+        array $request = [],
+        string $visibility = null,
+        bool $caseSensitive = false,
+        bool $untaggedOnly = false,
         bool $ignoreSticky = false
     ) {
         if ($visibility === null) {
@@ -126,8 +135,8 @@ class BookmarkFileService implements BookmarkServiceInterface
         }
 
         // 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);
@@ -135,7 +144,7 @@ class BookmarkFileService implements BookmarkServiceInterface
 
         return $this->bookmarkFilter->filter(
             BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
-            [$searchtags, $searchterm],
+            [$searchTags, $searchTerm],
             $caseSensitive,
             $visibility,
             $untaggedOnly
@@ -145,7 +154,7 @@ class BookmarkFileService implements BookmarkServiceInterface
     /**
      * @inheritDoc
      */
-    public function get($id, $visibility = null)
+    public function get(int $id, string $visibility = null): Bookmark
     {
         if (! isset($this->bookmarks[$id])) {
             throw new BookmarkNotFoundException();
@@ -168,20 +177,17 @@ class BookmarkFileService implements BookmarkServiceInterface
     /**
      * @inheritDoc
      */
-    public function set($bookmark, $save = true)
+    public function set(Bookmark $bookmark, bool $save = true): Bookmark
     {
         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();
@@ -193,15 +199,12 @@ class BookmarkFileService implements BookmarkServiceInterface
     /**
      * @inheritDoc
      */
-    public function add($bookmark, $save = true)
+    public function add(Bookmark $bookmark, bool $save = true): Bookmark
     {
         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());
@@ -218,14 +221,11 @@ class BookmarkFileService implements BookmarkServiceInterface
     /**
      * @inheritDoc
      */
-    public function addOrSet($bookmark, $save = true)
+    public function addOrSet(Bookmark $bookmark, bool $save = true): Bookmark
     {
         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);
         }
@@ -235,14 +235,11 @@ class BookmarkFileService implements BookmarkServiceInterface
     /**
      * @inheritDoc
      */
-    public function remove($bookmark, $save = true)
+    public function remove(Bookmark $bookmark, bool $save = true): void
     {
         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();
         }
@@ -257,7 +254,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;
@@ -280,7 +277,7 @@ class BookmarkFileService implements BookmarkServiceInterface
     /**
      * @inheritDoc
      */
-    public function count($visibility = null)
+    public function count(string $visibility = null): int
     {
         return count($this->search([], $visibility));
     }
@@ -288,7 +285,7 @@ class BookmarkFileService implements BookmarkServiceInterface
     /**
      * @inheritDoc
      */
-    public function save()
+    public function save(): void
     {
         if (true !== $this->isLoggedIn) {
             // TODO: raise an Exception instead
@@ -303,7 +300,7 @@ 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);
         $tags = [];
@@ -339,36 +336,55 @@ 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) as $bookmark) {
+            if ($to < $bookmark->getCreated()) {
+                $next = $bookmark->getCreated();
+            } else if ($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) as $bookmark) {
+            return $bookmark;
+        }
+
+        return null;
     }
 
     /**
      * @inheritDoc
      */
-    public function initialize()
+    public function initialize(): void
     {
         $initializer = new BookmarkInitializer($this);
         $initializer->initialize();
@@ -381,7 +397,7 @@ class BookmarkFileService implements BookmarkServiceInterface
     /**
      * Handles migration to the new database format (BookmarksArray).
      */
-    protected function migrate()
+    protected function migrate(): void
     {
         $bookmarkDb = new LegacyLinkDB(
             $this->conf->get('resource.datastore'),