aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/bookmark/Bookmark.php
diff options
context:
space:
mode:
Diffstat (limited to 'application/bookmark/Bookmark.php')
-rw-r--r--application/bookmark/Bookmark.php202
1 files changed, 134 insertions, 68 deletions
diff --git a/application/bookmark/Bookmark.php b/application/bookmark/Bookmark.php
index f9b21d3d..4810c5e6 100644
--- a/application/bookmark/Bookmark.php
+++ b/application/bookmark/Bookmark.php
@@ -1,8 +1,11 @@
1<?php 1<?php
2 2
3declare(strict_types=1);
4
3namespace Shaarli\Bookmark; 5namespace Shaarli\Bookmark;
4 6
5use DateTime; 7use DateTime;
8use DateTimeInterface;
6use Shaarli\Bookmark\Exception\InvalidBookmarkException; 9use Shaarli\Bookmark\Exception\InvalidBookmarkException;
7 10
8/** 11/**
@@ -36,21 +39,24 @@ class Bookmark
36 /** @var array List of bookmark's tags */ 39 /** @var array List of bookmark's tags */
37 protected $tags; 40 protected $tags;
38 41
39 /** @var string Thumbnail's URL - false if no thumbnail could be found */ 42 /** @var string|bool|null Thumbnail's URL - initialized at null, false if no thumbnail could be found */
40 protected $thumbnail; 43 protected $thumbnail;
41 44
42 /** @var bool Set to true if the bookmark is set as sticky */ 45 /** @var bool Set to true if the bookmark is set as sticky */
43 protected $sticky; 46 protected $sticky;
44 47
45 /** @var DateTime Creation datetime */ 48 /** @var DateTimeInterface Creation datetime */
46 protected $created; 49 protected $created;
47 50
48 /** @var DateTime Update datetime */ 51 /** @var DateTimeInterface datetime */
49 protected $updated; 52 protected $updated;
50 53
51 /** @var bool True if the bookmark can only be seen while logged in */ 54 /** @var bool True if the bookmark can only be seen while logged in */
52 protected $private; 55 protected $private;
53 56
57 /** @var mixed[] Available to store any additional content for a bookmark. Currently used for search highlight. */
58 protected $additionalContent = [];
59
54 /** 60 /**
55 * Initialize a link from array data. Especially useful to create a Bookmark from former link storage format. 61 * Initialize a link from array data. Especially useful to create a Bookmark from former link storage format.
56 * 62 *
@@ -58,25 +64,25 @@ class Bookmark
58 * 64 *
59 * @return $this 65 * @return $this
60 */ 66 */
61 public function fromArray($data) 67 public function fromArray(array $data): Bookmark
62 { 68 {
63 $this->id = $data['id']; 69 $this->id = $data['id'] ?? null;
64 $this->shortUrl = $data['shorturl']; 70 $this->shortUrl = $data['shorturl'] ?? null;
65 $this->url = $data['url']; 71 $this->url = $data['url'] ?? null;
66 $this->title = $data['title']; 72 $this->title = $data['title'] ?? null;
67 $this->description = $data['description']; 73 $this->description = $data['description'] ?? null;
68 $this->thumbnail = isset($data['thumbnail']) ? $data['thumbnail'] : null; 74 $this->thumbnail = $data['thumbnail'] ?? null;
69 $this->sticky = isset($data['sticky']) ? $data['sticky'] : false; 75 $this->sticky = $data['sticky'] ?? false;
70 $this->created = $data['created']; 76 $this->created = $data['created'] ?? null;
71 if (is_array($data['tags'])) { 77 if (is_array($data['tags'])) {
72 $this->tags = $data['tags']; 78 $this->tags = $data['tags'];
73 } else { 79 } else {
74 $this->tags = preg_split('/\s+/', $data['tags'], -1, PREG_SPLIT_NO_EMPTY); 80 $this->tags = preg_split('/\s+/', $data['tags'] ?? '', -1, PREG_SPLIT_NO_EMPTY);
75 } 81 }
76 if (! empty($data['updated'])) { 82 if (! empty($data['updated'])) {
77 $this->updated = $data['updated']; 83 $this->updated = $data['updated'];
78 } 84 }
79 $this->private = $data['private'] ? true : false; 85 $this->private = ($data['private'] ?? false) ? true : false;
80 86
81 return $this; 87 return $this;
82 } 88 }
@@ -92,24 +98,28 @@ class Bookmark
92 * - the URL with the permalink 98 * - the URL with the permalink
93 * - the title with the URL 99 * - the title with the URL
94 * 100 *
101 * Also make sure that we do not save search highlights in the datastore.
102 *
95 * @throws InvalidBookmarkException 103 * @throws InvalidBookmarkException
96 */ 104 */
97 public function validate() 105 public function validate(): void
98 { 106 {
99 if ($this->id === null 107 if ($this->id === null
100 || ! is_int($this->id) 108 || ! is_int($this->id)
101 || empty($this->shortUrl) 109 || empty($this->shortUrl)
102 || empty($this->created) 110 || empty($this->created)
103 || ! $this->created instanceof DateTime
104 ) { 111 ) {
105 throw new InvalidBookmarkException($this); 112 throw new InvalidBookmarkException($this);
106 } 113 }
107 if (empty($this->url)) { 114 if (empty($this->url)) {
108 $this->url = '?'. $this->shortUrl; 115 $this->url = '/shaare/'. $this->shortUrl;
109 } 116 }
110 if (empty($this->title)) { 117 if (empty($this->title)) {
111 $this->title = $this->url; 118 $this->title = $this->url;
112 } 119 }
120 if (array_key_exists('search_highlight', $this->additionalContent)) {
121 unset($this->additionalContent['search_highlight']);
122 }
113 } 123 }
114 124
115 /** 125 /**
@@ -118,11 +128,11 @@ class Bookmark
118 * - created: with the current datetime 128 * - created: with the current datetime
119 * - shortUrl: with a generated small hash from the date and the given ID 129 * - shortUrl: with a generated small hash from the date and the given ID
120 * 130 *
121 * @param int $id 131 * @param int|null $id
122 * 132 *
123 * @return Bookmark 133 * @return Bookmark
124 */ 134 */
125 public function setId($id) 135 public function setId(?int $id): Bookmark
126 { 136 {
127 $this->id = $id; 137 $this->id = $id;
128 if (empty($this->created)) { 138 if (empty($this->created)) {
@@ -138,9 +148,9 @@ class Bookmark
138 /** 148 /**
139 * Get the Id. 149 * Get the Id.
140 * 150 *
141 * @return int 151 * @return int|null
142 */ 152 */
143 public function getId() 153 public function getId(): ?int
144 { 154 {
145 return $this->id; 155 return $this->id;
146 } 156 }
@@ -148,9 +158,9 @@ class Bookmark
148 /** 158 /**
149 * Get the ShortUrl. 159 * Get the ShortUrl.
150 * 160 *
151 * @return string 161 * @return string|null
152 */ 162 */
153 public function getShortUrl() 163 public function getShortUrl(): ?string
154 { 164 {
155 return $this->shortUrl; 165 return $this->shortUrl;
156 } 166 }
@@ -158,9 +168,9 @@ class Bookmark
158 /** 168 /**
159 * Get the Url. 169 * Get the Url.
160 * 170 *
161 * @return string 171 * @return string|null
162 */ 172 */
163 public function getUrl() 173 public function getUrl(): ?string
164 { 174 {
165 return $this->url; 175 return $this->url;
166 } 176 }
@@ -170,7 +180,7 @@ class Bookmark
170 * 180 *
171 * @return string 181 * @return string
172 */ 182 */
173 public function getTitle() 183 public function getTitle(): ?string
174 { 184 {
175 return $this->title; 185 return $this->title;
176 } 186 }
@@ -180,7 +190,7 @@ class Bookmark
180 * 190 *
181 * @return string 191 * @return string
182 */ 192 */
183 public function getDescription() 193 public function getDescription(): string
184 { 194 {
185 return ! empty($this->description) ? $this->description : ''; 195 return ! empty($this->description) ? $this->description : '';
186 } 196 }
@@ -188,9 +198,9 @@ class Bookmark
188 /** 198 /**
189 * Get the Created. 199 * Get the Created.
190 * 200 *
191 * @return DateTime 201 * @return DateTimeInterface
192 */ 202 */
193 public function getCreated() 203 public function getCreated(): ?DateTimeInterface
194 { 204 {
195 return $this->created; 205 return $this->created;
196 } 206 }
@@ -198,9 +208,9 @@ class Bookmark
198 /** 208 /**
199 * Get the Updated. 209 * Get the Updated.
200 * 210 *
201 * @return DateTime 211 * @return DateTimeInterface
202 */ 212 */
203 public function getUpdated() 213 public function getUpdated(): ?DateTimeInterface
204 { 214 {
205 return $this->updated; 215 return $this->updated;
206 } 216 }
@@ -208,11 +218,11 @@ class Bookmark
208 /** 218 /**
209 * Set the ShortUrl. 219 * Set the ShortUrl.
210 * 220 *
211 * @param string $shortUrl 221 * @param string|null $shortUrl
212 * 222 *
213 * @return Bookmark 223 * @return Bookmark
214 */ 224 */
215 public function setShortUrl($shortUrl) 225 public function setShortUrl(?string $shortUrl): Bookmark
216 { 226 {
217 $this->shortUrl = $shortUrl; 227 $this->shortUrl = $shortUrl;
218 228
@@ -222,14 +232,14 @@ class Bookmark
222 /** 232 /**
223 * Set the Url. 233 * Set the Url.
224 * 234 *
225 * @param string $url 235 * @param string|null $url
226 * @param array $allowedProtocols 236 * @param string[] $allowedProtocols
227 * 237 *
228 * @return Bookmark 238 * @return Bookmark
229 */ 239 */
230 public function setUrl($url, $allowedProtocols = []) 240 public function setUrl(?string $url, array $allowedProtocols = []): Bookmark
231 { 241 {
232 $url = trim($url); 242 $url = $url !== null ? trim($url) : '';
233 if (! empty($url)) { 243 if (! empty($url)) {
234 $url = whitelist_protocols($url, $allowedProtocols); 244 $url = whitelist_protocols($url, $allowedProtocols);
235 } 245 }
@@ -241,13 +251,13 @@ class Bookmark
241 /** 251 /**
242 * Set the Title. 252 * Set the Title.
243 * 253 *
244 * @param string $title 254 * @param string|null $title
245 * 255 *
246 * @return Bookmark 256 * @return Bookmark
247 */ 257 */
248 public function setTitle($title) 258 public function setTitle(?string $title): Bookmark
249 { 259 {
250 $this->title = trim($title); 260 $this->title = $title !== null ? trim($title) : '';
251 261
252 return $this; 262 return $this;
253 } 263 }
@@ -255,11 +265,11 @@ class Bookmark
255 /** 265 /**
256 * Set the Description. 266 * Set the Description.
257 * 267 *
258 * @param string $description 268 * @param string|null $description
259 * 269 *
260 * @return Bookmark 270 * @return Bookmark
261 */ 271 */
262 public function setDescription($description) 272 public function setDescription(?string $description): Bookmark
263 { 273 {
264 $this->description = $description; 274 $this->description = $description;
265 275
@@ -270,11 +280,11 @@ class Bookmark
270 * Set the Created. 280 * Set the Created.
271 * Note: you shouldn't set this manually except for special cases (like bookmark import) 281 * Note: you shouldn't set this manually except for special cases (like bookmark import)
272 * 282 *
273 * @param DateTime $created 283 * @param DateTimeInterface|null $created
274 * 284 *
275 * @return Bookmark 285 * @return Bookmark
276 */ 286 */
277 public function setCreated($created) 287 public function setCreated(?DateTimeInterface $created): Bookmark
278 { 288 {
279 $this->created = $created; 289 $this->created = $created;
280 290
@@ -284,11 +294,11 @@ class Bookmark
284 /** 294 /**
285 * Set the Updated. 295 * Set the Updated.
286 * 296 *
287 * @param DateTime $updated 297 * @param DateTimeInterface|null $updated
288 * 298 *
289 * @return Bookmark 299 * @return Bookmark
290 */ 300 */
291 public function setUpdated($updated) 301 public function setUpdated(?DateTimeInterface $updated): Bookmark
292 { 302 {
293 $this->updated = $updated; 303 $this->updated = $updated;
294 304
@@ -300,7 +310,7 @@ class Bookmark
300 * 310 *
301 * @return bool 311 * @return bool
302 */ 312 */
303 public function isPrivate() 313 public function isPrivate(): bool
304 { 314 {
305 return $this->private ? true : false; 315 return $this->private ? true : false;
306 } 316 }
@@ -308,11 +318,11 @@ class Bookmark
308 /** 318 /**
309 * Set the Private. 319 * Set the Private.
310 * 320 *
311 * @param bool $private 321 * @param bool|null $private
312 * 322 *
313 * @return Bookmark 323 * @return Bookmark
314 */ 324 */
315 public function setPrivate($private) 325 public function setPrivate(?bool $private): Bookmark
316 { 326 {
317 $this->private = $private ? true : false; 327 $this->private = $private ? true : false;
318 328
@@ -322,9 +332,9 @@ class Bookmark
322 /** 332 /**
323 * Get the Tags. 333 * Get the Tags.
324 * 334 *
325 * @return array 335 * @return string[]
326 */ 336 */
327 public function getTags() 337 public function getTags(): array
328 { 338 {
329 return is_array($this->tags) ? $this->tags : []; 339 return is_array($this->tags) ? $this->tags : [];
330 } 340 }
@@ -332,13 +342,13 @@ class Bookmark
332 /** 342 /**
333 * Set the Tags. 343 * Set the Tags.
334 * 344 *
335 * @param array $tags 345 * @param string[]|null $tags
336 * 346 *
337 * @return Bookmark 347 * @return Bookmark
338 */ 348 */
339 public function setTags($tags) 349 public function setTags(?array $tags): Bookmark
340 { 350 {
341 $this->setTagsString(implode(' ', $tags)); 351 $this->setTagsString(implode(' ', $tags ?? []));
342 352
343 return $this; 353 return $this;
344 } 354 }
@@ -346,7 +356,7 @@ class Bookmark
346 /** 356 /**
347 * Get the Thumbnail. 357 * Get the Thumbnail.
348 * 358 *
349 * @return string|bool 359 * @return string|bool|null Thumbnail's URL - initialized at null, false if no thumbnail could be found
350 */ 360 */
351 public function getThumbnail() 361 public function getThumbnail()
352 { 362 {
@@ -356,11 +366,11 @@ class Bookmark
356 /** 366 /**
357 * Set the Thumbnail. 367 * Set the Thumbnail.
358 * 368 *
359 * @param string|bool $thumbnail 369 * @param string|bool|null $thumbnail Thumbnail's URL - false if no thumbnail could be found
360 * 370 *
361 * @return Bookmark 371 * @return Bookmark
362 */ 372 */
363 public function setThumbnail($thumbnail) 373 public function setThumbnail($thumbnail): Bookmark
364 { 374 {
365 $this->thumbnail = $thumbnail; 375 $this->thumbnail = $thumbnail;
366 376
@@ -368,11 +378,29 @@ class Bookmark
368 } 378 }
369 379
370 /** 380 /**
381 * Return true if:
382 * - the bookmark's thumbnail is not already set to false (= not found)
383 * - it's not a note
384 * - it's an HTTP(S) link
385 * - the thumbnail has not yet be retrieved (null) or its associated cache file doesn't exist anymore
386 *
387 * @return bool True if the bookmark's thumbnail needs to be retrieved.
388 */
389 public function shouldUpdateThumbnail(): bool
390 {
391 return $this->thumbnail !== false
392 && !$this->isNote()
393 && startsWith(strtolower($this->url), 'http')
394 && (null === $this->thumbnail || !is_file($this->thumbnail))
395 ;
396 }
397
398 /**
371 * Get the Sticky. 399 * Get the Sticky.
372 * 400 *
373 * @return bool 401 * @return bool
374 */ 402 */
375 public function isSticky() 403 public function isSticky(): bool
376 { 404 {
377 return $this->sticky ? true : false; 405 return $this->sticky ? true : false;
378 } 406 }
@@ -380,11 +408,11 @@ class Bookmark
380 /** 408 /**
381 * Set the Sticky. 409 * Set the Sticky.
382 * 410 *
383 * @param bool $sticky 411 * @param bool|null $sticky
384 * 412 *
385 * @return Bookmark 413 * @return Bookmark
386 */ 414 */
387 public function setSticky($sticky) 415 public function setSticky(?bool $sticky): Bookmark
388 { 416 {
389 $this->sticky = $sticky ? true : false; 417 $this->sticky = $sticky ? true : false;
390 418
@@ -394,7 +422,7 @@ class Bookmark
394 /** 422 /**
395 * @return string Bookmark's tags as a string, separated by a space 423 * @return string Bookmark's tags as a string, separated by a space
396 */ 424 */
397 public function getTagsString() 425 public function getTagsString(): string
398 { 426 {
399 return implode(' ', $this->getTags()); 427 return implode(' ', $this->getTags());
400 } 428 }
@@ -402,10 +430,10 @@ class Bookmark
402 /** 430 /**
403 * @return bool 431 * @return bool
404 */ 432 */
405 public function isNote() 433 public function isNote(): bool
406 { 434 {
407 // We check empty value to get a valid result if the link has not been saved yet 435 // We check empty value to get a valid result if the link has not been saved yet
408 return empty($this->url) || $this->url[0] === '?'; 436 return empty($this->url) || startsWith($this->url, '/shaare/') || $this->url[0] === '?';
409 } 437 }
410 438
411 /** 439 /**
@@ -415,14 +443,14 @@ class Bookmark
415 * - multiple spaces will be removed 443 * - multiple spaces will be removed
416 * - trailing dash in tags will be removed 444 * - trailing dash in tags will be removed
417 * 445 *
418 * @param string $tags 446 * @param string|null $tags
419 * 447 *
420 * @return $this 448 * @return $this
421 */ 449 */
422 public function setTagsString($tags) 450 public function setTagsString(?string $tags): Bookmark
423 { 451 {
424 // Remove first '-' char in tags. 452 // Remove first '-' char in tags.
425 $tags = preg_replace('/(^| )\-/', '$1', $tags); 453 $tags = preg_replace('/(^| )\-/', '$1', $tags ?? '');
426 // Explode all tags separted by spaces or commas 454 // Explode all tags separted by spaces or commas
427 $tags = preg_split('/[\s,]+/', $tags); 455 $tags = preg_split('/[\s,]+/', $tags);
428 // Remove eventual empty values 456 // Remove eventual empty values
@@ -434,12 +462,50 @@ class Bookmark
434 } 462 }
435 463
436 /** 464 /**
465 * Get entire additionalContent array.
466 *
467 * @return mixed[]
468 */
469 public function getAdditionalContent(): array
470 {
471 return $this->additionalContent;
472 }
473
474 /**
475 * Set a single entry in additionalContent, by key.
476 *
477 * @param string $key
478 * @param mixed|null $value Any type of value can be set.
479 *
480 * @return $this
481 */
482 public function addAdditionalContentEntry(string $key, $value): self
483 {
484 $this->additionalContent[$key] = $value;
485
486 return $this;
487 }
488
489 /**
490 * Get a single entry in additionalContent, by key.
491 *
492 * @param string $key
493 * @param mixed|null $default
494 *
495 * @return mixed|null can be any type or even null.
496 */
497 public function getAdditionalContentEntry(string $key, $default = null)
498 {
499 return array_key_exists($key, $this->additionalContent) ? $this->additionalContent[$key] : $default;
500 }
501
502 /**
437 * Rename a tag in tags list. 503 * Rename a tag in tags list.
438 * 504 *
439 * @param string $fromTag 505 * @param string $fromTag
440 * @param string $toTag 506 * @param string $toTag
441 */ 507 */
442 public function renameTag($fromTag, $toTag) 508 public function renameTag(string $fromTag, string $toTag): void
443 { 509 {
444 if (($pos = array_search($fromTag, $this->tags)) !== false) { 510 if (($pos = array_search($fromTag, $this->tags)) !== false) {
445 $this->tags[$pos] = trim($toTag); 511 $this->tags[$pos] = trim($toTag);
@@ -451,7 +517,7 @@ class Bookmark
451 * 517 *
452 * @param string $tag 518 * @param string $tag
453 */ 519 */
454 public function deleteTag($tag) 520 public function deleteTag(string $tag): void
455 { 521 {
456 if (($pos = array_search($tag, $this->tags)) !== false) { 522 if (($pos = array_search($tag, $this->tags)) !== false) {
457 unset($this->tags[$pos]); 523 unset($this->tags[$pos]);