Passed
Push — master ( 1c7374...ee05e0 )
by Christoph
14:21 queued 13s
created
apps/dav/lib/CalDAV/BirthdayService.php 2 patches
Indentation   +470 added lines, -470 removed lines patch added patch discarded remove patch
@@ -53,475 +53,475 @@
 block discarded – undo
53 53
  * @package OCA\DAV\CalDAV
54 54
  */
55 55
 class BirthdayService {
56
-	public const BIRTHDAY_CALENDAR_URI = 'contact_birthdays';
57
-	public const EXCLUDE_FROM_BIRTHDAY_CALENDAR_PROPERTY_NAME = 'X-NC-EXCLUDE-FROM-BIRTHDAY-CALENDAR';
58
-
59
-	private GroupPrincipalBackend $principalBackend;
60
-	private CalDavBackend $calDavBackEnd;
61
-	private CardDavBackend $cardDavBackEnd;
62
-	private IConfig $config;
63
-	private IDBConnection $dbConnection;
64
-	private IL10N $l10n;
65
-
66
-	/**
67
-	 * BirthdayService constructor.
68
-	 */
69
-	public function __construct(CalDavBackend $calDavBackEnd,
70
-								CardDavBackend $cardDavBackEnd,
71
-								GroupPrincipalBackend $principalBackend,
72
-								IConfig $config,
73
-								IDBConnection $dbConnection,
74
-								IL10N $l10n) {
75
-		$this->calDavBackEnd = $calDavBackEnd;
76
-		$this->cardDavBackEnd = $cardDavBackEnd;
77
-		$this->principalBackend = $principalBackend;
78
-		$this->config = $config;
79
-		$this->dbConnection = $dbConnection;
80
-		$this->l10n = $l10n;
81
-	}
82
-
83
-	public function onCardChanged(int $addressBookId,
84
-								  string $cardUri,
85
-								  string $cardData): void {
86
-		if (!$this->isGloballyEnabled()) {
87
-			return;
88
-		}
89
-
90
-		$targetPrincipals = $this->getAllAffectedPrincipals($addressBookId);
91
-		$book = $this->cardDavBackEnd->getAddressBookById($addressBookId);
92
-		if ($book === null) {
93
-			return;
94
-		}
95
-		$targetPrincipals[] = $book['principaluri'];
96
-		$datesToSync = [
97
-			['postfix' => '', 'field' => 'BDAY'],
98
-			['postfix' => '-death', 'field' => 'DEATHDATE'],
99
-			['postfix' => '-anniversary', 'field' => 'ANNIVERSARY'],
100
-		];
101
-
102
-		foreach ($targetPrincipals as $principalUri) {
103
-			if (!$this->isUserEnabled($principalUri)) {
104
-				continue;
105
-			}
106
-
107
-			$reminderOffset = $this->getReminderOffsetForUser($principalUri);
108
-
109
-			$calendar = $this->ensureCalendarExists($principalUri);
110
-			if ($calendar === null) {
111
-				return;
112
-			}
113
-			foreach ($datesToSync as $type) {
114
-				$this->updateCalendar($cardUri, $cardData, $book, (int) $calendar['id'], $type, $reminderOffset);
115
-			}
116
-		}
117
-	}
118
-
119
-	public function onCardDeleted(int $addressBookId,
120
-								  string $cardUri): void {
121
-		if (!$this->isGloballyEnabled()) {
122
-			return;
123
-		}
124
-
125
-		$targetPrincipals = $this->getAllAffectedPrincipals($addressBookId);
126
-		$book = $this->cardDavBackEnd->getAddressBookById($addressBookId);
127
-		$targetPrincipals[] = $book['principaluri'];
128
-		foreach ($targetPrincipals as $principalUri) {
129
-			if (!$this->isUserEnabled($principalUri)) {
130
-				continue;
131
-			}
132
-
133
-			$calendar = $this->ensureCalendarExists($principalUri);
134
-			foreach (['', '-death', '-anniversary'] as $tag) {
135
-				$objectUri = $book['uri'] . '-' . $cardUri . $tag .'.ics';
136
-				$this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri, CalDavBackend::CALENDAR_TYPE_CALENDAR, true);
137
-			}
138
-		}
139
-	}
140
-
141
-	/**
142
-	 * @throws \Sabre\DAV\Exception\BadRequest
143
-	 */
144
-	public function ensureCalendarExists(string $principal): ?array {
145
-		$calendar = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI);
146
-		if (!is_null($calendar)) {
147
-			return $calendar;
148
-		}
149
-		$this->calDavBackEnd->createCalendar($principal, self::BIRTHDAY_CALENDAR_URI, [
150
-			'{DAV:}displayname' => $this->l10n->t('Contact birthdays'),
151
-			'{http://apple.com/ns/ical/}calendar-color' => '#E9D859',
152
-			'components' => 'VEVENT',
153
-		]);
154
-
155
-		return $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI);
156
-	}
157
-
158
-	/**
159
-	 * @param $cardData
160
-	 * @param $dateField
161
-	 * @param $postfix
162
-	 * @param $reminderOffset
163
-	 * @return VCalendar|null
164
-	 * @throws InvalidDataException
165
-	 */
166
-	public function buildDateFromContact(string  $cardData,
167
-										 string  $dateField,
168
-										 string  $postfix,
169
-										 ?string $reminderOffset):?VCalendar {
170
-		if (empty($cardData)) {
171
-			return null;
172
-		}
173
-		try {
174
-			$doc = Reader::read($cardData);
175
-			// We're always converting to vCard 4.0 so we can rely on the
176
-			// VCardConverter handling the X-APPLE-OMIT-YEAR property for us.
177
-			if (!$doc instanceof VCard) {
178
-				return null;
179
-			}
180
-			$doc = $doc->convert(Document::VCARD40);
181
-		} catch (Exception $e) {
182
-			return null;
183
-		}
184
-
185
-		if (isset($doc->{self::EXCLUDE_FROM_BIRTHDAY_CALENDAR_PROPERTY_NAME})) {
186
-			return null;
187
-		}
188
-
189
-		if (!isset($doc->{$dateField})) {
190
-			return null;
191
-		}
192
-		if (!isset($doc->FN)) {
193
-			return null;
194
-		}
195
-		$birthday = $doc->{$dateField};
196
-		if (!(string)$birthday) {
197
-			return null;
198
-		}
199
-		// Skip if the BDAY property is not of the right type.
200
-		if (!$birthday instanceof DateAndOrTime) {
201
-			return null;
202
-		}
203
-
204
-		// Skip if we can't parse the BDAY value.
205
-		try {
206
-			$dateParts = DateTimeParser::parseVCardDateTime($birthday->getValue());
207
-		} catch (InvalidDataException $e) {
208
-			return null;
209
-		}
210
-		if ($dateParts['year'] !== null) {
211
-			$parameters = $birthday->parameters();
212
-			$omitYear = (isset($parameters['X-APPLE-OMIT-YEAR'])
213
-					&& $parameters['X-APPLE-OMIT-YEAR'] === $dateParts['year']);
214
-			// '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
215
-			if ($omitYear || (int)$dateParts['year'] === 1604) {
216
-				$dateParts['year'] = null;
217
-			}
218
-		}
219
-
220
-		$originalYear = null;
221
-		if ($dateParts['year'] !== null) {
222
-			$originalYear = (int)$dateParts['year'];
223
-		}
224
-
225
-		$leapDay = ((int)$dateParts['month'] === 2
226
-				&& (int)$dateParts['date'] === 29);
227
-		if ($dateParts['year'] === null || $originalYear < 1970) {
228
-			$birthday = ($leapDay ? '1972-' : '1970-')
229
-				. $dateParts['month'] . '-' . $dateParts['date'];
230
-		}
231
-
232
-		try {
233
-			if ($birthday instanceof DateAndOrTime) {
234
-				$date = $birthday->getDateTime();
235
-			} else {
236
-				$date = new \DateTimeImmutable($birthday);
237
-			}
238
-		} catch (Exception $e) {
239
-			return null;
240
-		}
241
-
242
-		$summary = $this->formatTitle($dateField, $doc->FN->getValue(), $originalYear, $this->dbConnection->supports4ByteText());
243
-
244
-		$vCal = new VCalendar();
245
-		$vCal->VERSION = '2.0';
246
-		$vCal->PRODID = '-//IDN nextcloud.com//Birthday calendar//EN';
247
-		$vEvent = $vCal->createComponent('VEVENT');
248
-		$vEvent->add('DTSTART');
249
-		$vEvent->DTSTART->setDateTime(
250
-			$date
251
-		);
252
-		$vEvent->DTSTART['VALUE'] = 'DATE';
253
-		$vEvent->add('DTEND');
254
-
255
-		$dtEndDate = (new \DateTime())->setTimestamp($date->getTimeStamp());
256
-		$dtEndDate->add(new \DateInterval('P1D'));
257
-		$vEvent->DTEND->setDateTime(
258
-			$dtEndDate
259
-		);
260
-
261
-		$vEvent->DTEND['VALUE'] = 'DATE';
262
-		$vEvent->{'UID'} = $doc->UID . $postfix;
263
-		$vEvent->{'RRULE'} = 'FREQ=YEARLY';
264
-		if ($leapDay) {
265
-			/* Sabre\VObject supports BYMONTHDAY only if BYMONTH
56
+    public const BIRTHDAY_CALENDAR_URI = 'contact_birthdays';
57
+    public const EXCLUDE_FROM_BIRTHDAY_CALENDAR_PROPERTY_NAME = 'X-NC-EXCLUDE-FROM-BIRTHDAY-CALENDAR';
58
+
59
+    private GroupPrincipalBackend $principalBackend;
60
+    private CalDavBackend $calDavBackEnd;
61
+    private CardDavBackend $cardDavBackEnd;
62
+    private IConfig $config;
63
+    private IDBConnection $dbConnection;
64
+    private IL10N $l10n;
65
+
66
+    /**
67
+     * BirthdayService constructor.
68
+     */
69
+    public function __construct(CalDavBackend $calDavBackEnd,
70
+                                CardDavBackend $cardDavBackEnd,
71
+                                GroupPrincipalBackend $principalBackend,
72
+                                IConfig $config,
73
+                                IDBConnection $dbConnection,
74
+                                IL10N $l10n) {
75
+        $this->calDavBackEnd = $calDavBackEnd;
76
+        $this->cardDavBackEnd = $cardDavBackEnd;
77
+        $this->principalBackend = $principalBackend;
78
+        $this->config = $config;
79
+        $this->dbConnection = $dbConnection;
80
+        $this->l10n = $l10n;
81
+    }
82
+
83
+    public function onCardChanged(int $addressBookId,
84
+                                    string $cardUri,
85
+                                    string $cardData): void {
86
+        if (!$this->isGloballyEnabled()) {
87
+            return;
88
+        }
89
+
90
+        $targetPrincipals = $this->getAllAffectedPrincipals($addressBookId);
91
+        $book = $this->cardDavBackEnd->getAddressBookById($addressBookId);
92
+        if ($book === null) {
93
+            return;
94
+        }
95
+        $targetPrincipals[] = $book['principaluri'];
96
+        $datesToSync = [
97
+            ['postfix' => '', 'field' => 'BDAY'],
98
+            ['postfix' => '-death', 'field' => 'DEATHDATE'],
99
+            ['postfix' => '-anniversary', 'field' => 'ANNIVERSARY'],
100
+        ];
101
+
102
+        foreach ($targetPrincipals as $principalUri) {
103
+            if (!$this->isUserEnabled($principalUri)) {
104
+                continue;
105
+            }
106
+
107
+            $reminderOffset = $this->getReminderOffsetForUser($principalUri);
108
+
109
+            $calendar = $this->ensureCalendarExists($principalUri);
110
+            if ($calendar === null) {
111
+                return;
112
+            }
113
+            foreach ($datesToSync as $type) {
114
+                $this->updateCalendar($cardUri, $cardData, $book, (int) $calendar['id'], $type, $reminderOffset);
115
+            }
116
+        }
117
+    }
118
+
119
+    public function onCardDeleted(int $addressBookId,
120
+                                    string $cardUri): void {
121
+        if (!$this->isGloballyEnabled()) {
122
+            return;
123
+        }
124
+
125
+        $targetPrincipals = $this->getAllAffectedPrincipals($addressBookId);
126
+        $book = $this->cardDavBackEnd->getAddressBookById($addressBookId);
127
+        $targetPrincipals[] = $book['principaluri'];
128
+        foreach ($targetPrincipals as $principalUri) {
129
+            if (!$this->isUserEnabled($principalUri)) {
130
+                continue;
131
+            }
132
+
133
+            $calendar = $this->ensureCalendarExists($principalUri);
134
+            foreach (['', '-death', '-anniversary'] as $tag) {
135
+                $objectUri = $book['uri'] . '-' . $cardUri . $tag .'.ics';
136
+                $this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri, CalDavBackend::CALENDAR_TYPE_CALENDAR, true);
137
+            }
138
+        }
139
+    }
140
+
141
+    /**
142
+     * @throws \Sabre\DAV\Exception\BadRequest
143
+     */
144
+    public function ensureCalendarExists(string $principal): ?array {
145
+        $calendar = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI);
146
+        if (!is_null($calendar)) {
147
+            return $calendar;
148
+        }
149
+        $this->calDavBackEnd->createCalendar($principal, self::BIRTHDAY_CALENDAR_URI, [
150
+            '{DAV:}displayname' => $this->l10n->t('Contact birthdays'),
151
+            '{http://apple.com/ns/ical/}calendar-color' => '#E9D859',
152
+            'components' => 'VEVENT',
153
+        ]);
154
+
155
+        return $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI);
156
+    }
157
+
158
+    /**
159
+     * @param $cardData
160
+     * @param $dateField
161
+     * @param $postfix
162
+     * @param $reminderOffset
163
+     * @return VCalendar|null
164
+     * @throws InvalidDataException
165
+     */
166
+    public function buildDateFromContact(string  $cardData,
167
+                                            string  $dateField,
168
+                                            string  $postfix,
169
+                                         ?string $reminderOffset):?VCalendar {
170
+        if (empty($cardData)) {
171
+            return null;
172
+        }
173
+        try {
174
+            $doc = Reader::read($cardData);
175
+            // We're always converting to vCard 4.0 so we can rely on the
176
+            // VCardConverter handling the X-APPLE-OMIT-YEAR property for us.
177
+            if (!$doc instanceof VCard) {
178
+                return null;
179
+            }
180
+            $doc = $doc->convert(Document::VCARD40);
181
+        } catch (Exception $e) {
182
+            return null;
183
+        }
184
+
185
+        if (isset($doc->{self::EXCLUDE_FROM_BIRTHDAY_CALENDAR_PROPERTY_NAME})) {
186
+            return null;
187
+        }
188
+
189
+        if (!isset($doc->{$dateField})) {
190
+            return null;
191
+        }
192
+        if (!isset($doc->FN)) {
193
+            return null;
194
+        }
195
+        $birthday = $doc->{$dateField};
196
+        if (!(string)$birthday) {
197
+            return null;
198
+        }
199
+        // Skip if the BDAY property is not of the right type.
200
+        if (!$birthday instanceof DateAndOrTime) {
201
+            return null;
202
+        }
203
+
204
+        // Skip if we can't parse the BDAY value.
205
+        try {
206
+            $dateParts = DateTimeParser::parseVCardDateTime($birthday->getValue());
207
+        } catch (InvalidDataException $e) {
208
+            return null;
209
+        }
210
+        if ($dateParts['year'] !== null) {
211
+            $parameters = $birthday->parameters();
212
+            $omitYear = (isset($parameters['X-APPLE-OMIT-YEAR'])
213
+                    && $parameters['X-APPLE-OMIT-YEAR'] === $dateParts['year']);
214
+            // '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
215
+            if ($omitYear || (int)$dateParts['year'] === 1604) {
216
+                $dateParts['year'] = null;
217
+            }
218
+        }
219
+
220
+        $originalYear = null;
221
+        if ($dateParts['year'] !== null) {
222
+            $originalYear = (int)$dateParts['year'];
223
+        }
224
+
225
+        $leapDay = ((int)$dateParts['month'] === 2
226
+                && (int)$dateParts['date'] === 29);
227
+        if ($dateParts['year'] === null || $originalYear < 1970) {
228
+            $birthday = ($leapDay ? '1972-' : '1970-')
229
+                . $dateParts['month'] . '-' . $dateParts['date'];
230
+        }
231
+
232
+        try {
233
+            if ($birthday instanceof DateAndOrTime) {
234
+                $date = $birthday->getDateTime();
235
+            } else {
236
+                $date = new \DateTimeImmutable($birthday);
237
+            }
238
+        } catch (Exception $e) {
239
+            return null;
240
+        }
241
+
242
+        $summary = $this->formatTitle($dateField, $doc->FN->getValue(), $originalYear, $this->dbConnection->supports4ByteText());
243
+
244
+        $vCal = new VCalendar();
245
+        $vCal->VERSION = '2.0';
246
+        $vCal->PRODID = '-//IDN nextcloud.com//Birthday calendar//EN';
247
+        $vEvent = $vCal->createComponent('VEVENT');
248
+        $vEvent->add('DTSTART');
249
+        $vEvent->DTSTART->setDateTime(
250
+            $date
251
+        );
252
+        $vEvent->DTSTART['VALUE'] = 'DATE';
253
+        $vEvent->add('DTEND');
254
+
255
+        $dtEndDate = (new \DateTime())->setTimestamp($date->getTimeStamp());
256
+        $dtEndDate->add(new \DateInterval('P1D'));
257
+        $vEvent->DTEND->setDateTime(
258
+            $dtEndDate
259
+        );
260
+
261
+        $vEvent->DTEND['VALUE'] = 'DATE';
262
+        $vEvent->{'UID'} = $doc->UID . $postfix;
263
+        $vEvent->{'RRULE'} = 'FREQ=YEARLY';
264
+        if ($leapDay) {
265
+            /* Sabre\VObject supports BYMONTHDAY only if BYMONTH
266 266
 			 * is also set */
267
-			$vEvent->{'RRULE'} = 'FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=-1';
268
-		}
269
-		$vEvent->{'SUMMARY'} = $summary;
270
-		$vEvent->{'TRANSP'} = 'TRANSPARENT';
271
-		$vEvent->{'X-NEXTCLOUD-BC-FIELD-TYPE'} = $dateField;
272
-		$vEvent->{'X-NEXTCLOUD-BC-UNKNOWN-YEAR'} = $dateParts['year'] === null ? '1' : '0';
273
-		if ($originalYear !== null) {
274
-			$vEvent->{'X-NEXTCLOUD-BC-YEAR'} = (string) $originalYear;
275
-		}
276
-		if ($reminderOffset) {
277
-			$alarm = $vCal->createComponent('VALARM');
278
-			$alarm->add($vCal->createProperty('TRIGGER', $reminderOffset, ['VALUE' => 'DURATION']));
279
-			$alarm->add($vCal->createProperty('ACTION', 'DISPLAY'));
280
-			$alarm->add($vCal->createProperty('DESCRIPTION', $vEvent->{'SUMMARY'}));
281
-			$vEvent->add($alarm);
282
-		}
283
-		$vCal->add($vEvent);
284
-		return $vCal;
285
-	}
286
-
287
-	/**
288
-	 * @param string $user
289
-	 */
290
-	public function resetForUser(string $user):void {
291
-		$principal = 'principals/users/'.$user;
292
-		$calendar = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI);
293
-		if (!$calendar) {
294
-			return; // The user's birthday calendar doesn't exist, no need to purge it
295
-		}
296
-		$calendarObjects = $this->calDavBackEnd->getCalendarObjects($calendar['id'], CalDavBackend::CALENDAR_TYPE_CALENDAR);
297
-
298
-		foreach ($calendarObjects as $calendarObject) {
299
-			$this->calDavBackEnd->deleteCalendarObject($calendar['id'], $calendarObject['uri'], CalDavBackend::CALENDAR_TYPE_CALENDAR, true);
300
-		}
301
-	}
302
-
303
-	/**
304
-	 * @param string $user
305
-	 * @throws \Sabre\DAV\Exception\BadRequest
306
-	 */
307
-	public function syncUser(string $user):void {
308
-		$principal = 'principals/users/'.$user;
309
-		$this->ensureCalendarExists($principal);
310
-		$books = $this->cardDavBackEnd->getAddressBooksForUser($principal);
311
-		foreach ($books as $book) {
312
-			$cards = $this->cardDavBackEnd->getCards($book['id']);
313
-			foreach ($cards as $card) {
314
-				$this->onCardChanged((int) $book['id'], $card['uri'], $card['carddata']);
315
-			}
316
-		}
317
-	}
318
-
319
-	/**
320
-	 * @param string $existingCalendarData
321
-	 * @param VCalendar $newCalendarData
322
-	 * @return bool
323
-	 */
324
-	public function birthdayEvenChanged(string $existingCalendarData,
325
-										VCalendar $newCalendarData):bool {
326
-		try {
327
-			$existingBirthday = Reader::read($existingCalendarData);
328
-		} catch (Exception $ex) {
329
-			return true;
330
-		}
331
-
332
-		return (
333
-			$newCalendarData->VEVENT->DTSTART->getValue() !== $existingBirthday->VEVENT->DTSTART->getValue() ||
334
-			$newCalendarData->VEVENT->SUMMARY->getValue() !== $existingBirthday->VEVENT->SUMMARY->getValue()
335
-		);
336
-	}
337
-
338
-	/**
339
-	 * @param integer $addressBookId
340
-	 * @return mixed
341
-	 */
342
-	protected function getAllAffectedPrincipals(int $addressBookId) {
343
-		$targetPrincipals = [];
344
-		$shares = $this->cardDavBackEnd->getShares($addressBookId);
345
-		foreach ($shares as $share) {
346
-			if ($share['{http://owncloud.org/ns}group-share']) {
347
-				$users = $this->principalBackend->getGroupMemberSet($share['{http://owncloud.org/ns}principal']);
348
-				foreach ($users as $user) {
349
-					$targetPrincipals[] = $user['uri'];
350
-				}
351
-			} else {
352
-				$targetPrincipals[] = $share['{http://owncloud.org/ns}principal'];
353
-			}
354
-		}
355
-		return array_values(array_unique($targetPrincipals, SORT_STRING));
356
-	}
357
-
358
-	/**
359
-	 * @param string $cardUri
360
-	 * @param string $cardData
361
-	 * @param array $book
362
-	 * @param int $calendarId
363
-	 * @param array $type
364
-	 * @param string $reminderOffset
365
-	 * @throws InvalidDataException
366
-	 * @throws \Sabre\DAV\Exception\BadRequest
367
-	 */
368
-	private function updateCalendar(string $cardUri,
369
-									string $cardData,
370
-									array $book,
371
-									int $calendarId,
372
-									array $type,
373
-									?string $reminderOffset):void {
374
-		$objectUri = $book['uri'] . '-' . $cardUri . $type['postfix'] . '.ics';
375
-		$calendarData = $this->buildDateFromContact($cardData, $type['field'], $type['postfix'], $reminderOffset);
376
-		$existing = $this->calDavBackEnd->getCalendarObject($calendarId, $objectUri);
377
-		if ($calendarData === null) {
378
-			if ($existing !== null) {
379
-				$this->calDavBackEnd->deleteCalendarObject($calendarId, $objectUri, CalDavBackend::CALENDAR_TYPE_CALENDAR, true);
380
-			}
381
-		} else {
382
-			if ($existing === null) {
383
-				// not found by URI, but maybe by UID
384
-				// happens when a contact with birthday is moved to a different address book
385
-				$calendarInfo = $this->calDavBackEnd->getCalendarById($calendarId);
386
-				$extraData = $this->calDavBackEnd->getDenormalizedData($calendarData->serialize());
387
-
388
-				if ($calendarInfo && array_key_exists('principaluri', $calendarInfo)) {
389
-					$existing2path = $this->calDavBackEnd->getCalendarObjectByUID($calendarInfo['principaluri'], $extraData['uid']);
390
-					if ($existing2path !== null && array_key_exists('uri', $calendarInfo)) {
391
-						// delete the old birthday entry first so that we do not get duplicate UIDs
392
-						$existing2objectUri = substr($existing2path, strlen($calendarInfo['uri']) + 1);
393
-						$this->calDavBackEnd->deleteCalendarObject($calendarId, $existing2objectUri, CalDavBackend::CALENDAR_TYPE_CALENDAR, true);
394
-					}
395
-				}
396
-
397
-				$this->calDavBackEnd->createCalendarObject($calendarId, $objectUri, $calendarData->serialize());
398
-			} else {
399
-				if ($this->birthdayEvenChanged($existing['calendardata'], $calendarData)) {
400
-					$this->calDavBackEnd->updateCalendarObject($calendarId, $objectUri, $calendarData->serialize());
401
-				}
402
-			}
403
-		}
404
-	}
405
-
406
-	/**
407
-	 * checks if the admin opted-out of birthday calendars
408
-	 *
409
-	 * @return bool
410
-	 */
411
-	private function isGloballyEnabled():bool {
412
-		return $this->config->getAppValue('dav', 'generateBirthdayCalendar', 'yes') === 'yes';
413
-	}
414
-
415
-	/**
416
-	 * Extracts the userId part of a principal
417
-	 *
418
-	 * @param string $userPrincipal
419
-	 * @return string|null
420
-	 */
421
-	private function principalToUserId(string $userPrincipal):?string {
422
-		if (substr($userPrincipal, 0, 17) === 'principals/users/') {
423
-			return substr($userPrincipal, 17);
424
-		}
425
-		return null;
426
-	}
427
-
428
-	/**
429
-	 * Checks if the user opted-out of birthday calendars
430
-	 *
431
-	 * @param string $userPrincipal The user principal to check for
432
-	 * @return bool
433
-	 */
434
-	private function isUserEnabled(string $userPrincipal):bool {
435
-		$userId = $this->principalToUserId($userPrincipal);
436
-		if ($userId !== null) {
437
-			$isEnabled = $this->config->getUserValue($userId, 'dav', 'generateBirthdayCalendar', 'yes');
438
-			return $isEnabled === 'yes';
439
-		}
440
-
441
-		// not sure how we got here, just be on the safe side and return true
442
-		return true;
443
-	}
444
-
445
-	/**
446
-	 * Get the reminder offset value for a user. This is a duration string (e.g.
447
-	 * PT9H) or null if no reminder is wanted.
448
-	 *
449
-	 * @param string $userPrincipal
450
-	 * @return string|null
451
-	 */
452
-	private function getReminderOffsetForUser(string $userPrincipal):?string {
453
-		$userId = $this->principalToUserId($userPrincipal);
454
-		if ($userId !== null) {
455
-			return $this->config->getUserValue($userId, 'dav', 'birthdayCalendarReminderOffset', 'PT9H') ?: null;
456
-		}
457
-
458
-		// not sure how we got here, just be on the safe side and return the default value
459
-		return 'PT9H';
460
-	}
461
-
462
-	/**
463
-	 * Formats title of Birthday event
464
-	 *
465
-	 * @param string $field Field name like BDAY, ANNIVERSARY, ...
466
-	 * @param string $name Name of contact
467
-	 * @param int|null $year Year of birth, anniversary, ...
468
-	 * @param bool $supports4Byte Whether or not the database supports 4 byte chars
469
-	 * @return string The formatted title
470
-	 */
471
-	private function formatTitle(string $field,
472
-								 string $name,
473
-								 int $year = null,
474
-								 bool $supports4Byte = true):string {
475
-		if ($supports4Byte) {
476
-			switch ($field) {
477
-				case 'BDAY':
478
-					return implode('', [
479
-						'
Please login to merge, or discard this patch.
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
 
133 133
 			$calendar = $this->ensureCalendarExists($principalUri);
134 134
 			foreach (['', '-death', '-anniversary'] as $tag) {
135
-				$objectUri = $book['uri'] . '-' . $cardUri . $tag .'.ics';
135
+				$objectUri = $book['uri'].'-'.$cardUri.$tag.'.ics';
136 136
 				$this->calDavBackEnd->deleteCalendarObject($calendar['id'], $objectUri, CalDavBackend::CALENDAR_TYPE_CALENDAR, true);
137 137
 			}
138 138
 		}
@@ -166,7 +166,7 @@  discard block
 block discarded – undo
166 166
 	public function buildDateFromContact(string  $cardData,
167 167
 										 string  $dateField,
168 168
 										 string  $postfix,
169
-										 ?string $reminderOffset):?VCalendar {
169
+										 ?string $reminderOffset): ?VCalendar {
170 170
 		if (empty($cardData)) {
171 171
 			return null;
172 172
 		}
@@ -193,7 +193,7 @@  discard block
 block discarded – undo
193 193
 			return null;
194 194
 		}
195 195
 		$birthday = $doc->{$dateField};
196
-		if (!(string)$birthday) {
196
+		if (!(string) $birthday) {
197 197
 			return null;
198 198
 		}
199 199
 		// Skip if the BDAY property is not of the right type.
@@ -212,21 +212,21 @@  discard block
 block discarded – undo
212 212
 			$omitYear = (isset($parameters['X-APPLE-OMIT-YEAR'])
213 213
 					&& $parameters['X-APPLE-OMIT-YEAR'] === $dateParts['year']);
214 214
 			// '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
215
-			if ($omitYear || (int)$dateParts['year'] === 1604) {
215
+			if ($omitYear || (int) $dateParts['year'] === 1604) {
216 216
 				$dateParts['year'] = null;
217 217
 			}
218 218
 		}
219 219
 
220 220
 		$originalYear = null;
221 221
 		if ($dateParts['year'] !== null) {
222
-			$originalYear = (int)$dateParts['year'];
222
+			$originalYear = (int) $dateParts['year'];
223 223
 		}
224 224
 
225
-		$leapDay = ((int)$dateParts['month'] === 2
226
-				&& (int)$dateParts['date'] === 29);
225
+		$leapDay = ((int) $dateParts['month'] === 2
226
+				&& (int) $dateParts['date'] === 29);
227 227
 		if ($dateParts['year'] === null || $originalYear < 1970) {
228 228
 			$birthday = ($leapDay ? '1972-' : '1970-')
229
-				. $dateParts['month'] . '-' . $dateParts['date'];
229
+				. $dateParts['month'].'-'.$dateParts['date'];
230 230
 		}
231 231
 
232 232
 		try {
@@ -259,7 +259,7 @@  discard block
 block discarded – undo
259 259
 		);
260 260
 
261 261
 		$vEvent->DTEND['VALUE'] = 'DATE';
262
-		$vEvent->{'UID'} = $doc->UID . $postfix;
262
+		$vEvent->{'UID'} = $doc->UID.$postfix;
263 263
 		$vEvent->{'RRULE'} = 'FREQ=YEARLY';
264 264
 		if ($leapDay) {
265 265
 			/* Sabre\VObject supports BYMONTHDAY only if BYMONTH
@@ -371,7 +371,7 @@  discard block
 block discarded – undo
371 371
 									int $calendarId,
372 372
 									array $type,
373 373
 									?string $reminderOffset):void {
374
-		$objectUri = $book['uri'] . '-' . $cardUri . $type['postfix'] . '.ics';
374
+		$objectUri = $book['uri'].'-'.$cardUri.$type['postfix'].'.ics';
375 375
 		$calendarData = $this->buildDateFromContact($cardData, $type['field'], $type['postfix'], $reminderOffset);
376 376
 		$existing = $this->calDavBackEnd->getCalendarObject($calendarId, $objectUri);
377 377
 		if ($calendarData === null) {
@@ -418,7 +418,7 @@  discard block
 block discarded – undo
418 418
 	 * @param string $userPrincipal
419 419
 	 * @return string|null
420 420
 	 */
421
-	private function principalToUserId(string $userPrincipal):?string {
421
+	private function principalToUserId(string $userPrincipal): ?string {
422 422
 		if (substr($userPrincipal, 0, 17) === 'principals/users/') {
423 423
 			return substr($userPrincipal, 17);
424 424
 		}
@@ -449,7 +449,7 @@  discard block
 block discarded – undo
449 449
 	 * @param string $userPrincipal
450 450
 	 * @return string|null
451 451
 	 */
452
-	private function getReminderOffsetForUser(string $userPrincipal):?string {
452
+	private function getReminderOffsetForUser(string $userPrincipal): ?string {
453 453
 		$userId = $this->principalToUserId($userPrincipal);
454 454
 		if ($userId !== null) {
455 455
 			return $this->config->getUserValue($userId, 'dav', 'birthdayCalendarReminderOffset', 'PT9H') ?: null;
@@ -478,20 +478,20 @@  discard block
 block discarded – undo
478 478
 					return implode('', [
479 479
 						'
Please login to merge, or discard this patch.