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