1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Charcoal\Cms; |
4
|
|
|
|
5
|
|
|
use DateTime; |
6
|
|
|
use DateTimeInterface; |
7
|
|
|
use InvalidArgumentException; |
8
|
|
|
|
9
|
|
|
// From PSR-7 |
10
|
|
|
use Psr\Http\Message\RequestInterface; |
11
|
|
|
use Psr\Http\Message\ResponseInterface; |
12
|
|
|
|
13
|
|
|
// From 'charcoal-object' |
14
|
|
|
use Charcoal\Object\Content; |
15
|
|
|
use Charcoal\Object\CategorizableInterface; |
16
|
|
|
use Charcoal\Object\CategorizableTrait; |
17
|
|
|
use Charcoal\Object\PublishableInterface; |
18
|
|
|
use Charcoal\Object\PublishableTrait; |
19
|
|
|
use Charcoal\Object\RoutableInterface; |
20
|
|
|
use Charcoal\Object\RoutableTrait; |
21
|
|
|
|
22
|
|
|
// From 'charcoal-translator' |
23
|
|
|
use Charcoal\Translator\Translation; |
24
|
|
|
|
25
|
|
|
// From 'charcoal-cms' |
26
|
|
|
use Charcoal\Cms\MetatagInterface; |
27
|
|
|
use Charcoal\Cms\SearchableInterface; |
28
|
|
|
use Charcoal\Cms\TemplateableInterface; |
29
|
|
|
|
30
|
|
|
use Charcoal\Cms\MetatagTrait; |
31
|
|
|
use Charcoal\Cms\SearchableTrait; |
32
|
|
|
use Charcoal\Cms\TemplateableTrait; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* |
36
|
|
|
*/ |
37
|
|
|
abstract class AbstractEvent extends Content implements |
38
|
|
|
CategorizableInterface, |
39
|
|
|
EventInterface, |
40
|
|
|
MetatagInterface, |
41
|
|
|
PublishableInterface, |
42
|
|
|
RoutableInterface, |
43
|
|
|
SearchableInterface, |
44
|
|
|
TemplateableInterface |
45
|
|
|
{ |
46
|
|
|
use CategorizableTrait; |
47
|
|
|
use PublishableTrait; |
48
|
|
|
use MetatagTrait; |
49
|
|
|
use RoutableTrait; |
50
|
|
|
use SearchableTrait; |
51
|
|
|
use TemplateableTrait; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var Translation|string|null |
55
|
|
|
*/ |
56
|
|
|
private $title; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var Translation|string|null |
60
|
|
|
*/ |
61
|
|
|
private $subtitle; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var Translation|string|null |
65
|
|
|
*/ |
66
|
|
|
private $summary; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var Translation|string|null |
70
|
|
|
*/ |
71
|
|
|
private $content; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @var Translation|string|null |
75
|
|
|
*/ |
76
|
|
|
private $image; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @var DateTimeInterface|null |
80
|
|
|
*/ |
81
|
|
|
private $startDate; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @var DateTimeInterface|null |
85
|
|
|
*/ |
86
|
|
|
private $endDate; |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* @var array |
90
|
|
|
*/ |
91
|
|
|
protected $keywords; |
92
|
|
|
|
93
|
|
|
// ========================================================================== |
94
|
|
|
// INIT |
95
|
|
|
// ========================================================================== |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Section constructor. |
99
|
|
|
* @param array $data The data. |
100
|
|
|
*/ |
101
|
|
View Code Duplication |
public function __construct(array $data = null) |
|
|
|
|
102
|
|
|
{ |
103
|
|
|
parent::__construct($data); |
104
|
|
|
|
105
|
|
|
if (is_callable([ $this, 'defaultData' ])) { |
106
|
|
|
$this->setData($this->defaultData()); |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
// ========================================================================== |
111
|
|
|
// FUNCTIONS |
112
|
|
|
// ========================================================================== |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Some dates cannot be null |
116
|
|
|
* @return void |
117
|
|
|
*/ |
118
|
|
|
public function verifyDates() |
119
|
|
|
{ |
120
|
|
|
if (!$this->startDate()) { |
121
|
|
|
$this->setStartDate('now'); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
if (!$this->endDate()) { |
125
|
|
|
$this->setEndDate($this->startDate()); |
|
|
|
|
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
if (!$this->publishDate()) { |
129
|
|
|
$this->setPublishDate('now'); |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @return string The date filtered for admin dual select input and others. |
135
|
|
|
*/ |
136
|
|
|
public function adminDateFilter() |
137
|
|
|
{ |
138
|
|
|
$start = $this->startDate()->format('Y-m-d'); |
139
|
|
|
$end = $this->endDate()->format('Y-m-d'); |
140
|
|
|
|
141
|
|
|
if ($start === $end) { |
142
|
|
|
$date = $start; |
143
|
|
|
} else { |
144
|
|
|
$date = $start.' - '.$end; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
return $date; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
// ========================================================================== |
151
|
|
|
// SETTERS and GETTERS |
152
|
|
|
// ========================================================================== |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* @param mixed $title The event title (localized). |
156
|
|
|
* @return self |
157
|
|
|
*/ |
158
|
|
|
public function setTitle($title) |
159
|
|
|
{ |
160
|
|
|
$this->title = $this->translator()->translation($title); |
161
|
|
|
|
162
|
|
|
return $this; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @return Translation|string|null |
167
|
|
|
*/ |
168
|
|
|
public function title() |
169
|
|
|
{ |
170
|
|
|
return $this->title; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* @param mixed $subtitle The event subtitle (localized). |
175
|
|
|
* @return self |
176
|
|
|
*/ |
177
|
|
|
public function setSubtitle($subtitle) |
178
|
|
|
{ |
179
|
|
|
$this->subtitle = $this->translator()->translation($subtitle); |
180
|
|
|
|
181
|
|
|
return $this; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* @return Translation|string|null |
186
|
|
|
*/ |
187
|
|
|
public function subtitle() |
188
|
|
|
{ |
189
|
|
|
return $this->subtitle; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* @param mixed $summary The news summary (localized). |
194
|
|
|
* @return self |
195
|
|
|
*/ |
196
|
|
|
public function setSummary($summary) |
197
|
|
|
{ |
198
|
|
|
$this->summary = $this->translator()->translation($summary); |
199
|
|
|
|
200
|
|
|
return $this; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* @return Translation|string|null |
205
|
|
|
*/ |
206
|
|
|
public function summary() |
207
|
|
|
{ |
208
|
|
|
return $this->summary; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* @param mixed $content The event content (localized). |
213
|
|
|
* @return self |
214
|
|
|
*/ |
215
|
|
|
public function setContent($content) |
216
|
|
|
{ |
217
|
|
|
$this->content = $this->translator()->translation($content); |
218
|
|
|
|
219
|
|
|
return $this; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* @return Translation|string|null |
224
|
|
|
*/ |
225
|
|
|
public function content() |
226
|
|
|
{ |
227
|
|
|
return $this->content; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* @param mixed $image The section main image (localized). |
232
|
|
|
* @return self |
233
|
|
|
*/ |
234
|
|
|
public function setImage($image) |
235
|
|
|
{ |
236
|
|
|
$this->image = $this->translator()->translation($image); |
237
|
|
|
|
238
|
|
|
return $this; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* @return Translation|string|null |
243
|
|
|
*/ |
244
|
|
|
public function image() |
245
|
|
|
{ |
246
|
|
|
return $this->image; |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
/** |
250
|
|
|
* @param string|DateTimeInterface $startDate Event starting date. |
251
|
|
|
* @throws InvalidArgumentException If the timestamp is invalid. |
252
|
|
|
* @return self |
253
|
|
|
*/ |
254
|
|
View Code Duplication |
public function setStartDate($startDate) |
|
|
|
|
255
|
|
|
{ |
256
|
|
|
if ($startDate === null || $startDate === '') { |
257
|
|
|
$this->startDate = null; |
258
|
|
|
|
259
|
|
|
return $this; |
260
|
|
|
} |
261
|
|
|
if (is_string($startDate)) { |
262
|
|
|
$startDate = new DateTime($startDate); |
263
|
|
|
} |
264
|
|
|
if (!($startDate instanceof DateTimeInterface)) { |
265
|
|
|
throw new InvalidArgumentException( |
266
|
|
|
'Invalid "Start Date" value. Must be a date/time string or a DateTime object.' |
267
|
|
|
); |
268
|
|
|
} |
269
|
|
|
$this->startDate = $startDate; |
270
|
|
|
|
271
|
|
|
return $this; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* @return DateTimeInterface|null |
276
|
|
|
*/ |
277
|
|
|
public function startDate() |
278
|
|
|
{ |
279
|
|
|
return $this->startDate; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* @param string|DateTimeInterface $endDate Event end date. |
284
|
|
|
* @throws InvalidArgumentException If the timestamp is invalid. |
285
|
|
|
* @return self |
286
|
|
|
*/ |
287
|
|
View Code Duplication |
public function setEndDate($endDate) |
|
|
|
|
288
|
|
|
{ |
289
|
|
|
if ($endDate === null || $endDate === '') { |
290
|
|
|
$this->endDate = null; |
291
|
|
|
|
292
|
|
|
return $this; |
293
|
|
|
} |
294
|
|
|
if (is_string($endDate)) { |
295
|
|
|
$endDate = new DateTime($endDate); |
296
|
|
|
} |
297
|
|
|
if (!($endDate instanceof DateTimeInterface)) { |
298
|
|
|
throw new InvalidArgumentException( |
299
|
|
|
'Invalid "End Date" value. Must be a date/time string or a DateTime object.' |
300
|
|
|
); |
301
|
|
|
} |
302
|
|
|
$this->endDate = $endDate; |
303
|
|
|
|
304
|
|
|
return $this; |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* @return DateTimeInterface|null |
309
|
|
|
*/ |
310
|
|
|
public function endDate() |
311
|
|
|
{ |
312
|
|
|
return $this->endDate; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
// ========================================================================== |
316
|
|
|
// META TAGS |
317
|
|
|
// ========================================================================== |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* MetatagTrait > canonical_url |
321
|
|
|
* |
322
|
|
|
* @todo |
323
|
|
|
* @return string |
324
|
|
|
*/ |
325
|
|
|
public function canonicalUrl() |
326
|
|
|
{ |
327
|
|
|
return ''; |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* @return Translation|string|null |
332
|
|
|
*/ |
333
|
|
|
public function defaultMetaTitle() |
334
|
|
|
{ |
335
|
|
|
return $this->title(); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* @return Translation|string|null |
340
|
|
|
*/ |
341
|
|
View Code Duplication |
public function defaultMetaDescription() |
|
|
|
|
342
|
|
|
{ |
343
|
|
|
$content = $this->translator()->translation($this->content()); |
344
|
|
|
if ($content instanceof Translation) { |
345
|
|
|
$desc = []; |
346
|
|
|
foreach ($content->data() as $lang => $text) { |
347
|
|
|
$desc[$lang] = strip_tags($text); |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
return $this->translator()->translation($desc); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
return null; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* @return Translation|string|null |
358
|
|
|
*/ |
359
|
|
|
public function defaultMetaImage() |
360
|
|
|
{ |
361
|
|
|
return $this->image(); |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Retrieve the object's keywords. |
366
|
|
|
* |
367
|
|
|
* @return string[] |
368
|
|
|
*/ |
369
|
|
|
public function keywords() |
370
|
|
|
{ |
371
|
|
|
return $this->keywords; |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
// ========================================================================== |
375
|
|
|
// EVENTS |
376
|
|
|
// ========================================================================== |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* {@inheritdoc} |
380
|
|
|
* |
381
|
|
|
* @return boolean |
382
|
|
|
*/ |
383
|
|
|
public function preSave() |
384
|
|
|
{ |
385
|
|
|
$this->verifyDates(); |
386
|
|
|
$this->setSlug($this->generateSlug()); |
387
|
|
|
$this->generateDefaultMetaTags(); |
388
|
|
|
|
389
|
|
|
return parent::preSave(); |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
/** |
393
|
|
|
* {@inheritdoc} |
394
|
|
|
* |
395
|
|
|
* @param array $properties Optional properties to update. |
396
|
|
|
* @return boolean |
397
|
|
|
*/ |
398
|
|
|
public function preUpdate(array $properties = null) |
399
|
|
|
{ |
400
|
|
|
$this->verifyDates(); |
401
|
|
|
$this->setSlug($this->generateSlug()); |
402
|
|
|
$this->generateDefaultMetaTags(); |
403
|
|
|
|
404
|
|
|
return parent::preUpdate($properties); |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* @return boolean Parent postSave(). |
409
|
|
|
*/ |
410
|
|
|
public function postSave() |
411
|
|
|
{ |
412
|
|
|
// RoutableTrait |
413
|
|
|
$this->generateObjectRoute($this->slug()); |
414
|
|
|
|
415
|
|
|
return parent::postSave(); |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
/** |
419
|
|
|
* @param array|null $properties Properties. |
420
|
|
|
* @return boolean |
421
|
|
|
*/ |
422
|
|
|
public function postUpdate(array $properties = null) |
423
|
|
|
{ |
424
|
|
|
// RoutableTrait |
425
|
|
|
$this->generateObjectRoute($this->slug()); |
426
|
|
|
|
427
|
|
|
return parent::postUpdate($properties); |
|
|
|
|
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
/** |
431
|
|
|
* GenericRoute checks if the route is active. |
432
|
|
|
* Default in RoutableTrait. |
433
|
|
|
* |
434
|
|
|
* @return boolean |
435
|
|
|
*/ |
436
|
|
|
public function isActiveRoute() |
437
|
|
|
{ |
438
|
|
|
return ( |
439
|
|
|
$this->active() && |
440
|
|
|
$this->isPublished() |
441
|
|
|
); |
442
|
|
|
} |
443
|
|
|
} |
444
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.