]> git.immae.eu Git - github/shaarli/Shaarli.git/blame - application/bookmark/Bookmark.php
Merge pull request #1596 from ArthurHoaro/feature/better-rename-tag
[github/shaarli/Shaarli.git] / application / bookmark / Bookmark.php
CommitLineData
336a28fa
A
1<?php
2
efb7d21b
A
3declare(strict_types=1);
4
336a28fa
A
5namespace Shaarli\Bookmark;
6
7use DateTime;
c4d5be53 8use DateTimeInterface;
336a28fa
A
9use Shaarli\Bookmark\Exception\InvalidBookmarkException;
10
11/**
12 * Class Bookmark
13 *
14 * This class represent a single Bookmark with all its attributes.
15 * Every bookmark should manipulated using this, before being formatted.
16 *
17 * @package Shaarli\Bookmark
18 */
19class Bookmark
20{
21 /** @var string Date format used in string (former ID format) */
22 const LINK_DATE_FORMAT = 'Ymd_His';
23
24 /** @var int Bookmark ID */
25 protected $id;
26
27 /** @var string Permalink identifier */
28 protected $shortUrl;
29
30 /** @var string Bookmark's URL - $shortUrl prefixed with `?` for notes */
31 protected $url;
32
33 /** @var string Bookmark's title */
34 protected $title;
35
36 /** @var string Raw bookmark's description */
37 protected $description;
38
39 /** @var array List of bookmark's tags */
40 protected $tags;
41
1a68ae5a 42 /** @var string|bool|null Thumbnail's URL - initialized at null, false if no thumbnail could be found */
336a28fa
A
43 protected $thumbnail;
44
45 /** @var bool Set to true if the bookmark is set as sticky */
46 protected $sticky;
47
c4d5be53 48 /** @var DateTimeInterface Creation datetime */
336a28fa
A
49 protected $created;
50
c4d5be53 51 /** @var DateTimeInterface datetime */
336a28fa
A
52 protected $updated;
53
54 /** @var bool True if the bookmark can only be seen while logged in */
55 protected $private;
56
57 /**
58 * Initialize a link from array data. Especially useful to create a Bookmark from former link storage format.
59 *
60 * @param array $data
61 *
62 * @return $this
63 */
efb7d21b 64 public function fromArray(array $data): Bookmark
336a28fa 65 {
efb7d21b
A
66 $this->id = $data['id'] ?? null;
67 $this->shortUrl = $data['shorturl'] ?? null;
68 $this->url = $data['url'] ?? null;
69 $this->title = $data['title'] ?? null;
70 $this->description = $data['description'] ?? null;
71 $this->thumbnail = $data['thumbnail'] ?? null;
72 $this->sticky = $data['sticky'] ?? false;
73 $this->created = $data['created'] ?? null;
336a28fa
A
74 if (is_array($data['tags'])) {
75 $this->tags = $data['tags'];
76 } else {
efb7d21b 77 $this->tags = preg_split('/\s+/', $data['tags'] ?? '', -1, PREG_SPLIT_NO_EMPTY);
336a28fa
A
78 }
79 if (! empty($data['updated'])) {
80 $this->updated = $data['updated'];
81 }
efb7d21b 82 $this->private = ($data['private'] ?? false) ? true : false;
336a28fa
A
83
84 return $this;
85 }
86
87 /**
88 * Make sure that the current instance of Bookmark is valid and can be saved into the data store.
89 * A valid link requires:
90 * - an integer ID
91 * - a short URL (for permalinks)
92 * - a creation date
93 *
94 * This function also initialize optional empty fields:
95 * - the URL with the permalink
96 * - the title with the URL
97 *
98 * @throws InvalidBookmarkException
99 */
efb7d21b 100 public function validate(): void
336a28fa
A
101 {
102 if ($this->id === null
103 || ! is_int($this->id)
104 || empty($this->shortUrl)
105 || empty($this->created)
336a28fa
A
106 ) {
107 throw new InvalidBookmarkException($this);
108 }
109 if (empty($this->url)) {
301c7ab1 110 $this->url = '/shaare/'. $this->shortUrl;
336a28fa
A
111 }
112 if (empty($this->title)) {
113 $this->title = $this->url;
114 }
115 }
116
117 /**
118 * Set the Id.
119 * If they're not already initialized, this function also set:
120 * - created: with the current datetime
121 * - shortUrl: with a generated small hash from the date and the given ID
122 *
efb7d21b 123 * @param int|null $id
336a28fa
A
124 *
125 * @return Bookmark
126 */
efb7d21b 127 public function setId(?int $id): Bookmark
336a28fa
A
128 {
129 $this->id = $id;
130 if (empty($this->created)) {
131 $this->created = new DateTime();
132 }
133 if (empty($this->shortUrl)) {
134 $this->shortUrl = link_small_hash($this->created, $this->id);
135 }
136
137 return $this;
138 }
139
140 /**
141 * Get the Id.
142 *
efb7d21b 143 * @return int|null
336a28fa 144 */
efb7d21b 145 public function getId(): ?int
336a28fa
A
146 {
147 return $this->id;
148 }
149
150 /**
151 * Get the ShortUrl.
152 *
efb7d21b 153 * @return string|null
336a28fa 154 */
efb7d21b 155 public function getShortUrl(): ?string
336a28fa
A
156 {
157 return $this->shortUrl;
158 }
159
160 /**
161 * Get the Url.
162 *
efb7d21b 163 * @return string|null
336a28fa 164 */
efb7d21b 165 public function getUrl(): ?string
336a28fa
A
166 {
167 return $this->url;
168 }
169
170 /**
171 * Get the Title.
172 *
173 * @return string
174 */
efb7d21b 175 public function getTitle(): ?string
336a28fa
A
176 {
177 return $this->title;
178 }
179
180 /**
181 * Get the Description.
182 *
183 * @return string
184 */
efb7d21b 185 public function getDescription(): string
336a28fa
A
186 {
187 return ! empty($this->description) ? $this->description : '';
188 }
189
190 /**
191 * Get the Created.
192 *
c4d5be53 193 * @return DateTimeInterface
336a28fa 194 */
efb7d21b 195 public function getCreated(): ?DateTimeInterface
336a28fa
A
196 {
197 return $this->created;
198 }
199
200 /**
201 * Get the Updated.
202 *
c4d5be53 203 * @return DateTimeInterface
336a28fa 204 */
efb7d21b 205 public function getUpdated(): ?DateTimeInterface
336a28fa
A
206 {
207 return $this->updated;
208 }
209
210 /**
211 * Set the ShortUrl.
212 *
efb7d21b 213 * @param string|null $shortUrl
336a28fa
A
214 *
215 * @return Bookmark
216 */
efb7d21b 217 public function setShortUrl(?string $shortUrl): Bookmark
336a28fa
A
218 {
219 $this->shortUrl = $shortUrl;
220
221 return $this;
222 }
223
224 /**
225 * Set the Url.
226 *
efb7d21b
A
227 * @param string|null $url
228 * @param string[] $allowedProtocols
336a28fa
A
229 *
230 * @return Bookmark
231 */
efb7d21b 232 public function setUrl(?string $url, array $allowedProtocols = []): Bookmark
336a28fa 233 {
efb7d21b 234 $url = $url !== null ? trim($url) : '';
336a28fa
A
235 if (! empty($url)) {
236 $url = whitelist_protocols($url, $allowedProtocols);
237 }
238 $this->url = $url;
239
240 return $this;
241 }
242
243 /**
244 * Set the Title.
245 *
efb7d21b 246 * @param string|null $title
336a28fa
A
247 *
248 * @return Bookmark
249 */
efb7d21b 250 public function setTitle(?string $title): Bookmark
336a28fa 251 {
efb7d21b 252 $this->title = $title !== null ? trim($title) : '';
336a28fa
A
253
254 return $this;
255 }
256
257 /**
258 * Set the Description.
259 *
efb7d21b 260 * @param string|null $description
336a28fa
A
261 *
262 * @return Bookmark
263 */
efb7d21b 264 public function setDescription(?string $description): Bookmark
336a28fa
A
265 {
266 $this->description = $description;
267
268 return $this;
269 }
270
271 /**
272 * Set the Created.
273 * Note: you shouldn't set this manually except for special cases (like bookmark import)
274 *
efb7d21b 275 * @param DateTimeInterface|null $created
336a28fa
A
276 *
277 * @return Bookmark
278 */
efb7d21b 279 public function setCreated(?DateTimeInterface $created): Bookmark
336a28fa
A
280 {
281 $this->created = $created;
282
283 return $this;
284 }
285
286 /**
287 * Set the Updated.
288 *
efb7d21b 289 * @param DateTimeInterface|null $updated
336a28fa
A
290 *
291 * @return Bookmark
292 */
efb7d21b 293 public function setUpdated(?DateTimeInterface $updated): Bookmark
336a28fa
A
294 {
295 $this->updated = $updated;
296
297 return $this;
298 }
299
300 /**
301 * Get the Private.
302 *
303 * @return bool
304 */
efb7d21b 305 public function isPrivate(): bool
336a28fa
A
306 {
307 return $this->private ? true : false;
308 }
309
310 /**
311 * Set the Private.
312 *
efb7d21b 313 * @param bool|null $private
336a28fa
A
314 *
315 * @return Bookmark
316 */
efb7d21b 317 public function setPrivate(?bool $private): Bookmark
336a28fa
A
318 {
319 $this->private = $private ? true : false;
320
321 return $this;
322 }
323
324 /**
325 * Get the Tags.
326 *
efb7d21b 327 * @return string[]
336a28fa 328 */
efb7d21b 329 public function getTags(): array
336a28fa
A
330 {
331 return is_array($this->tags) ? $this->tags : [];
332 }
333
334 /**
335 * Set the Tags.
336 *
efb7d21b 337 * @param string[]|null $tags
336a28fa
A
338 *
339 * @return Bookmark
340 */
efb7d21b 341 public function setTags(?array $tags): Bookmark
336a28fa 342 {
efb7d21b 343 $this->setTagsString(implode(' ', $tags ?? []));
336a28fa
A
344
345 return $this;
346 }
347
348 /**
349 * Get the Thumbnail.
350 *
1a68ae5a 351 * @return string|bool|null Thumbnail's URL - initialized at null, false if no thumbnail could be found
336a28fa
A
352 */
353 public function getThumbnail()
354 {
355 return !$this->isNote() ? $this->thumbnail : false;
356 }
357
358 /**
359 * Set the Thumbnail.
360 *
efb7d21b 361 * @param string|bool|null $thumbnail Thumbnail's URL - false if no thumbnail could be found
336a28fa
A
362 *
363 * @return Bookmark
364 */
efb7d21b 365 public function setThumbnail($thumbnail): Bookmark
336a28fa
A
366 {
367 $this->thumbnail = $thumbnail;
368
369 return $this;
370 }
371
372 /**
373 * Get the Sticky.
374 *
375 * @return bool
376 */
efb7d21b 377 public function isSticky(): bool
336a28fa
A
378 {
379 return $this->sticky ? true : false;
380 }
381
382 /**
383 * Set the Sticky.
384 *
efb7d21b 385 * @param bool|null $sticky
336a28fa
A
386 *
387 * @return Bookmark
388 */
efb7d21b 389 public function setSticky(?bool $sticky): Bookmark
336a28fa
A
390 {
391 $this->sticky = $sticky ? true : false;
392
393 return $this;
394 }
395
396 /**
397 * @return string Bookmark's tags as a string, separated by a space
398 */
efb7d21b 399 public function getTagsString(): string
336a28fa
A
400 {
401 return implode(' ', $this->getTags());
402 }
403
404 /**
405 * @return bool
406 */
efb7d21b 407 public function isNote(): bool
336a28fa
A
408 {
409 // We check empty value to get a valid result if the link has not been saved yet
301c7ab1 410 return empty($this->url) || startsWith($this->url, '/shaare/') || $this->url[0] === '?';
336a28fa
A
411 }
412
413 /**
414 * Set tags from a string.
415 * Note:
416 * - tags must be separated whether by a space or a comma
417 * - multiple spaces will be removed
418 * - trailing dash in tags will be removed
419 *
efb7d21b 420 * @param string|null $tags
336a28fa
A
421 *
422 * @return $this
423 */
efb7d21b 424 public function setTagsString(?string $tags): Bookmark
336a28fa
A
425 {
426 // Remove first '-' char in tags.
efb7d21b 427 $tags = preg_replace('/(^| )\-/', '$1', $tags ?? '');
336a28fa
A
428 // Explode all tags separted by spaces or commas
429 $tags = preg_split('/[\s,]+/', $tags);
430 // Remove eventual empty values
431 $tags = array_values(array_filter($tags));
432
433 $this->tags = $tags;
434
435 return $this;
436 }
437
438 /**
439 * Rename a tag in tags list.
440 *
441 * @param string $fromTag
442 * @param string $toTag
443 */
efb7d21b 444 public function renameTag(string $fromTag, string $toTag): void
336a28fa
A
445 {
446 if (($pos = array_search($fromTag, $this->tags)) !== false) {
447 $this->tags[$pos] = trim($toTag);
448 }
449 }
450
451 /**
452 * Delete a tag from tags list.
453 *
454 * @param string $tag
455 */
efb7d21b 456 public function deleteTag(string $tag): void
336a28fa
A
457 {
458 if (($pos = array_search($tag, $this->tags)) !== false) {
459 unset($this->tags[$pos]);
460 $this->tags = array_values($this->tags);
461 }
462 }
463}