1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* @copyright Copyright (c) 2016, ownCloud, Inc. |
7
|
|
|
* @copyright Copyright (c) 2019, Georg Ehrke |
8
|
|
|
* |
9
|
|
|
* @author Achim Königs <[email protected]> |
10
|
|
|
* @author Christoph Wurst <[email protected]> |
11
|
|
|
* @author Georg Ehrke <[email protected]> |
12
|
|
|
* @author Robin Appelman <[email protected]> |
13
|
|
|
* @author Sven Strickroth <[email protected]> |
14
|
|
|
* @author Thomas Müller <[email protected]> |
15
|
|
|
* |
16
|
|
|
* @license AGPL-3.0 |
17
|
|
|
* |
18
|
|
|
* This code is free software: you can redistribute it and/or modify |
19
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
20
|
|
|
* as published by the Free Software Foundation. |
21
|
|
|
* |
22
|
|
|
* This program is distributed in the hope that it will be useful, |
23
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
24
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
25
|
|
|
* GNU Affero General Public License for more details. |
26
|
|
|
* |
27
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
28
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
29
|
|
|
* |
30
|
|
|
*/ |
31
|
|
|
|
32
|
|
|
namespace OCA\DAV\CalDAV; |
33
|
|
|
|
34
|
|
|
use Exception; |
35
|
|
|
use OCA\DAV\CardDAV\CardDavBackend; |
36
|
|
|
use OCA\DAV\DAV\GroupPrincipalBackend; |
37
|
|
|
use OCP\IConfig; |
38
|
|
|
use OCP\IDBConnection; |
39
|
|
|
use OCP\IL10N; |
40
|
|
|
use Sabre\VObject\Component\VCalendar; |
41
|
|
|
use Sabre\VObject\Component\VCard; |
42
|
|
|
use Sabre\VObject\DateTimeParser; |
43
|
|
|
use Sabre\VObject\Document; |
44
|
|
|
use Sabre\VObject\InvalidDataException; |
45
|
|
|
use Sabre\VObject\Property\VCard\DateAndOrTime; |
46
|
|
|
use Sabre\VObject\Reader; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Class BirthdayService |
50
|
|
|
* |
51
|
|
|
* @package OCA\DAV\CalDAV |
52
|
|
|
*/ |
53
|
|
|
class BirthdayService { |
54
|
|
|
public const BIRTHDAY_CALENDAR_URI = 'contact_birthdays'; |
55
|
|
|
|
56
|
|
|
/** @var GroupPrincipalBackend */ |
57
|
|
|
private $principalBackend; |
58
|
|
|
|
59
|
|
|
/** @var CalDavBackend */ |
60
|
|
|
private $calDavBackEnd; |
61
|
|
|
|
62
|
|
|
/** @var CardDavBackend */ |
63
|
|
|
private $cardDavBackEnd; |
64
|
|
|
|
65
|
|
|
/** @var IConfig */ |
66
|
|
|
private $config; |
67
|
|
|
|
68
|
|
|
/** @var IDBConnection */ |
69
|
|
|
private $dbConnection; |
70
|
|
|
|
71
|
|
|
/** @var IL10N */ |
72
|
|
|
private $l10n; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* BirthdayService constructor. |
76
|
|
|
* |
77
|
|
|
* @param CalDavBackend $calDavBackEnd |
78
|
|
|
* @param CardDavBackend $cardDavBackEnd |
79
|
|
|
* @param GroupPrincipalBackend $principalBackend |
80
|
|
|
* @param IConfig $config |
81
|
|
|
* @param IDBConnection $dbConnection |
82
|
|
|
* @param IL10N $l10n |
83
|
|
|
*/ |
84
|
|
|
public function __construct(CalDavBackend $calDavBackEnd, |
85
|
|
|
CardDavBackend $cardDavBackEnd, |
86
|
|
|
GroupPrincipalBackend $principalBackend, |
87
|
|
|
IConfig $config, |
88
|
|
|
IDBConnection $dbConnection, |
89
|
|
|
IL10N $l10n) { |
90
|
|
|
$this->calDavBackEnd = $calDavBackEnd; |
91
|
|
|
$this->cardDavBackEnd = $cardDavBackEnd; |
92
|
|
|
$this->principalBackend = $principalBackend; |
93
|
|
|
$this->config = $config; |
94
|
|
|
$this->dbConnection = $dbConnection; |
95
|
|
|
$this->l10n = $l10n; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @param int $addressBookId |
100
|
|
|
* @param string $cardUri |
101
|
|
|
* @param string $cardData |
102
|
|
|
*/ |
103
|
|
|
public function onCardChanged(int $addressBookId, |
104
|
|
|
string $cardUri, |
105
|
|
|
string $cardData) { |
106
|
|
|
if (!$this->isGloballyEnabled()) { |
107
|
|
|
return; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
$targetPrincipals = $this->getAllAffectedPrincipals($addressBookId); |
111
|
|
|
$book = $this->cardDavBackEnd->getAddressBookById($addressBookId); |
|
|
|
|
112
|
|
|
$targetPrincipals[] = $book['principaluri']; |
113
|
|
|
$datesToSync = [ |
114
|
|
|
['postfix' => '', 'field' => 'BDAY'], |
115
|
|
|
['postfix' => '-death', 'field' => 'DEATHDATE'], |
116
|
|
|
['postfix' => '-anniversary', 'field' => 'ANNIVERSARY'], |
117
|
|
|
]; |
118
|
|
|
|
119
|
|
|
foreach ($targetPrincipals as $principalUri) { |
120
|
|
|
if (!$this->isUserEnabled($principalUri)) { |
121
|
|
|
continue; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
$calendar = $this->ensureCalendarExists($principalUri); |
|
|
|
|
125
|
|
|
foreach ($datesToSync as $type) { |
126
|
|
|
$this->updateCalendar($cardUri, $cardData, $book, (int) $calendar['id'], $type); |
|
|
|
|
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* @param int $addressBookId |
133
|
|
|
* @param string $cardUri |
134
|
|
|
*/ |
135
|
|
|
public function onCardDeleted(int $addressBookId, |
136
|
|
|
string $cardUri) { |
137
|
|
|
if (!$this->isGloballyEnabled()) { |
138
|
|
|
return; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
$targetPrincipals = $this->getAllAffectedPrincipals($addressBookId); |
142
|
|
|
$book = $this->cardDavBackEnd->getAddressBookById($addressBookId); |
|
|
|
|
143
|
|
|
$targetPrincipals[] = $book['principaluri']; |
144
|
|
|
foreach ($targetPrincipals as $principalUri) { |
145
|
|
|
if (!$this->isUserEnabled($principalUri)) { |
146
|
|
|
continue; |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
$calendar = $this->ensureCalendarExists($principalUri); |
|
|
|
|
150
|
|
|
foreach (['', '-death', '-anniversary'] as $tag) { |
151
|
|
|
$objectUri = $book['uri'] . '-' . $cardUri . $tag .'.ics'; |
152
|
|
|
$this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri); |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* @param string $principal |
159
|
|
|
* @return array|null |
160
|
|
|
* @throws \Sabre\DAV\Exception\BadRequest |
161
|
|
|
*/ |
162
|
|
|
public function ensureCalendarExists(string $principal):?array { |
163
|
|
|
$calendar = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI); |
|
|
|
|
164
|
|
|
if (!is_null($calendar)) { |
|
|
|
|
165
|
|
|
return $calendar; |
166
|
|
|
} |
167
|
|
|
$this->calDavBackEnd->createCalendar($principal, self::BIRTHDAY_CALENDAR_URI, [ |
168
|
|
|
'{DAV:}displayname' => $this->l10n->t('Contact birthdays'), |
169
|
|
|
'{http://apple.com/ns/ical/}calendar-color' => '#E9D859', |
170
|
|
|
'components' => 'VEVENT', |
171
|
|
|
]); |
172
|
|
|
|
173
|
|
|
return $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI); |
|
|
|
|
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* @param $cardData |
178
|
|
|
* @param $dateField |
179
|
|
|
* @param $postfix |
180
|
|
|
* @return VCalendar|null |
181
|
|
|
* @throws InvalidDataException |
182
|
|
|
*/ |
183
|
|
|
public function buildDateFromContact(string $cardData, |
184
|
|
|
string $dateField, |
185
|
|
|
string $postfix):?VCalendar { |
186
|
|
|
if (empty($cardData)) { |
187
|
|
|
return null; |
188
|
|
|
} |
189
|
|
|
try { |
190
|
|
|
$doc = Reader::read($cardData); |
191
|
|
|
// We're always converting to vCard 4.0 so we can rely on the |
192
|
|
|
// VCardConverter handling the X-APPLE-OMIT-YEAR property for us. |
193
|
|
|
if (!$doc instanceof VCard) { |
194
|
|
|
return null; |
195
|
|
|
} |
196
|
|
|
$doc = $doc->convert(Document::VCARD40); |
197
|
|
|
} catch (Exception $e) { |
198
|
|
|
return null; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
if (!isset($doc->{$dateField})) { |
202
|
|
|
return null; |
203
|
|
|
} |
204
|
|
|
if (!isset($doc->FN)) { |
205
|
|
|
return null; |
206
|
|
|
} |
207
|
|
|
$birthday = $doc->{$dateField}; |
208
|
|
|
if (!(string)$birthday) { |
209
|
|
|
return null; |
210
|
|
|
} |
211
|
|
|
// Skip if the BDAY property is not of the right type. |
212
|
|
|
if (!$birthday instanceof DateAndOrTime) { |
213
|
|
|
return null; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
// Skip if we can't parse the BDAY value. |
217
|
|
|
try { |
218
|
|
|
$dateParts = DateTimeParser::parseVCardDateTime($birthday->getValue()); |
219
|
|
|
} catch (InvalidDataException $e) { |
220
|
|
|
return null; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
$unknownYear = false; |
224
|
|
|
$originalYear = null; |
225
|
|
|
if (!$dateParts['year']) { |
226
|
|
|
$birthday = '1970-' . $dateParts['month'] . '-' . $dateParts['date']; |
227
|
|
|
|
228
|
|
|
$unknownYear = true; |
229
|
|
|
} else { |
230
|
|
|
$parameters = $birthday->parameters(); |
231
|
|
|
if (isset($parameters['X-APPLE-OMIT-YEAR'])) { |
232
|
|
|
$omitYear = $parameters['X-APPLE-OMIT-YEAR']; |
233
|
|
|
if ($dateParts['year'] === $omitYear) { |
234
|
|
|
$birthday = '1970-' . $dateParts['month'] . '-' . $dateParts['date']; |
235
|
|
|
$unknownYear = true; |
236
|
|
|
} |
237
|
|
|
} else { |
238
|
|
|
$originalYear = (int)$dateParts['year']; |
239
|
|
|
// 'X-APPLE-OMIT-YEAR' is not always present, at least iOS 12.4 uses the hard coded date of 1604 (the start of the gregorian calendar) when the year is unknown |
240
|
|
|
if ($originalYear == 1604) { |
241
|
|
|
$originalYear = null; |
242
|
|
|
$unknownYear = true; |
243
|
|
|
$birthday = '1970-' . $dateParts['month'] . '-' . $dateParts['date']; |
244
|
|
|
} |
245
|
|
|
if ($originalYear < 1970) { |
246
|
|
|
$birthday = '1970-' . $dateParts['month'] . '-' . $dateParts['date']; |
247
|
|
|
} |
248
|
|
|
} |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
try { |
252
|
|
|
if ($birthday instanceof DateAndOrTime) { |
253
|
|
|
$date = $birthday->getDateTime(); |
254
|
|
|
} else { |
255
|
|
|
$date = new \DateTimeImmutable($birthday); |
256
|
|
|
} |
257
|
|
|
} catch (Exception $e) { |
258
|
|
|
return null; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
$summary = $this->formatTitle($dateField, $doc->FN->getValue(), $originalYear, $this->dbConnection->supports4ByteText()); |
262
|
|
|
|
263
|
|
|
$vCal = new VCalendar(); |
264
|
|
|
$vCal->VERSION = '2.0'; |
265
|
|
|
$vCal->PRODID = '-//IDN nextcloud.com//Birthday calendar//EN'; |
266
|
|
|
$vEvent = $vCal->createComponent('VEVENT'); |
267
|
|
|
$vEvent->add('DTSTART'); |
268
|
|
|
$vEvent->DTSTART->setDateTime( |
|
|
|
|
269
|
|
|
$date |
270
|
|
|
); |
271
|
|
|
$vEvent->DTSTART['VALUE'] = 'DATE'; |
272
|
|
|
$vEvent->add('DTEND'); |
273
|
|
|
|
274
|
|
|
$dtEndDate = (new \DateTime())->setTimestamp($date->getTimeStamp()); |
275
|
|
|
$dtEndDate->add(new \DateInterval('P1D')); |
276
|
|
|
$vEvent->DTEND->setDateTime( |
|
|
|
|
277
|
|
|
$dtEndDate |
278
|
|
|
); |
279
|
|
|
|
280
|
|
|
$vEvent->DTEND['VALUE'] = 'DATE'; |
281
|
|
|
$vEvent->{'UID'} = $doc->UID . $postfix; |
282
|
|
|
$vEvent->{'RRULE'} = 'FREQ=YEARLY'; |
283
|
|
|
$vEvent->{'SUMMARY'} = $summary; |
284
|
|
|
$vEvent->{'TRANSP'} = 'TRANSPARENT'; |
285
|
|
|
$vEvent->{'X-NEXTCLOUD-BC-FIELD-TYPE'} = $dateField; |
286
|
|
|
$vEvent->{'X-NEXTCLOUD-BC-UNKNOWN-YEAR'} = $unknownYear ? '1' : '0'; |
287
|
|
|
if ($originalYear !== null) { |
288
|
|
|
$vEvent->{'X-NEXTCLOUD-BC-YEAR'} = (string) $originalYear; |
289
|
|
|
} |
290
|
|
|
$alarm = $vCal->createComponent('VALARM'); |
291
|
|
|
$alarm->add($vCal->createProperty('TRIGGER', '-PT0M', ['VALUE' => 'DURATION'])); |
292
|
|
|
$alarm->add($vCal->createProperty('ACTION', 'DISPLAY')); |
293
|
|
|
$alarm->add($vCal->createProperty('DESCRIPTION', $vEvent->{'SUMMARY'})); |
294
|
|
|
$vEvent->add($alarm); |
295
|
|
|
$vCal->add($vEvent); |
296
|
|
|
return $vCal; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* @param string $user |
301
|
|
|
*/ |
302
|
|
|
public function resetForUser(string $user):void { |
303
|
|
|
$principal = 'principals/users/'.$user; |
304
|
|
|
$calendar = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI); |
|
|
|
|
305
|
|
|
$calendarObjects = $this->calDavBackEnd->getCalendarObjects($calendar['id'], CalDavBackend::CALENDAR_TYPE_CALENDAR); |
306
|
|
|
|
307
|
|
|
foreach ($calendarObjects as $calendarObject) { |
308
|
|
|
$this->calDavBackEnd->deleteCalendarObject($calendar['id'], $calendarObject['uri'], CalDavBackend::CALENDAR_TYPE_CALENDAR); |
309
|
|
|
} |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* @param string $user |
314
|
|
|
* @throws \Sabre\DAV\Exception\BadRequest |
315
|
|
|
*/ |
316
|
|
|
public function syncUser(string $user):void { |
317
|
|
|
$principal = 'principals/users/'.$user; |
318
|
|
|
$this->ensureCalendarExists($principal); |
319
|
|
|
$books = $this->cardDavBackEnd->getAddressBooksForUser($principal); |
320
|
|
|
foreach ($books as $book) { |
321
|
|
|
$cards = $this->cardDavBackEnd->getCards($book['id']); |
322
|
|
|
foreach ($cards as $card) { |
323
|
|
|
$this->onCardChanged((int) $book['id'], $card['uri'], $card['carddata']); |
324
|
|
|
} |
325
|
|
|
} |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* @param string $existingCalendarData |
330
|
|
|
* @param VCalendar $newCalendarData |
331
|
|
|
* @return bool |
332
|
|
|
*/ |
333
|
|
|
public function birthdayEvenChanged(string $existingCalendarData, |
334
|
|
|
VCalendar $newCalendarData):bool { |
335
|
|
|
try { |
336
|
|
|
$existingBirthday = Reader::read($existingCalendarData); |
337
|
|
|
} catch (Exception $ex) { |
338
|
|
|
return true; |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
return ( |
342
|
|
|
$newCalendarData->VEVENT->DTSTART->getValue() !== $existingBirthday->VEVENT->DTSTART->getValue() || |
|
|
|
|
343
|
|
|
$newCalendarData->VEVENT->SUMMARY->getValue() !== $existingBirthday->VEVENT->SUMMARY->getValue() |
|
|
|
|
344
|
|
|
); |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* @param integer $addressBookId |
349
|
|
|
* @return mixed |
350
|
|
|
*/ |
351
|
|
|
protected function getAllAffectedPrincipals(int $addressBookId) { |
352
|
|
|
$targetPrincipals = []; |
353
|
|
|
$shares = $this->cardDavBackEnd->getShares($addressBookId); |
354
|
|
|
foreach ($shares as $share) { |
355
|
|
|
if ($share['{http://owncloud.org/ns}group-share']) { |
356
|
|
|
$users = $this->principalBackend->getGroupMemberSet($share['{http://owncloud.org/ns}principal']); |
357
|
|
|
foreach ($users as $user) { |
358
|
|
|
$targetPrincipals[] = $user['uri']; |
359
|
|
|
} |
360
|
|
|
} else { |
361
|
|
|
$targetPrincipals[] = $share['{http://owncloud.org/ns}principal']; |
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
return array_values(array_unique($targetPrincipals, SORT_STRING)); |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
/** |
368
|
|
|
* @param string $cardUri |
369
|
|
|
* @param string $cardData |
370
|
|
|
* @param array $book |
371
|
|
|
* @param int $calendarId |
372
|
|
|
* @param array $type |
373
|
|
|
* @throws InvalidDataException |
374
|
|
|
* @throws \Sabre\DAV\Exception\BadRequest |
375
|
|
|
*/ |
376
|
|
|
private function updateCalendar(string $cardUri, |
377
|
|
|
string $cardData, |
378
|
|
|
array $book, |
379
|
|
|
int $calendarId, |
380
|
|
|
array $type):void { |
381
|
|
|
$objectUri = $book['uri'] . '-' . $cardUri . $type['postfix'] . '.ics'; |
382
|
|
|
$calendarData = $this->buildDateFromContact($cardData, $type['field'], $type['postfix']); |
383
|
|
|
$existing = $this->calDavBackEnd->getCalendarObject($calendarId, $objectUri); |
384
|
|
|
if ($calendarData === null) { |
385
|
|
|
if ($existing !== null) { |
386
|
|
|
$this->calDavBackEnd->deleteCalendarObject($calendarId, $objectUri); |
387
|
|
|
} |
388
|
|
|
} else { |
389
|
|
|
if ($existing === null) { |
390
|
|
|
// not found by URI, but maybe by UID |
391
|
|
|
// happens when a contact with birthday is moved to a different address book |
392
|
|
|
$calendarInfo = $this->calDavBackEnd->getCalendarById($calendarId); |
|
|
|
|
393
|
|
|
$extraData = $this->calDavBackEnd->getDenormalizedData($calendarData->serialize()); |
394
|
|
|
|
395
|
|
|
if ($calendarInfo && array_key_exists('principaluri', $calendarInfo)) { |
|
|
|
|
396
|
|
|
$existing2path = $this->calDavBackEnd->getCalendarObjectByUID($calendarInfo['principaluri'], $extraData['uid']); |
397
|
|
|
if ($existing2path !== null && array_key_exists('uri', $calendarInfo)) { |
398
|
|
|
// delete the old birthday entry first so that we do not get duplicate UIDs |
399
|
|
|
$existing2objectUri = substr($existing2path, strlen($calendarInfo['uri']) + 1); |
400
|
|
|
$this->calDavBackEnd->deleteCalendarObject($calendarId, $existing2objectUri); |
401
|
|
|
} |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
$this->calDavBackEnd->createCalendarObject($calendarId, $objectUri, $calendarData->serialize()); |
405
|
|
|
} else { |
406
|
|
|
if ($this->birthdayEvenChanged($existing['calendardata'], $calendarData)) { |
407
|
|
|
$this->calDavBackEnd->updateCalendarObject($calendarId, $objectUri, $calendarData->serialize()); |
408
|
|
|
} |
409
|
|
|
} |
410
|
|
|
} |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
/** |
414
|
|
|
* checks if the admin opted-out of birthday calendars |
415
|
|
|
* |
416
|
|
|
* @return bool |
417
|
|
|
*/ |
418
|
|
|
private function isGloballyEnabled():bool { |
419
|
|
|
return $this->config->getAppValue('dav', 'generateBirthdayCalendar', 'yes') === 'yes'; |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
/** |
423
|
|
|
* Checks if the user opted-out of birthday calendars |
424
|
|
|
* |
425
|
|
|
* @param string $userPrincipal The user principal to check for |
426
|
|
|
* @return bool |
427
|
|
|
*/ |
428
|
|
|
private function isUserEnabled(string $userPrincipal):bool { |
429
|
|
|
if (strpos($userPrincipal, 'principals/users/') === 0) { |
430
|
|
|
$userId = substr($userPrincipal, 17); |
431
|
|
|
$isEnabled = $this->config->getUserValue($userId, 'dav', 'generateBirthdayCalendar', 'yes'); |
432
|
|
|
return $isEnabled === 'yes'; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
// not sure how we got here, just be on the safe side and return true |
436
|
|
|
return true; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
/** |
440
|
|
|
* Formats title of Birthday event |
441
|
|
|
* |
442
|
|
|
* @param string $field Field name like BDAY, ANNIVERSARY, ... |
443
|
|
|
* @param string $name Name of contact |
444
|
|
|
* @param int|null $year Year of birth, anniversary, ... |
445
|
|
|
* @param bool $supports4Byte Whether or not the database supports 4 byte chars |
446
|
|
|
* @return string The formatted title |
447
|
|
|
*/ |
448
|
|
|
private function formatTitle(string $field, |
449
|
|
|
string $name, |
450
|
|
|
int $year = null, |
451
|
|
|
bool $supports4Byte = true):string { |
452
|
|
|
if ($supports4Byte) { |
453
|
|
|
switch ($field) { |
454
|
|
|
case 'BDAY': |
455
|
|
|
return implode('', [ |
456
|
|
|
'🎂 ', |
457
|
|
|
$name, |
458
|
|
|
$year ? (' (' . $year . ')') : '', |
459
|
|
|
]); |
460
|
|
|
|
461
|
|
|
case 'DEATHDATE': |
462
|
|
|
return implode('', [ |
463
|
|
|
$this->l10n->t('Death of %s', [$name]), |
464
|
|
|
$year ? (' (' . $year . ')') : '', |
465
|
|
|
]); |
466
|
|
|
|
467
|
|
|
case 'ANNIVERSARY': |
468
|
|
|
return implode('', [ |
469
|
|
|
'💍 ', |
470
|
|
|
$name, |
471
|
|
|
$year ? (' (' . $year . ')') : '', |
472
|
|
|
]); |
473
|
|
|
|
474
|
|
|
default: |
475
|
|
|
return ''; |
476
|
|
|
} |
477
|
|
|
} else { |
478
|
|
|
switch ($field) { |
479
|
|
|
case 'BDAY': |
480
|
|
|
return implode('', [ |
481
|
|
|
$name, |
482
|
|
|
' ', |
483
|
|
|
$year ? ('(*' . $year . ')') : '*', |
484
|
|
|
]); |
485
|
|
|
|
486
|
|
|
case 'DEATHDATE': |
487
|
|
|
return implode('', [ |
488
|
|
|
$this->l10n->t('Death of %s', [$name]), |
489
|
|
|
$year ? (' (' . $year . ')') : '', |
490
|
|
|
]); |
491
|
|
|
|
492
|
|
|
case 'ANNIVERSARY': |
493
|
|
|
return implode('', [ |
494
|
|
|
$name, |
495
|
|
|
' ', |
496
|
|
|
$year ? ('(⚭' . $year . ')') : '⚭', |
497
|
|
|
]); |
498
|
|
|
|
499
|
|
|
default: |
500
|
|
|
return ''; |
501
|
|
|
} |
502
|
|
|
} |
503
|
|
|
} |
504
|
|
|
} |
505
|
|
|
|
This check looks for function or method calls that always return null and whose return value is assigned to a variable.
The method
getObject()
can return nothing but null, so it makes no sense to assign that value to a variable.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.