1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
/** |
4
|
|
|
* @copyright Copyright (c) 2019, Thomas Citharel |
5
|
|
|
* @copyright Copyright (c) 2019, Georg Ehrke |
6
|
|
|
* |
7
|
|
|
* @author Thomas Citharel <[email protected]> |
8
|
|
|
* @author Georg Ehrke <[email protected]> |
9
|
|
|
* |
10
|
|
|
* @license GNU AGPL version 3 or any later version |
11
|
|
|
* |
12
|
|
|
* This program is free software: you can redistribute it and/or modify |
13
|
|
|
* it under the terms of the GNU Affero General Public License as |
14
|
|
|
* published by the Free Software Foundation, either version 3 of the |
15
|
|
|
* License, or (at your option) any later version. |
16
|
|
|
* |
17
|
|
|
* This program is distributed in the hope that it will be useful, |
18
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
19
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20
|
|
|
* GNU Affero General Public License for more details. |
21
|
|
|
* |
22
|
|
|
* You should have received a copy of the GNU Affero General Public License |
23
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
24
|
|
|
* |
25
|
|
|
*/ |
26
|
|
|
namespace OCA\DAV\CalDAV\Reminder\NotificationProvider; |
27
|
|
|
|
28
|
|
|
use \DateTime; |
29
|
|
|
use OCP\IConfig; |
30
|
|
|
use OCP\IL10N; |
31
|
|
|
use OCP\ILogger; |
32
|
|
|
use OCP\IURLGenerator; |
33
|
|
|
use OCP\L10N\IFactory as L10NFactory; |
34
|
|
|
use OCP\Mail\IEMailTemplate; |
35
|
|
|
use OCP\Mail\IMailer; |
36
|
|
|
use OCP\IUser; |
37
|
|
|
use Sabre\VObject\Component\VEvent; |
38
|
|
|
use Sabre\VObject; |
39
|
|
|
use Sabre\VObject\Parameter; |
40
|
|
|
use Sabre\VObject\Property; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Class EmailProvider |
44
|
|
|
* |
45
|
|
|
* @package OCA\DAV\CalDAV\Reminder\NotificationProvider |
46
|
|
|
*/ |
47
|
|
|
class EmailProvider extends AbstractProvider { |
48
|
|
|
|
49
|
|
|
/** @var string */ |
50
|
|
|
public const NOTIFICATION_TYPE = 'EMAIL'; |
51
|
|
|
|
52
|
|
|
/** @var IMailer */ |
53
|
|
|
private $mailer; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @param IConfig $config |
57
|
|
|
* @param IMailer $mailer |
58
|
|
|
* @param ILogger $logger |
59
|
|
|
* @param L10NFactory $l10nFactory |
60
|
|
|
* @param IUrlGenerator $urlGenerator |
61
|
|
|
*/ |
62
|
|
|
public function __construct(IConfig $config, |
63
|
|
|
IMailer $mailer, |
64
|
|
|
ILogger $logger, |
65
|
|
|
L10NFactory $l10nFactory, |
66
|
|
|
IURLGenerator $urlGenerator) { |
67
|
|
|
parent::__construct($logger, $l10nFactory, $urlGenerator, $config); |
68
|
|
|
$this->mailer = $mailer; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Send out notification via email |
73
|
|
|
* |
74
|
|
|
* @param VEvent $vevent |
75
|
|
|
* @param string $calendarDisplayName |
76
|
|
|
* @param array $users |
77
|
|
|
* @throws \Exception |
78
|
|
|
*/ |
79
|
|
|
public function send(VEvent $vevent, |
80
|
|
|
string $calendarDisplayName, |
81
|
|
|
array $users=[]):void { |
82
|
|
|
$fallbackLanguage = $this->getFallbackLanguage(); |
83
|
|
|
|
84
|
|
|
$emailAddressesOfSharees = $this->getEMailAddressesOfAllUsersWithWriteAccessToCalendar($users); |
85
|
|
|
$emailAddressesOfAttendees = $this->getAllEMailAddressesFromEvent($vevent); |
86
|
|
|
|
87
|
|
|
// Quote from php.net: |
88
|
|
|
// If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. |
89
|
|
|
// => if there are duplicate email addresses, it will always take the system value |
90
|
|
|
$emailAddresses = array_merge( |
91
|
|
|
$emailAddressesOfAttendees, |
92
|
|
|
$emailAddressesOfSharees |
93
|
|
|
); |
94
|
|
|
|
95
|
|
|
$sortedByLanguage = $this->sortEMailAddressesByLanguage($emailAddresses, $fallbackLanguage); |
96
|
|
|
$organizer = $this->getOrganizerEMailAndNameFromEvent($vevent); |
97
|
|
|
|
98
|
|
|
foreach($sortedByLanguage as $lang => $emailAddresses) { |
99
|
|
|
if (!$this->hasL10NForLang($lang)) { |
100
|
|
|
$lang = $fallbackLanguage; |
101
|
|
|
} |
102
|
|
|
$l10n = $this->getL10NForLang($lang); |
103
|
|
|
$fromEMail = \OCP\Util::getDefaultEmailAddress('reminders-noreply'); |
104
|
|
|
|
105
|
|
|
$template = $this->mailer->createEMailTemplate('dav.calendarReminder'); |
106
|
|
|
$template->addHeader(); |
107
|
|
|
$this->addSubjectAndHeading($template, $l10n, $vevent); |
108
|
|
|
$this->addBulletList($template, $l10n, $calendarDisplayName, $vevent); |
109
|
|
|
$template->addFooter(); |
110
|
|
|
|
111
|
|
|
foreach ($emailAddresses as $emailAddress) { |
112
|
|
|
$message = $this->mailer->createMessage(); |
113
|
|
|
$message->setFrom([$fromEMail]); |
114
|
|
|
if ($organizer) { |
115
|
|
|
$message->setReplyTo($organizer); |
116
|
|
|
} |
117
|
|
|
$message->setTo([$emailAddress]); |
118
|
|
|
$message->useTemplate($template); |
119
|
|
|
|
120
|
|
|
try { |
121
|
|
|
$failed = $this->mailer->send($message); |
122
|
|
|
if ($failed) { |
|
|
|
|
123
|
|
|
$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]); |
124
|
|
|
} |
125
|
|
|
} catch (\Exception $ex) { |
126
|
|
|
$this->logger->logException($ex, ['app' => 'dav']); |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* @param IEMailTemplate $template |
134
|
|
|
* @param IL10N $l10n |
135
|
|
|
* @param VEvent $vevent |
136
|
|
|
*/ |
137
|
|
|
private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n, VEvent $vevent):void { |
138
|
|
|
$template->setSubject('Notification: ' . $this->getTitleFromVEvent($vevent, $l10n)); |
139
|
|
|
$template->addHeading($this->getTitleFromVEvent($vevent, $l10n)); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @param IEMailTemplate $template |
144
|
|
|
* @param IL10N $l10n |
145
|
|
|
* @param string $calendarDisplayName |
146
|
|
|
* @param array $eventData |
147
|
|
|
*/ |
148
|
|
|
private function addBulletList(IEMailTemplate $template, |
149
|
|
|
IL10N $l10n, |
150
|
|
|
string $calendarDisplayName, |
151
|
|
|
VEvent $vevent):void { |
152
|
|
|
$template->addBodyListItem($calendarDisplayName, $l10n->t('Calendar:'), |
153
|
|
|
$this->getAbsoluteImagePath('actions/info.svg')); |
154
|
|
|
|
155
|
|
|
$template->addBodyListItem($this->generateDateString($l10n, $vevent), $l10n->t('Date:'), |
156
|
|
|
$this->getAbsoluteImagePath('places/calendar.svg')); |
157
|
|
|
|
158
|
|
|
if (isset($vevent->LOCATION)) { |
159
|
|
|
$template->addBodyListItem((string) $vevent->LOCATION, $l10n->t('Where:'), |
160
|
|
|
$this->getAbsoluteImagePath('actions/address.svg')); |
161
|
|
|
} |
162
|
|
|
if (isset($vevent->DESCRIPTION)) { |
163
|
|
|
$template->addBodyListItem((string) $vevent->DESCRIPTION, $l10n->t('Description:'), |
164
|
|
|
$this->getAbsoluteImagePath('actions/more.svg')); |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* @param string $path |
170
|
|
|
* @return string |
171
|
|
|
*/ |
172
|
|
|
private function getAbsoluteImagePath(string $path):string { |
173
|
|
|
return $this->urlGenerator->getAbsoluteURL( |
174
|
|
|
$this->urlGenerator->imagePath('core', $path) |
175
|
|
|
); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* @param VEvent $vevent |
180
|
|
|
* @return array|null |
181
|
|
|
*/ |
182
|
|
|
private function getOrganizerEMailAndNameFromEvent(VEvent $vevent):?array { |
183
|
|
|
if (!$vevent->ORGANIZER) { |
184
|
|
|
return null; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
$organizer = $vevent->ORGANIZER; |
188
|
|
|
if (strcasecmp($organizer->getValue(), 'mailto:') !== 0) { |
189
|
|
|
return null; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
$organizerEMail = substr($organizer->getValue(), 7); |
193
|
|
|
|
194
|
|
|
$name = $organizer->offsetGet('CN'); |
195
|
|
|
if ($name instanceof Parameter) { |
196
|
|
|
return [$organizerEMail => $name]; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
return [$organizerEMail]; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @param array $emails |
204
|
|
|
* @param string $defaultLanguage |
205
|
|
|
* @return array |
206
|
|
|
*/ |
207
|
|
|
private function sortEMailAddressesByLanguage(array $emails, |
208
|
|
|
string $defaultLanguage):array { |
209
|
|
|
$sortedByLanguage = []; |
210
|
|
|
|
211
|
|
|
foreach($emails as $emailAddress => $parameters) { |
212
|
|
|
if (isset($parameters['LANG'])) { |
213
|
|
|
$lang = $parameters['LANG']; |
214
|
|
|
} else { |
215
|
|
|
$lang = $defaultLanguage; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
if (!isset($sortedByLanguage[$lang])) { |
219
|
|
|
$sortedByLanguage[$lang] = []; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
$sortedByLanguage[$lang][] = $emailAddress; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
return $sortedByLanguage; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* @param VEvent $vevent |
230
|
|
|
* @return array |
231
|
|
|
*/ |
232
|
|
|
private function getAllEMailAddressesFromEvent(VEvent $vevent):array { |
233
|
|
|
$emailAddresses = []; |
234
|
|
|
|
235
|
|
|
if (isset($vevent->ATTENDEE)) { |
236
|
|
|
foreach ($vevent->ATTENDEE as $attendee) { |
237
|
|
|
if (!($attendee instanceof VObject\Property)) { |
238
|
|
|
continue; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
$cuType = $this->getCUTypeOfAttendee($attendee); |
242
|
|
|
if (\in_array($cuType, ['RESOURCE', 'ROOM', 'UNKNOWN'])) { |
243
|
|
|
// Don't send emails to things |
244
|
|
|
continue; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
$partstat = $this->getPartstatOfAttendee($attendee); |
248
|
|
|
if ($partstat === 'DECLINED') { |
249
|
|
|
// Don't send out emails to people who declined |
250
|
|
|
continue; |
251
|
|
|
} |
252
|
|
|
if ($partstat === 'DELEGATED') { |
253
|
|
|
$delegates = $attendee->offsetGet('DELEGATED-TO'); |
254
|
|
|
if (!($delegates instanceof VObject\Parameter)) { |
255
|
|
|
continue; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
$emailAddressesOfDelegates = $delegates->getParts(); |
259
|
|
|
foreach($emailAddressesOfDelegates as $addressesOfDelegate) { |
260
|
|
|
if (strcasecmp($addressesOfDelegate, 'mailto:') === 0) { |
261
|
|
|
$emailAddresses[substr($addressesOfDelegate, 7)] = []; |
262
|
|
|
} |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
continue; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
$emailAddressOfAttendee = $this->getEMailAddressOfAttendee($attendee); |
269
|
|
|
if ($emailAddressOfAttendee !== null) { |
270
|
|
|
$properties = []; |
271
|
|
|
|
272
|
|
|
$langProp = $attendee->offsetGet('LANG'); |
273
|
|
|
if ($langProp instanceof VObject\Parameter) { |
274
|
|
|
$properties['LANG'] = $langProp->getValue(); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
$emailAddresses[$emailAddressOfAttendee] = $properties; |
278
|
|
|
} |
279
|
|
|
} |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
if (isset($vevent->ORGANIZER) && $this->hasAttendeeMailURI($vevent->ORGANIZER)) { |
283
|
|
|
$emailAddresses[$this->getEMailAddressOfAttendee($vevent->ORGANIZER)] = []; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
return $emailAddresses; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
|
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* @param VObject\Property $attendee |
293
|
|
|
* @return string |
294
|
|
|
*/ |
295
|
|
|
private function getCUTypeOfAttendee(VObject\Property $attendee):string { |
296
|
|
|
$cuType = $attendee->offsetGet('CUTYPE'); |
297
|
|
|
if ($cuType instanceof VObject\Parameter) { |
298
|
|
|
return strtoupper($cuType->getValue()); |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
return 'INDIVIDUAL'; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
/** |
305
|
|
|
* @param VObject\Property $attendee |
306
|
|
|
* @return string |
307
|
|
|
*/ |
308
|
|
|
private function getPartstatOfAttendee(VObject\Property $attendee):string { |
309
|
|
|
$partstat = $attendee->offsetGet('PARTSTAT'); |
310
|
|
|
if ($partstat instanceof VObject\Parameter) { |
311
|
|
|
return strtoupper($partstat->getValue()); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
return 'NEEDS-ACTION'; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* @param VObject\Property $attendee |
319
|
|
|
* @return bool |
320
|
|
|
*/ |
321
|
|
|
private function hasAttendeeMailURI(VObject\Property $attendee):bool { |
322
|
|
|
return stripos($attendee->getValue(), 'mailto:') === 0; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* @param VObject\Property $attendee |
327
|
|
|
* @return string|null |
328
|
|
|
*/ |
329
|
|
|
private function getEMailAddressOfAttendee(VObject\Property $attendee):?string { |
330
|
|
|
if (!$this->hasAttendeeMailURI($attendee)) { |
331
|
|
|
return null; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
return substr($attendee->getValue(), 7); |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* @param array $users |
339
|
|
|
* @return array |
340
|
|
|
*/ |
341
|
|
|
private function getEMailAddressesOfAllUsersWithWriteAccessToCalendar(array $users):array { |
342
|
|
|
$emailAddresses = []; |
343
|
|
|
|
344
|
|
|
foreach($users as $user) { |
345
|
|
|
$emailAddress = $user->getEMailAddress(); |
346
|
|
|
if ($emailAddress) { |
347
|
|
|
$lang = $this->getLangForUser($user); |
348
|
|
|
if ($lang) { |
349
|
|
|
$emailAddresses[$emailAddress] = [ |
350
|
|
|
'LANG' => $lang, |
351
|
|
|
]; |
352
|
|
|
} else { |
353
|
|
|
$emailAddresses[$emailAddress] = []; |
354
|
|
|
} |
355
|
|
|
} |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
return $emailAddresses; |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* @param IUser $user |
363
|
|
|
* @return string |
364
|
|
|
*/ |
365
|
|
|
private function getLangForUser(IUser $user): ?string { |
366
|
|
|
return $this->config->getUserValue($user->getUID(), 'core', 'lang', null); |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* @param IL10N $l10n |
371
|
|
|
* @param VEvent $vevent |
372
|
|
|
* @return string |
373
|
|
|
* @throws \Exception |
374
|
|
|
*/ |
375
|
|
|
private function generateDateString(IL10N $l10n, VEvent $vevent):string { |
376
|
|
|
$isAllDay = $vevent->DTSTART instanceof Property\ICalendar\Date; |
377
|
|
|
|
378
|
|
|
/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */ |
379
|
|
|
/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */ |
380
|
|
|
/** @var \DateTimeImmutable $dtstartDt */ |
381
|
|
|
$dtstartDt = $vevent->DTSTART->getDateTime(); |
|
|
|
|
382
|
|
|
/** @var \DateTimeImmutable $dtendDt */ |
383
|
|
|
$dtendDt = $this->getDTEndFromEvent($vevent)->getDateTime(); |
384
|
|
|
|
385
|
|
|
$diff = $dtstartDt->diff($dtendDt); |
386
|
|
|
|
387
|
|
|
/** @phan-suppress-next-line PhanUndeclaredClassMethod */ |
388
|
|
|
$dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM)); |
389
|
|
|
/** @phan-suppress-next-line PhanUndeclaredClassMethod */ |
390
|
|
|
$dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM)); |
391
|
|
|
|
392
|
|
|
if ($isAllDay) { |
393
|
|
|
// One day event |
394
|
|
|
if ($diff->days === 1) { |
395
|
|
|
return $this->getDateString($l10n, $dtstartDt); |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
return implode(' - ', [ |
399
|
|
|
$this->getDateString($l10n, $dtstartDt), |
400
|
|
|
$this->getDateString($l10n, $dtendDt), |
401
|
|
|
]); |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
$startTimezone = $endTimezone = null; |
405
|
|
|
if (!$vevent->DTSTART->isFloating()) { |
|
|
|
|
406
|
|
|
/** @phan-suppress-next-line PhanUndeclaredClassMethod */ |
407
|
|
|
$startTimezone = $vevent->DTSTART->getDateTime()->getTimezone()->getName(); |
408
|
|
|
/** @phan-suppress-next-line PhanUndeclaredClassMethod */ |
409
|
|
|
$endTimezone = $this->getDTEndFromEvent($vevent)->getDateTime()->getTimezone()->getName(); |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
$localeStart = implode(', ', [ |
413
|
|
|
$this->getWeekDayName($l10n, $dtstartDt), |
414
|
|
|
$this->getDateTimeString($l10n, $dtstartDt) |
415
|
|
|
]); |
416
|
|
|
|
417
|
|
|
// always show full date with timezone if timezones are different |
418
|
|
|
if ($startTimezone !== $endTimezone) { |
419
|
|
|
$localeEnd = implode(', ', [ |
420
|
|
|
$this->getWeekDayName($l10n, $dtendDt), |
421
|
|
|
$this->getDateTimeString($l10n, $dtendDt) |
422
|
|
|
]); |
423
|
|
|
|
424
|
|
|
return $localeStart |
425
|
|
|
. ' (' . $startTimezone . ') ' |
426
|
|
|
. ' - ' |
427
|
|
|
. $localeEnd |
428
|
|
|
. ' (' . $endTimezone . ')'; |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
// Show only the time if the day is the same |
432
|
|
|
$localeEnd = $this->isDayEqual($dtstartDt, $dtendDt) |
433
|
|
|
? $this->getTimeString($l10n, $dtendDt) |
434
|
|
|
: implode(', ', [ |
435
|
|
|
$this->getWeekDayName($l10n, $dtendDt), |
436
|
|
|
$this->getDateTimeString($l10n, $dtendDt) |
437
|
|
|
]); |
438
|
|
|
|
439
|
|
|
return $localeStart |
440
|
|
|
. ' - ' |
441
|
|
|
. $localeEnd |
442
|
|
|
. ' (' . $startTimezone . ')'; |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
/** |
446
|
|
|
* @param DateTime $dtStart |
447
|
|
|
* @param DateTime $dtEnd |
448
|
|
|
* @return bool |
449
|
|
|
*/ |
450
|
|
|
private function isDayEqual(DateTime $dtStart, |
451
|
|
|
DateTime $dtEnd):bool { |
452
|
|
|
return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d'); |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
/** |
456
|
|
|
* @param IL10N $l10n |
457
|
|
|
* @param DateTime $dt |
458
|
|
|
* @return string |
459
|
|
|
*/ |
460
|
|
|
private function getWeekDayName(IL10N $l10n, DateTime $dt):string { |
461
|
|
|
return $l10n->l('weekdayName', $dt, ['width' => 'abbreviated']); |
|
|
|
|
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
/** |
465
|
|
|
* @param IL10N $l10n |
466
|
|
|
* @param DateTime $dt |
467
|
|
|
* @return string |
468
|
|
|
*/ |
469
|
|
|
private function getDateString(IL10N $l10n, DateTime $dt):string { |
470
|
|
|
return $l10n->l('date', $dt, ['width' => 'medium']); |
|
|
|
|
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
/** |
474
|
|
|
* @param IL10N $l10n |
475
|
|
|
* @param DateTime $dt |
476
|
|
|
* @return string |
477
|
|
|
*/ |
478
|
|
|
private function getDateTimeString(IL10N $l10n, DateTime $dt):string { |
479
|
|
|
return $l10n->l('datetime', $dt, ['width' => 'medium|short']); |
|
|
|
|
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
/** |
483
|
|
|
* @param IL10N $l10n |
484
|
|
|
* @param DateTime $dt |
485
|
|
|
* @return string |
486
|
|
|
*/ |
487
|
|
|
private function getTimeString(IL10N $l10n, DateTime $dt):string { |
488
|
|
|
return $l10n->l('time', $dt, ['width' => 'short']); |
|
|
|
|
489
|
|
|
} |
490
|
|
|
|
491
|
|
|
/** |
492
|
|
|
* @param VEvent $vevent |
493
|
|
|
* @param IL10N $l10n |
494
|
|
|
* @return string |
495
|
|
|
*/ |
496
|
|
|
private function getTitleFromVEvent(VEvent $vevent, IL10N $l10n):string { |
497
|
|
|
if (isset($vevent->SUMMARY)) { |
498
|
|
|
return (string)$vevent->SUMMARY; |
499
|
|
|
} |
500
|
|
|
|
501
|
|
|
return $l10n->t('Untitled event'); |
502
|
|
|
} |
503
|
|
|
} |
504
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.