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