Passed
Push — master ( 962901...d43a47 )
by Roeland
09:25 queued 10s
created
apps/dav/lib/CalDAV/BirthdayService.php 2 patches
Indentation   +430 added lines, -430 removed lines patch added patch discarded remove patch
@@ -50,434 +50,434 @@
 block discarded – undo
50 50
  */
51 51
 class BirthdayService {
52 52
 
53
-	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' => '#FFFFCA',
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
-		$vEvent = $vCal->createComponent('VEVENT');
260
-		$vEvent->add('DTSTART');
261
-		$vEvent->DTSTART->setDateTime(
262
-			$date
263
-		);
264
-		$vEvent->DTSTART['VALUE'] = 'DATE';
265
-		$vEvent->add('DTEND');
266
-
267
-		$dtEndDate = (new \DateTime())->setTimestamp($date->getTimeStamp());
268
-		$dtEndDate->add(new \DateInterval('P1D'));
269
-		$vEvent->DTEND->setDateTime(
270
-			$dtEndDate
271
-		);
272
-
273
-		$vEvent->DTEND['VALUE'] = 'DATE';
274
-		$vEvent->{'UID'} = $doc->UID . $postfix;
275
-		$vEvent->{'RRULE'} = 'FREQ=YEARLY';
276
-		$vEvent->{'SUMMARY'} = $summary;
277
-		$vEvent->{'TRANSP'} = 'TRANSPARENT';
278
-		$vEvent->{'X-NEXTCLOUD-BC-FIELD-TYPE'} = $dateField;
279
-		$vEvent->{'X-NEXTCLOUD-BC-UNKNOWN-YEAR'} = $unknownYear ? '1' : '0';
280
-		if ($originalYear !== null) {
281
-			$vEvent->{'X-NEXTCLOUD-BC-YEAR'} = (string) $originalYear;
282
-		}
283
-		$alarm = $vCal->createComponent('VALARM');
284
-		$alarm->add($vCal->createProperty('TRIGGER', '-PT0M', ['VALUE' => 'DURATION']));
285
-		$alarm->add($vCal->createProperty('ACTION', 'DISPLAY'));
286
-		$alarm->add($vCal->createProperty('DESCRIPTION', $vEvent->{'SUMMARY'}));
287
-		$vEvent->add($alarm);
288
-		$vCal->add($vEvent);
289
-		return $vCal;
290
-	}
291
-
292
-	/**
293
-	 * @param string $user
294
-	 */
295
-	public function resetForUser(string $user):void {
296
-		$principal = 'principals/users/'.$user;
297
-		$calendar = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI);
298
-		$calendarObjects = $this->calDavBackEnd->getCalendarObjects($calendar['id'], CalDavBackend::CALENDAR_TYPE_CALENDAR);
299
-
300
-		foreach($calendarObjects as $calendarObject) {
301
-			$this->calDavBackEnd->deleteCalendarObject($calendar['id'], $calendarObject['uri'], CalDavBackend::CALENDAR_TYPE_CALENDAR);
302
-		}
303
-	}
304
-
305
-	/**
306
-	 * @param string $user
307
-	 * @throws \Sabre\DAV\Exception\BadRequest
308
-	 */
309
-	public function syncUser(string $user):void {
310
-		$principal = 'principals/users/'.$user;
311
-		$this->ensureCalendarExists($principal);
312
-		$books = $this->cardDavBackEnd->getAddressBooksForUser($principal);
313
-		foreach($books as $book) {
314
-			$cards = $this->cardDavBackEnd->getCards($book['id']);
315
-			foreach($cards as $card) {
316
-				$this->onCardChanged((int) $book['id'], $card['uri'], $card['carddata']);
317
-			}
318
-		}
319
-	}
320
-
321
-	/**
322
-	 * @param string $existingCalendarData
323
-	 * @param VCalendar $newCalendarData
324
-	 * @return bool
325
-	 */
326
-	public function birthdayEvenChanged(string $existingCalendarData,
327
-										VCalendar $newCalendarData):bool {
328
-		try {
329
-			$existingBirthday = Reader::read($existingCalendarData);
330
-		} catch (Exception $ex) {
331
-			return true;
332
-		}
333
-
334
-		return (
335
-			$newCalendarData->VEVENT->DTSTART->getValue() !== $existingBirthday->VEVENT->DTSTART->getValue() ||
336
-			$newCalendarData->VEVENT->SUMMARY->getValue() !== $existingBirthday->VEVENT->SUMMARY->getValue()
337
-		);
338
-	}
339
-
340
-	/**
341
-	 * @param integer $addressBookId
342
-	 * @return mixed
343
-	 */
344
-	protected function getAllAffectedPrincipals(int $addressBookId) {
345
-		$targetPrincipals = [];
346
-		$shares = $this->cardDavBackEnd->getShares($addressBookId);
347
-		foreach ($shares as $share) {
348
-			if ($share['{http://owncloud.org/ns}group-share']) {
349
-				$users = $this->principalBackend->getGroupMemberSet($share['{http://owncloud.org/ns}principal']);
350
-				foreach ($users as $user) {
351
-					$targetPrincipals[] = $user['uri'];
352
-				}
353
-			} else {
354
-				$targetPrincipals[] = $share['{http://owncloud.org/ns}principal'];
355
-			}
356
-		}
357
-		return array_values(array_unique($targetPrincipals, SORT_STRING));
358
-	}
359
-
360
-	/**
361
-	 * @param string $cardUri
362
-	 * @param string $cardData
363
-	 * @param array $book
364
-	 * @param int $calendarId
365
-	 * @param array $type
366
-	 * @throws InvalidDataException
367
-	 * @throws \Sabre\DAV\Exception\BadRequest
368
-	 */
369
-	private function updateCalendar(string $cardUri,
370
-									string $cardData,
371
-									array $book,
372
-									int $calendarId,
373
-									array $type):void {
374
-		$objectUri = $book['uri'] . '-' . $cardUri . $type['postfix'] . '.ics';
375
-		$calendarData = $this->buildDateFromContact($cardData, $type['field'], $type['postfix']);
376
-		$existing = $this->calDavBackEnd->getCalendarObject($calendarId, $objectUri);
377
-		if (is_null($calendarData)) {
378
-			if (!is_null($existing)) {
379
-				$this->calDavBackEnd->deleteCalendarObject($calendarId, $objectUri);
380
-			}
381
-		} else {
382
-			if (is_null($existing)) {
383
-				$this->calDavBackEnd->createCalendarObject($calendarId, $objectUri, $calendarData->serialize());
384
-			} else {
385
-				if ($this->birthdayEvenChanged($existing['calendardata'], $calendarData)) {
386
-					$this->calDavBackEnd->updateCalendarObject($calendarId, $objectUri, $calendarData->serialize());
387
-				}
388
-			}
389
-		}
390
-	}
391
-
392
-	/**
393
-	 * checks if the admin opted-out of birthday calendars
394
-	 *
395
-	 * @return bool
396
-	 */
397
-	private function isGloballyEnabled():bool {
398
-		return $this->config->getAppValue('dav', 'generateBirthdayCalendar', 'yes') === 'yes';
399
-	}
400
-
401
-	/**
402
-	 * Checks if the user opted-out of birthday calendars
403
-	 *
404
-	 * @param string $userPrincipal The user principal to check for
405
-	 * @return bool
406
-	 */
407
-	private function isUserEnabled(string $userPrincipal):bool {
408
-		if (strpos($userPrincipal, 'principals/users/') === 0) {
409
-			$userId = substr($userPrincipal, 17);
410
-			$isEnabled = $this->config->getUserValue($userId, 'dav', 'generateBirthdayCalendar', 'yes');
411
-			return $isEnabled === 'yes';
412
-		}
413
-
414
-		// not sure how we got here, just be on the safe side and return true
415
-		return true;
416
-	}
417
-
418
-	/**
419
-	 * Formats title of Birthday event
420
-	 *
421
-	 * @param string $field Field name like BDAY, ANNIVERSARY, ...
422
-	 * @param string $name Name of contact
423
-	 * @param int|null $year Year of birth, anniversary, ...
424
-	 * @param bool $supports4Byte Whether or not the database supports 4 byte chars
425
-	 * @return string The formatted title
426
-	 */
427
-	private function formatTitle(string $field,
428
-								 string $name,
429
-								 int $year=null,
430
-								 bool $supports4Byte=true):string {
431
-		if ($supports4Byte) {
432
-			switch ($field) {
433
-				case 'BDAY':
434
-					return implode('', [
435
-						'
Please login to merge, or discard this patch.
Spacing   +22 added lines, -22 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
 		}
@@ -271,7 +271,7 @@  discard block
 block discarded – undo
271 271
 		);
272 272
 
273 273
 		$vEvent->DTEND['VALUE'] = 'DATE';
274
-		$vEvent->{'UID'} = $doc->UID . $postfix;
274
+		$vEvent->{'UID'} = $doc->UID.$postfix;
275 275
 		$vEvent->{'RRULE'} = 'FREQ=YEARLY';
276 276
 		$vEvent->{'SUMMARY'} = $summary;
277 277
 		$vEvent->{'TRANSP'} = 'TRANSPARENT';
@@ -297,7 +297,7 @@  discard block
 block discarded – undo
297 297
 		$calendar = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI);
298 298
 		$calendarObjects = $this->calDavBackEnd->getCalendarObjects($calendar['id'], CalDavBackend::CALENDAR_TYPE_CALENDAR);
299 299
 
300
-		foreach($calendarObjects as $calendarObject) {
300
+		foreach ($calendarObjects as $calendarObject) {
301 301
 			$this->calDavBackEnd->deleteCalendarObject($calendar['id'], $calendarObject['uri'], CalDavBackend::CALENDAR_TYPE_CALENDAR);
302 302
 		}
303 303
 	}
@@ -310,9 +310,9 @@  discard block
 block discarded – undo
310 310
 		$principal = 'principals/users/'.$user;
311 311
 		$this->ensureCalendarExists($principal);
312 312
 		$books = $this->cardDavBackEnd->getAddressBooksForUser($principal);
313
-		foreach($books as $book) {
313
+		foreach ($books as $book) {
314 314
 			$cards = $this->cardDavBackEnd->getCards($book['id']);
315
-			foreach($cards as $card) {
315
+			foreach ($cards as $card) {
316 316
 				$this->onCardChanged((int) $book['id'], $card['uri'], $card['carddata']);
317 317
 			}
318 318
 		}
@@ -371,7 +371,7 @@  discard block
 block discarded – undo
371 371
 									array $book,
372 372
 									int $calendarId,
373 373
 									array $type):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']);
376 376
 		$existing = $this->calDavBackEnd->getCalendarObject($calendarId, $objectUri);
377 377
 		if (is_null($calendarData)) {
@@ -426,53 +426,53 @@  discard block
 block discarded – undo
426 426
 	 */
427 427
 	private function formatTitle(string $field,
428 428
 								 string $name,
429
-								 int $year=null,
430
-								 bool $supports4Byte=true):string {
429
+								 int $year = null,
430
+								 bool $supports4Byte = true):string {
431 431
 		if ($supports4Byte) {
432 432
 			switch ($field) {
433 433
 				case 'BDAY':
434 434
 					return implode('', [
435 435
 						'
Please login to merge, or discard this patch.