Passed
Push — master ( 280cc4...6e0101 )
by Christoph
14:56 queued 13s
created
apps/dav/lib/CalDAV/Reminder/ReminderService.php 1 patch
Indentation   +728 added lines, -728 removed lines patch added patch discarded remove patch
@@ -48,732 +48,732 @@
 block discarded – undo
48 48
 
49 49
 class ReminderService {
50 50
 
51
-	/** @var Backend */
52
-	private $backend;
53
-
54
-	/** @var NotificationProviderManager */
55
-	private $notificationProviderManager;
56
-
57
-	/** @var IUserManager */
58
-	private $userManager;
59
-
60
-	/** @var IGroupManager */
61
-	private $groupManager;
62
-
63
-	/** @var CalDavBackend */
64
-	private $caldavBackend;
65
-
66
-	/** @var ITimeFactory */
67
-	private $timeFactory;
68
-
69
-	public const REMINDER_TYPE_EMAIL = 'EMAIL';
70
-	public const REMINDER_TYPE_DISPLAY = 'DISPLAY';
71
-	public const REMINDER_TYPE_AUDIO = 'AUDIO';
72
-
73
-	/**
74
-	 * @var String[]
75
-	 *
76
-	 * Official RFC5545 reminder types
77
-	 */
78
-	public const REMINDER_TYPES = [
79
-		self::REMINDER_TYPE_EMAIL,
80
-		self::REMINDER_TYPE_DISPLAY,
81
-		self::REMINDER_TYPE_AUDIO
82
-	];
83
-
84
-	/**
85
-	 * ReminderService constructor.
86
-	 *
87
-	 * @param Backend $backend
88
-	 * @param NotificationProviderManager $notificationProviderManager
89
-	 * @param IUserManager $userManager
90
-	 * @param IGroupManager $groupManager
91
-	 * @param CalDavBackend $caldavBackend
92
-	 * @param ITimeFactory $timeFactory
93
-	 */
94
-	public function __construct(Backend $backend,
95
-								NotificationProviderManager $notificationProviderManager,
96
-								IUserManager $userManager,
97
-								IGroupManager $groupManager,
98
-								CalDavBackend $caldavBackend,
99
-								ITimeFactory $timeFactory) {
100
-		$this->backend = $backend;
101
-		$this->notificationProviderManager = $notificationProviderManager;
102
-		$this->userManager = $userManager;
103
-		$this->groupManager = $groupManager;
104
-		$this->caldavBackend = $caldavBackend;
105
-		$this->timeFactory = $timeFactory;
106
-	}
107
-
108
-	/**
109
-	 * Process reminders to activate
110
-	 *
111
-	 * @throws NotificationProvider\ProviderNotAvailableException
112
-	 * @throws NotificationTypeDoesNotExistException
113
-	 */
114
-	public function processReminders():void {
115
-		$reminders = $this->backend->getRemindersToProcess();
116
-
117
-		foreach ($reminders as $reminder) {
118
-			$calendarData = is_resource($reminder['calendardata'])
119
-				? stream_get_contents($reminder['calendardata'])
120
-				: $reminder['calendardata'];
121
-
122
-			if (!$calendarData) {
123
-				continue;
124
-			}
125
-
126
-			$vcalendar = $this->parseCalendarData($calendarData);
127
-			if (!$vcalendar) {
128
-				$this->backend->removeReminder($reminder['id']);
129
-				continue;
130
-			}
131
-
132
-			$vevent = $this->getVEventByRecurrenceId($vcalendar, $reminder['recurrence_id'], $reminder['is_recurrence_exception']);
133
-			if (!$vevent) {
134
-				$this->backend->removeReminder($reminder['id']);
135
-				continue;
136
-			}
137
-
138
-			if ($this->wasEventCancelled($vevent)) {
139
-				$this->deleteOrProcessNext($reminder, $vevent);
140
-				continue;
141
-			}
142
-
143
-			if (!$this->notificationProviderManager->hasProvider($reminder['type'])) {
144
-				$this->deleteOrProcessNext($reminder, $vevent);
145
-				continue;
146
-			}
147
-
148
-			$users = $this->getAllUsersWithWriteAccessToCalendar($reminder['calendar_id']);
149
-			$user = $this->getUserFromPrincipalURI($reminder['principaluri']);
150
-			if ($user) {
151
-				$users[] = $user;
152
-			}
153
-
154
-			$notificationProvider = $this->notificationProviderManager->getProvider($reminder['type']);
155
-			$notificationProvider->send($vevent, $reminder['displayname'], $users);
156
-
157
-			$this->deleteOrProcessNext($reminder, $vevent);
158
-		}
159
-	}
160
-
161
-	/**
162
-	 * @param array $objectData
163
-	 * @throws VObject\InvalidDataException
164
-	 */
165
-	public function onCalendarObjectCreate(array $objectData):void {
166
-		// We only support VEvents for now
167
-		if (strcasecmp($objectData['component'], 'vevent') !== 0) {
168
-			return;
169
-		}
170
-
171
-		$calendarData = is_resource($objectData['calendardata'])
172
-			? stream_get_contents($objectData['calendardata'])
173
-			: $objectData['calendardata'];
174
-
175
-		if (!$calendarData) {
176
-			return;
177
-		}
178
-
179
-		/** @var VObject\Component\VCalendar $vcalendar */
180
-		$vcalendar = $this->parseCalendarData($calendarData);
181
-		if (!$vcalendar) {
182
-			return;
183
-		}
184
-
185
-		$vevents = $this->getAllVEventsFromVCalendar($vcalendar);
186
-		if (count($vevents) === 0) {
187
-			return;
188
-		}
189
-
190
-		$uid = (string) $vevents[0]->UID;
191
-		$recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
192
-		$masterItem = $this->getMasterItemFromListOfVEvents($vevents);
193
-		$now = $this->timeFactory->getDateTime();
194
-		$isRecurring = $masterItem ? $this->isRecurring($masterItem) : false;
195
-
196
-		foreach ($recurrenceExceptions as $recurrenceException) {
197
-			$eventHash = $this->getEventHash($recurrenceException);
198
-
199
-			if (!isset($recurrenceException->VALARM)) {
200
-				continue;
201
-			}
202
-
203
-			foreach ($recurrenceException->VALARM as $valarm) {
204
-				/** @var VAlarm $valarm */
205
-				$alarmHash = $this->getAlarmHash($valarm);
206
-				$triggerTime = $valarm->getEffectiveTriggerTime();
207
-				$diff = $now->diff($triggerTime);
208
-				if ($diff->invert === 1) {
209
-					continue;
210
-				}
211
-
212
-				$alarms = $this->getRemindersForVAlarm($valarm, $objectData,
213
-					$eventHash, $alarmHash, true, true);
214
-				$this->writeRemindersToDatabase($alarms);
215
-			}
216
-		}
217
-
218
-		if ($masterItem) {
219
-			$processedAlarms = [];
220
-			$masterAlarms = [];
221
-			$masterHash = $this->getEventHash($masterItem);
222
-
223
-			if (!isset($masterItem->VALARM)) {
224
-				return;
225
-			}
226
-
227
-			foreach ($masterItem->VALARM as $valarm) {
228
-				$masterAlarms[] = $this->getAlarmHash($valarm);
229
-			}
230
-
231
-			try {
232
-				$iterator = new EventIterator($vevents, $uid);
233
-			} catch (NoInstancesException $e) {
234
-				// This event is recurring, but it doesn't have a single
235
-				// instance. We are skipping this event from the output
236
-				// entirely.
237
-				return;
238
-			}
239
-
240
-			while ($iterator->valid() && count($processedAlarms) < count($masterAlarms)) {
241
-				$event = $iterator->getEventObject();
242
-
243
-				// Recurrence-exceptions are handled separately, so just ignore them here
244
-				if (\in_array($event, $recurrenceExceptions, true)) {
245
-					$iterator->next();
246
-					continue;
247
-				}
248
-
249
-				foreach ($event->VALARM as $valarm) {
250
-					/** @var VAlarm $valarm */
251
-					$alarmHash = $this->getAlarmHash($valarm);
252
-					if (\in_array($alarmHash, $processedAlarms, true)) {
253
-						continue;
254
-					}
255
-
256
-					if (!\in_array((string) $valarm->ACTION, self::REMINDER_TYPES, true)) {
257
-						// Action allows x-name, we don't insert reminders
258
-						// into the database if they are not standard
259
-						$processedAlarms[] = $alarmHash;
260
-						continue;
261
-					}
262
-
263
-					try {
264
-						$triggerTime = $valarm->getEffectiveTriggerTime();
265
-					} catch (InvalidDataException $e) {
266
-						continue;
267
-					}
268
-
269
-					// If effective trigger time is in the past
270
-					// just skip and generate for next event
271
-					$diff = $now->diff($triggerTime);
272
-					if ($diff->invert === 1) {
273
-						// If an absolute alarm is in the past,
274
-						// just add it to processedAlarms, so
275
-						// we don't extend till eternity
276
-						if (!$this->isAlarmRelative($valarm)) {
277
-							$processedAlarms[] = $alarmHash;
278
-						}
279
-
280
-						continue;
281
-					}
282
-
283
-					$alarms = $this->getRemindersForVAlarm($valarm, $objectData, $masterHash, $alarmHash, $isRecurring, false);
284
-					$this->writeRemindersToDatabase($alarms);
285
-					$processedAlarms[] = $alarmHash;
286
-				}
287
-
288
-				$iterator->next();
289
-			}
290
-		}
291
-	}
292
-
293
-	/**
294
-	 * @param array $objectData
295
-	 * @throws VObject\InvalidDataException
296
-	 */
297
-	public function onCalendarObjectEdit(array $objectData):void {
298
-		// TODO - this can be vastly improved
299
-		//  - get cached reminders
300
-		//  - ...
301
-
302
-		$this->onCalendarObjectDelete($objectData);
303
-		$this->onCalendarObjectCreate($objectData);
304
-	}
305
-
306
-	/**
307
-	 * @param array $objectData
308
-	 * @throws VObject\InvalidDataException
309
-	 */
310
-	public function onCalendarObjectDelete(array $objectData):void {
311
-		// We only support VEvents for now
312
-		if (strcasecmp($objectData['component'], 'vevent') !== 0) {
313
-			return;
314
-		}
315
-
316
-		$this->backend->cleanRemindersForEvent((int) $objectData['id']);
317
-	}
318
-
319
-	/**
320
-	 * @param VAlarm $valarm
321
-	 * @param array $objectData
322
-	 * @param string|null $eventHash
323
-	 * @param string|null $alarmHash
324
-	 * @param bool $isRecurring
325
-	 * @param bool $isRecurrenceException
326
-	 * @return array
327
-	 */
328
-	private function getRemindersForVAlarm(VAlarm $valarm,
329
-										   array $objectData,
330
-										   string $eventHash = null,
331
-										   string $alarmHash = null,
332
-										   bool $isRecurring = false,
333
-										   bool $isRecurrenceException = false):array {
334
-		if ($eventHash === null) {
335
-			$eventHash = $this->getEventHash($valarm->parent);
336
-		}
337
-		if ($alarmHash === null) {
338
-			$alarmHash = $this->getAlarmHash($valarm);
339
-		}
340
-
341
-		$recurrenceId = $this->getEffectiveRecurrenceIdOfVEvent($valarm->parent);
342
-		$isRelative = $this->isAlarmRelative($valarm);
343
-		/** @var DateTimeImmutable $notificationDate */
344
-		$notificationDate = $valarm->getEffectiveTriggerTime();
345
-		$clonedNotificationDate = new \DateTime('now', $notificationDate->getTimezone());
346
-		$clonedNotificationDate->setTimestamp($notificationDate->getTimestamp());
347
-
348
-		$alarms = [];
349
-
350
-		$alarms[] = [
351
-			'calendar_id' => $objectData['calendarid'],
352
-			'object_id' => $objectData['id'],
353
-			'uid' => (string) $valarm->parent->UID,
354
-			'is_recurring' => $isRecurring,
355
-			'recurrence_id' => $recurrenceId,
356
-			'is_recurrence_exception' => $isRecurrenceException,
357
-			'event_hash' => $eventHash,
358
-			'alarm_hash' => $alarmHash,
359
-			'type' => (string) $valarm->ACTION,
360
-			'is_relative' => $isRelative,
361
-			'notification_date' => $notificationDate->getTimestamp(),
362
-			'is_repeat_based' => false,
363
-		];
364
-
365
-		$repeat = isset($valarm->REPEAT) ? (int) $valarm->REPEAT->getValue() : 0;
366
-		for ($i = 0; $i < $repeat; $i++) {
367
-			if ($valarm->DURATION === null) {
368
-				continue;
369
-			}
370
-
371
-			$clonedNotificationDate->add($valarm->DURATION->getDateInterval());
372
-			$alarms[] = [
373
-				'calendar_id' => $objectData['calendarid'],
374
-				'object_id' => $objectData['id'],
375
-				'uid' => (string) $valarm->parent->UID,
376
-				'is_recurring' => $isRecurring,
377
-				'recurrence_id' => $recurrenceId,
378
-				'is_recurrence_exception' => $isRecurrenceException,
379
-				'event_hash' => $eventHash,
380
-				'alarm_hash' => $alarmHash,
381
-				'type' => (string) $valarm->ACTION,
382
-				'is_relative' => $isRelative,
383
-				'notification_date' => $clonedNotificationDate->getTimestamp(),
384
-				'is_repeat_based' => true,
385
-			];
386
-		}
387
-
388
-		return $alarms;
389
-	}
390
-
391
-	/**
392
-	 * @param array $reminders
393
-	 */
394
-	private function writeRemindersToDatabase(array $reminders): void {
395
-		foreach ($reminders as $reminder) {
396
-			$this->backend->insertReminder(
397
-				(int) $reminder['calendar_id'],
398
-				(int) $reminder['object_id'],
399
-				$reminder['uid'],
400
-				$reminder['is_recurring'],
401
-				(int) $reminder['recurrence_id'],
402
-				$reminder['is_recurrence_exception'],
403
-				$reminder['event_hash'],
404
-				$reminder['alarm_hash'],
405
-				$reminder['type'],
406
-				$reminder['is_relative'],
407
-				(int) $reminder['notification_date'],
408
-				$reminder['is_repeat_based']
409
-			);
410
-		}
411
-	}
412
-
413
-	/**
414
-	 * @param array $reminder
415
-	 * @param VEvent $vevent
416
-	 */
417
-	private function deleteOrProcessNext(array $reminder,
418
-										 VObject\Component\VEvent $vevent):void {
419
-		if ($reminder['is_repeat_based'] ||
420
-			!$reminder['is_recurring'] ||
421
-			!$reminder['is_relative'] ||
422
-			$reminder['is_recurrence_exception']) {
423
-			$this->backend->removeReminder($reminder['id']);
424
-			return;
425
-		}
426
-
427
-		$vevents = $this->getAllVEventsFromVCalendar($vevent->parent);
428
-		$recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
429
-		$now = $this->timeFactory->getDateTime();
430
-
431
-		try {
432
-			$iterator = new EventIterator($vevents, $reminder['uid']);
433
-		} catch (NoInstancesException $e) {
434
-			// This event is recurring, but it doesn't have a single
435
-			// instance. We are skipping this event from the output
436
-			// entirely.
437
-			return;
438
-		}
439
-
440
-		while ($iterator->valid()) {
441
-			$event = $iterator->getEventObject();
442
-
443
-			// Recurrence-exceptions are handled separately, so just ignore them here
444
-			if (\in_array($event, $recurrenceExceptions, true)) {
445
-				$iterator->next();
446
-				continue;
447
-			}
448
-
449
-			$recurrenceId = $this->getEffectiveRecurrenceIdOfVEvent($event);
450
-			if ($reminder['recurrence_id'] >= $recurrenceId) {
451
-				$iterator->next();
452
-				continue;
453
-			}
454
-
455
-			foreach ($event->VALARM as $valarm) {
456
-				/** @var VAlarm $valarm */
457
-				$alarmHash = $this->getAlarmHash($valarm);
458
-				if ($alarmHash !== $reminder['alarm_hash']) {
459
-					continue;
460
-				}
461
-
462
-				$triggerTime = $valarm->getEffectiveTriggerTime();
463
-
464
-				// If effective trigger time is in the past
465
-				// just skip and generate for next event
466
-				$diff = $now->diff($triggerTime);
467
-				if ($diff->invert === 1) {
468
-					continue;
469
-				}
470
-
471
-				$this->backend->removeReminder($reminder['id']);
472
-				$alarms = $this->getRemindersForVAlarm($valarm, [
473
-					'calendarid' => $reminder['calendar_id'],
474
-					'id' => $reminder['object_id'],
475
-				], $reminder['event_hash'], $alarmHash, true, false);
476
-				$this->writeRemindersToDatabase($alarms);
477
-
478
-				// Abort generating reminders after creating one successfully
479
-				return;
480
-			}
481
-
482
-			$iterator->next();
483
-		}
484
-
485
-		$this->backend->removeReminder($reminder['id']);
486
-	}
487
-
488
-	/**
489
-	 * @param int $calendarId
490
-	 * @return IUser[]
491
-	 */
492
-	private function getAllUsersWithWriteAccessToCalendar(int $calendarId):array {
493
-		$shares = $this->caldavBackend->getShares($calendarId);
494
-
495
-		$users = [];
496
-		$userIds = [];
497
-		$groups = [];
498
-		foreach ($shares as $share) {
499
-			// Only consider writable shares
500
-			if ($share['readOnly']) {
501
-				continue;
502
-			}
503
-
504
-			$principal = explode('/', $share['{http://owncloud.org/ns}principal']);
505
-			if ($principal[1] === 'users') {
506
-				$user = $this->userManager->get($principal[2]);
507
-				if ($user) {
508
-					$users[] = $user;
509
-					$userIds[] = $principal[2];
510
-				}
511
-			} elseif ($principal[1] === 'groups') {
512
-				$groups[] = $principal[2];
513
-			}
514
-		}
515
-
516
-		foreach ($groups as $gid) {
517
-			$group = $this->groupManager->get($gid);
518
-			if ($group instanceof IGroup) {
519
-				foreach ($group->getUsers() as $user) {
520
-					if (!\in_array($user->getUID(), $userIds, true)) {
521
-						$users[] = $user;
522
-						$userIds[] = $user->getUID();
523
-					}
524
-				}
525
-			}
526
-		}
527
-
528
-		return $users;
529
-	}
530
-
531
-	/**
532
-	 * Gets a hash of the event.
533
-	 * If the hash changes, we have to update all relative alarms.
534
-	 *
535
-	 * @param VEvent $vevent
536
-	 * @return string
537
-	 */
538
-	private function getEventHash(VEvent $vevent):string {
539
-		$properties = [
540
-			(string) $vevent->DTSTART->serialize(),
541
-		];
542
-
543
-		if ($vevent->DTEND) {
544
-			$properties[] = (string) $vevent->DTEND->serialize();
545
-		}
546
-		if ($vevent->DURATION) {
547
-			$properties[] = (string) $vevent->DURATION->serialize();
548
-		}
549
-		if ($vevent->{'RECURRENCE-ID'}) {
550
-			$properties[] = (string) $vevent->{'RECURRENCE-ID'}->serialize();
551
-		}
552
-		if ($vevent->RRULE) {
553
-			$properties[] = (string) $vevent->RRULE->serialize();
554
-		}
555
-		if ($vevent->EXDATE) {
556
-			$properties[] = (string) $vevent->EXDATE->serialize();
557
-		}
558
-		if ($vevent->RDATE) {
559
-			$properties[] = (string) $vevent->RDATE->serialize();
560
-		}
561
-
562
-		return md5(implode('::', $properties));
563
-	}
564
-
565
-	/**
566
-	 * Gets a hash of the alarm.
567
-	 * If the hash changes, we have to update oc_dav_reminders.
568
-	 *
569
-	 * @param VAlarm $valarm
570
-	 * @return string
571
-	 */
572
-	private function getAlarmHash(VAlarm $valarm):string {
573
-		$properties = [
574
-			(string) $valarm->ACTION->serialize(),
575
-			(string) $valarm->TRIGGER->serialize(),
576
-		];
577
-
578
-		if ($valarm->DURATION) {
579
-			$properties[] = (string) $valarm->DURATION->serialize();
580
-		}
581
-		if ($valarm->REPEAT) {
582
-			$properties[] = (string) $valarm->REPEAT->serialize();
583
-		}
584
-
585
-		return md5(implode('::', $properties));
586
-	}
587
-
588
-	/**
589
-	 * @param VObject\Component\VCalendar $vcalendar
590
-	 * @param int $recurrenceId
591
-	 * @param bool $isRecurrenceException
592
-	 * @return VEvent|null
593
-	 */
594
-	private function getVEventByRecurrenceId(VObject\Component\VCalendar $vcalendar,
595
-											 int $recurrenceId,
596
-											 bool $isRecurrenceException):?VEvent {
597
-		$vevents = $this->getAllVEventsFromVCalendar($vcalendar);
598
-		if (count($vevents) === 0) {
599
-			return null;
600
-		}
601
-
602
-		$uid = (string) $vevents[0]->UID;
603
-		$recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
604
-		$masterItem = $this->getMasterItemFromListOfVEvents($vevents);
605
-
606
-		// Handle recurrence-exceptions first, because recurrence-expansion is expensive
607
-		if ($isRecurrenceException) {
608
-			foreach ($recurrenceExceptions as $recurrenceException) {
609
-				if ($this->getEffectiveRecurrenceIdOfVEvent($recurrenceException) === $recurrenceId) {
610
-					return $recurrenceException;
611
-				}
612
-			}
613
-
614
-			return null;
615
-		}
616
-
617
-		if ($masterItem) {
618
-			try {
619
-				$iterator = new EventIterator($vevents, $uid);
620
-			} catch (NoInstancesException $e) {
621
-				// This event is recurring, but it doesn't have a single
622
-				// instance. We are skipping this event from the output
623
-				// entirely.
624
-				return null;
625
-			}
626
-
627
-			while ($iterator->valid()) {
628
-				$event = $iterator->getEventObject();
629
-
630
-				// Recurrence-exceptions are handled separately, so just ignore them here
631
-				if (\in_array($event, $recurrenceExceptions, true)) {
632
-					$iterator->next();
633
-					continue;
634
-				}
635
-
636
-				if ($this->getEffectiveRecurrenceIdOfVEvent($event) === $recurrenceId) {
637
-					return $event;
638
-				}
639
-
640
-				$iterator->next();
641
-			}
642
-		}
643
-
644
-		return null;
645
-	}
646
-
647
-	/**
648
-	 * @param VEvent $vevent
649
-	 * @return string
650
-	 */
651
-	private function getStatusOfEvent(VEvent $vevent):string {
652
-		if ($vevent->STATUS) {
653
-			return (string) $vevent->STATUS;
654
-		}
655
-
656
-		// Doesn't say so in the standard,
657
-		// but we consider events without a status
658
-		// to be confirmed
659
-		return 'CONFIRMED';
660
-	}
661
-
662
-	/**
663
-	 * @param VObject\Component\VEvent $vevent
664
-	 * @return bool
665
-	 */
666
-	private function wasEventCancelled(VObject\Component\VEvent $vevent):bool {
667
-		return $this->getStatusOfEvent($vevent) === 'CANCELLED';
668
-	}
669
-
670
-	/**
671
-	 * @param string $calendarData
672
-	 * @return VObject\Component\VCalendar|null
673
-	 */
674
-	private function parseCalendarData(string $calendarData):?VObject\Component\VCalendar {
675
-		try {
676
-			return VObject\Reader::read($calendarData,
677
-				VObject\Reader::OPTION_FORGIVING);
678
-		} catch (ParseException $ex) {
679
-			return null;
680
-		}
681
-	}
682
-
683
-	/**
684
-	 * @param string $principalUri
685
-	 * @return IUser|null
686
-	 */
687
-	private function getUserFromPrincipalURI(string $principalUri):?IUser {
688
-		if (!$principalUri) {
689
-			return null;
690
-		}
691
-
692
-		if (stripos($principalUri, 'principals/users/') !== 0) {
693
-			return null;
694
-		}
695
-
696
-		$userId = substr($principalUri, 17);
697
-		return $this->userManager->get($userId);
698
-	}
699
-
700
-	/**
701
-	 * @param VObject\Component\VCalendar $vcalendar
702
-	 * @return VObject\Component\VEvent[]
703
-	 */
704
-	private function getAllVEventsFromVCalendar(VObject\Component\VCalendar $vcalendar):array {
705
-		$vevents = [];
706
-
707
-		foreach ($vcalendar->children() as $child) {
708
-			if (!($child instanceof VObject\Component)) {
709
-				continue;
710
-			}
711
-
712
-			if ($child->name !== 'VEVENT') {
713
-				continue;
714
-			}
715
-
716
-			$vevents[] = $child;
717
-		}
718
-
719
-		return $vevents;
720
-	}
721
-
722
-	/**
723
-	 * @param array $vevents
724
-	 * @return VObject\Component\VEvent[]
725
-	 */
726
-	private function getRecurrenceExceptionFromListOfVEvents(array $vevents):array {
727
-		return array_values(array_filter($vevents, function (VEvent $vevent) {
728
-			return $vevent->{'RECURRENCE-ID'} !== null;
729
-		}));
730
-	}
731
-
732
-	/**
733
-	 * @param array $vevents
734
-	 * @return VEvent|null
735
-	 */
736
-	private function getMasterItemFromListOfVEvents(array $vevents):?VEvent {
737
-		$elements = array_values(array_filter($vevents, function (VEvent $vevent) {
738
-			return $vevent->{'RECURRENCE-ID'} === null;
739
-		}));
740
-
741
-		if (count($elements) === 0) {
742
-			return null;
743
-		}
744
-		if (count($elements) > 1) {
745
-			throw new \TypeError('Multiple master objects');
746
-		}
747
-
748
-		return $elements[0];
749
-	}
750
-
751
-	/**
752
-	 * @param VAlarm $valarm
753
-	 * @return bool
754
-	 */
755
-	private function isAlarmRelative(VAlarm $valarm):bool {
756
-		$trigger = $valarm->TRIGGER;
757
-		return $trigger instanceof VObject\Property\ICalendar\Duration;
758
-	}
759
-
760
-	/**
761
-	 * @param VEvent $vevent
762
-	 * @return int
763
-	 */
764
-	private function getEffectiveRecurrenceIdOfVEvent(VEvent $vevent):int {
765
-		if (isset($vevent->{'RECURRENCE-ID'})) {
766
-			return $vevent->{'RECURRENCE-ID'}->getDateTime()->getTimestamp();
767
-		}
768
-
769
-		return $vevent->DTSTART->getDateTime()->getTimestamp();
770
-	}
771
-
772
-	/**
773
-	 * @param VEvent $vevent
774
-	 * @return bool
775
-	 */
776
-	private function isRecurring(VEvent $vevent):bool {
777
-		return isset($vevent->RRULE) || isset($vevent->RDATE);
778
-	}
51
+    /** @var Backend */
52
+    private $backend;
53
+
54
+    /** @var NotificationProviderManager */
55
+    private $notificationProviderManager;
56
+
57
+    /** @var IUserManager */
58
+    private $userManager;
59
+
60
+    /** @var IGroupManager */
61
+    private $groupManager;
62
+
63
+    /** @var CalDavBackend */
64
+    private $caldavBackend;
65
+
66
+    /** @var ITimeFactory */
67
+    private $timeFactory;
68
+
69
+    public const REMINDER_TYPE_EMAIL = 'EMAIL';
70
+    public const REMINDER_TYPE_DISPLAY = 'DISPLAY';
71
+    public const REMINDER_TYPE_AUDIO = 'AUDIO';
72
+
73
+    /**
74
+     * @var String[]
75
+     *
76
+     * Official RFC5545 reminder types
77
+     */
78
+    public const REMINDER_TYPES = [
79
+        self::REMINDER_TYPE_EMAIL,
80
+        self::REMINDER_TYPE_DISPLAY,
81
+        self::REMINDER_TYPE_AUDIO
82
+    ];
83
+
84
+    /**
85
+     * ReminderService constructor.
86
+     *
87
+     * @param Backend $backend
88
+     * @param NotificationProviderManager $notificationProviderManager
89
+     * @param IUserManager $userManager
90
+     * @param IGroupManager $groupManager
91
+     * @param CalDavBackend $caldavBackend
92
+     * @param ITimeFactory $timeFactory
93
+     */
94
+    public function __construct(Backend $backend,
95
+                                NotificationProviderManager $notificationProviderManager,
96
+                                IUserManager $userManager,
97
+                                IGroupManager $groupManager,
98
+                                CalDavBackend $caldavBackend,
99
+                                ITimeFactory $timeFactory) {
100
+        $this->backend = $backend;
101
+        $this->notificationProviderManager = $notificationProviderManager;
102
+        $this->userManager = $userManager;
103
+        $this->groupManager = $groupManager;
104
+        $this->caldavBackend = $caldavBackend;
105
+        $this->timeFactory = $timeFactory;
106
+    }
107
+
108
+    /**
109
+     * Process reminders to activate
110
+     *
111
+     * @throws NotificationProvider\ProviderNotAvailableException
112
+     * @throws NotificationTypeDoesNotExistException
113
+     */
114
+    public function processReminders():void {
115
+        $reminders = $this->backend->getRemindersToProcess();
116
+
117
+        foreach ($reminders as $reminder) {
118
+            $calendarData = is_resource($reminder['calendardata'])
119
+                ? stream_get_contents($reminder['calendardata'])
120
+                : $reminder['calendardata'];
121
+
122
+            if (!$calendarData) {
123
+                continue;
124
+            }
125
+
126
+            $vcalendar = $this->parseCalendarData($calendarData);
127
+            if (!$vcalendar) {
128
+                $this->backend->removeReminder($reminder['id']);
129
+                continue;
130
+            }
131
+
132
+            $vevent = $this->getVEventByRecurrenceId($vcalendar, $reminder['recurrence_id'], $reminder['is_recurrence_exception']);
133
+            if (!$vevent) {
134
+                $this->backend->removeReminder($reminder['id']);
135
+                continue;
136
+            }
137
+
138
+            if ($this->wasEventCancelled($vevent)) {
139
+                $this->deleteOrProcessNext($reminder, $vevent);
140
+                continue;
141
+            }
142
+
143
+            if (!$this->notificationProviderManager->hasProvider($reminder['type'])) {
144
+                $this->deleteOrProcessNext($reminder, $vevent);
145
+                continue;
146
+            }
147
+
148
+            $users = $this->getAllUsersWithWriteAccessToCalendar($reminder['calendar_id']);
149
+            $user = $this->getUserFromPrincipalURI($reminder['principaluri']);
150
+            if ($user) {
151
+                $users[] = $user;
152
+            }
153
+
154
+            $notificationProvider = $this->notificationProviderManager->getProvider($reminder['type']);
155
+            $notificationProvider->send($vevent, $reminder['displayname'], $users);
156
+
157
+            $this->deleteOrProcessNext($reminder, $vevent);
158
+        }
159
+    }
160
+
161
+    /**
162
+     * @param array $objectData
163
+     * @throws VObject\InvalidDataException
164
+     */
165
+    public function onCalendarObjectCreate(array $objectData):void {
166
+        // We only support VEvents for now
167
+        if (strcasecmp($objectData['component'], 'vevent') !== 0) {
168
+            return;
169
+        }
170
+
171
+        $calendarData = is_resource($objectData['calendardata'])
172
+            ? stream_get_contents($objectData['calendardata'])
173
+            : $objectData['calendardata'];
174
+
175
+        if (!$calendarData) {
176
+            return;
177
+        }
178
+
179
+        /** @var VObject\Component\VCalendar $vcalendar */
180
+        $vcalendar = $this->parseCalendarData($calendarData);
181
+        if (!$vcalendar) {
182
+            return;
183
+        }
184
+
185
+        $vevents = $this->getAllVEventsFromVCalendar($vcalendar);
186
+        if (count($vevents) === 0) {
187
+            return;
188
+        }
189
+
190
+        $uid = (string) $vevents[0]->UID;
191
+        $recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
192
+        $masterItem = $this->getMasterItemFromListOfVEvents($vevents);
193
+        $now = $this->timeFactory->getDateTime();
194
+        $isRecurring = $masterItem ? $this->isRecurring($masterItem) : false;
195
+
196
+        foreach ($recurrenceExceptions as $recurrenceException) {
197
+            $eventHash = $this->getEventHash($recurrenceException);
198
+
199
+            if (!isset($recurrenceException->VALARM)) {
200
+                continue;
201
+            }
202
+
203
+            foreach ($recurrenceException->VALARM as $valarm) {
204
+                /** @var VAlarm $valarm */
205
+                $alarmHash = $this->getAlarmHash($valarm);
206
+                $triggerTime = $valarm->getEffectiveTriggerTime();
207
+                $diff = $now->diff($triggerTime);
208
+                if ($diff->invert === 1) {
209
+                    continue;
210
+                }
211
+
212
+                $alarms = $this->getRemindersForVAlarm($valarm, $objectData,
213
+                    $eventHash, $alarmHash, true, true);
214
+                $this->writeRemindersToDatabase($alarms);
215
+            }
216
+        }
217
+
218
+        if ($masterItem) {
219
+            $processedAlarms = [];
220
+            $masterAlarms = [];
221
+            $masterHash = $this->getEventHash($masterItem);
222
+
223
+            if (!isset($masterItem->VALARM)) {
224
+                return;
225
+            }
226
+
227
+            foreach ($masterItem->VALARM as $valarm) {
228
+                $masterAlarms[] = $this->getAlarmHash($valarm);
229
+            }
230
+
231
+            try {
232
+                $iterator = new EventIterator($vevents, $uid);
233
+            } catch (NoInstancesException $e) {
234
+                // This event is recurring, but it doesn't have a single
235
+                // instance. We are skipping this event from the output
236
+                // entirely.
237
+                return;
238
+            }
239
+
240
+            while ($iterator->valid() && count($processedAlarms) < count($masterAlarms)) {
241
+                $event = $iterator->getEventObject();
242
+
243
+                // Recurrence-exceptions are handled separately, so just ignore them here
244
+                if (\in_array($event, $recurrenceExceptions, true)) {
245
+                    $iterator->next();
246
+                    continue;
247
+                }
248
+
249
+                foreach ($event->VALARM as $valarm) {
250
+                    /** @var VAlarm $valarm */
251
+                    $alarmHash = $this->getAlarmHash($valarm);
252
+                    if (\in_array($alarmHash, $processedAlarms, true)) {
253
+                        continue;
254
+                    }
255
+
256
+                    if (!\in_array((string) $valarm->ACTION, self::REMINDER_TYPES, true)) {
257
+                        // Action allows x-name, we don't insert reminders
258
+                        // into the database if they are not standard
259
+                        $processedAlarms[] = $alarmHash;
260
+                        continue;
261
+                    }
262
+
263
+                    try {
264
+                        $triggerTime = $valarm->getEffectiveTriggerTime();
265
+                    } catch (InvalidDataException $e) {
266
+                        continue;
267
+                    }
268
+
269
+                    // If effective trigger time is in the past
270
+                    // just skip and generate for next event
271
+                    $diff = $now->diff($triggerTime);
272
+                    if ($diff->invert === 1) {
273
+                        // If an absolute alarm is in the past,
274
+                        // just add it to processedAlarms, so
275
+                        // we don't extend till eternity
276
+                        if (!$this->isAlarmRelative($valarm)) {
277
+                            $processedAlarms[] = $alarmHash;
278
+                        }
279
+
280
+                        continue;
281
+                    }
282
+
283
+                    $alarms = $this->getRemindersForVAlarm($valarm, $objectData, $masterHash, $alarmHash, $isRecurring, false);
284
+                    $this->writeRemindersToDatabase($alarms);
285
+                    $processedAlarms[] = $alarmHash;
286
+                }
287
+
288
+                $iterator->next();
289
+            }
290
+        }
291
+    }
292
+
293
+    /**
294
+     * @param array $objectData
295
+     * @throws VObject\InvalidDataException
296
+     */
297
+    public function onCalendarObjectEdit(array $objectData):void {
298
+        // TODO - this can be vastly improved
299
+        //  - get cached reminders
300
+        //  - ...
301
+
302
+        $this->onCalendarObjectDelete($objectData);
303
+        $this->onCalendarObjectCreate($objectData);
304
+    }
305
+
306
+    /**
307
+     * @param array $objectData
308
+     * @throws VObject\InvalidDataException
309
+     */
310
+    public function onCalendarObjectDelete(array $objectData):void {
311
+        // We only support VEvents for now
312
+        if (strcasecmp($objectData['component'], 'vevent') !== 0) {
313
+            return;
314
+        }
315
+
316
+        $this->backend->cleanRemindersForEvent((int) $objectData['id']);
317
+    }
318
+
319
+    /**
320
+     * @param VAlarm $valarm
321
+     * @param array $objectData
322
+     * @param string|null $eventHash
323
+     * @param string|null $alarmHash
324
+     * @param bool $isRecurring
325
+     * @param bool $isRecurrenceException
326
+     * @return array
327
+     */
328
+    private function getRemindersForVAlarm(VAlarm $valarm,
329
+                                            array $objectData,
330
+                                            string $eventHash = null,
331
+                                            string $alarmHash = null,
332
+                                            bool $isRecurring = false,
333
+                                            bool $isRecurrenceException = false):array {
334
+        if ($eventHash === null) {
335
+            $eventHash = $this->getEventHash($valarm->parent);
336
+        }
337
+        if ($alarmHash === null) {
338
+            $alarmHash = $this->getAlarmHash($valarm);
339
+        }
340
+
341
+        $recurrenceId = $this->getEffectiveRecurrenceIdOfVEvent($valarm->parent);
342
+        $isRelative = $this->isAlarmRelative($valarm);
343
+        /** @var DateTimeImmutable $notificationDate */
344
+        $notificationDate = $valarm->getEffectiveTriggerTime();
345
+        $clonedNotificationDate = new \DateTime('now', $notificationDate->getTimezone());
346
+        $clonedNotificationDate->setTimestamp($notificationDate->getTimestamp());
347
+
348
+        $alarms = [];
349
+
350
+        $alarms[] = [
351
+            'calendar_id' => $objectData['calendarid'],
352
+            'object_id' => $objectData['id'],
353
+            'uid' => (string) $valarm->parent->UID,
354
+            'is_recurring' => $isRecurring,
355
+            'recurrence_id' => $recurrenceId,
356
+            'is_recurrence_exception' => $isRecurrenceException,
357
+            'event_hash' => $eventHash,
358
+            'alarm_hash' => $alarmHash,
359
+            'type' => (string) $valarm->ACTION,
360
+            'is_relative' => $isRelative,
361
+            'notification_date' => $notificationDate->getTimestamp(),
362
+            'is_repeat_based' => false,
363
+        ];
364
+
365
+        $repeat = isset($valarm->REPEAT) ? (int) $valarm->REPEAT->getValue() : 0;
366
+        for ($i = 0; $i < $repeat; $i++) {
367
+            if ($valarm->DURATION === null) {
368
+                continue;
369
+            }
370
+
371
+            $clonedNotificationDate->add($valarm->DURATION->getDateInterval());
372
+            $alarms[] = [
373
+                'calendar_id' => $objectData['calendarid'],
374
+                'object_id' => $objectData['id'],
375
+                'uid' => (string) $valarm->parent->UID,
376
+                'is_recurring' => $isRecurring,
377
+                'recurrence_id' => $recurrenceId,
378
+                'is_recurrence_exception' => $isRecurrenceException,
379
+                'event_hash' => $eventHash,
380
+                'alarm_hash' => $alarmHash,
381
+                'type' => (string) $valarm->ACTION,
382
+                'is_relative' => $isRelative,
383
+                'notification_date' => $clonedNotificationDate->getTimestamp(),
384
+                'is_repeat_based' => true,
385
+            ];
386
+        }
387
+
388
+        return $alarms;
389
+    }
390
+
391
+    /**
392
+     * @param array $reminders
393
+     */
394
+    private function writeRemindersToDatabase(array $reminders): void {
395
+        foreach ($reminders as $reminder) {
396
+            $this->backend->insertReminder(
397
+                (int) $reminder['calendar_id'],
398
+                (int) $reminder['object_id'],
399
+                $reminder['uid'],
400
+                $reminder['is_recurring'],
401
+                (int) $reminder['recurrence_id'],
402
+                $reminder['is_recurrence_exception'],
403
+                $reminder['event_hash'],
404
+                $reminder['alarm_hash'],
405
+                $reminder['type'],
406
+                $reminder['is_relative'],
407
+                (int) $reminder['notification_date'],
408
+                $reminder['is_repeat_based']
409
+            );
410
+        }
411
+    }
412
+
413
+    /**
414
+     * @param array $reminder
415
+     * @param VEvent $vevent
416
+     */
417
+    private function deleteOrProcessNext(array $reminder,
418
+                                            VObject\Component\VEvent $vevent):void {
419
+        if ($reminder['is_repeat_based'] ||
420
+            !$reminder['is_recurring'] ||
421
+            !$reminder['is_relative'] ||
422
+            $reminder['is_recurrence_exception']) {
423
+            $this->backend->removeReminder($reminder['id']);
424
+            return;
425
+        }
426
+
427
+        $vevents = $this->getAllVEventsFromVCalendar($vevent->parent);
428
+        $recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
429
+        $now = $this->timeFactory->getDateTime();
430
+
431
+        try {
432
+            $iterator = new EventIterator($vevents, $reminder['uid']);
433
+        } catch (NoInstancesException $e) {
434
+            // This event is recurring, but it doesn't have a single
435
+            // instance. We are skipping this event from the output
436
+            // entirely.
437
+            return;
438
+        }
439
+
440
+        while ($iterator->valid()) {
441
+            $event = $iterator->getEventObject();
442
+
443
+            // Recurrence-exceptions are handled separately, so just ignore them here
444
+            if (\in_array($event, $recurrenceExceptions, true)) {
445
+                $iterator->next();
446
+                continue;
447
+            }
448
+
449
+            $recurrenceId = $this->getEffectiveRecurrenceIdOfVEvent($event);
450
+            if ($reminder['recurrence_id'] >= $recurrenceId) {
451
+                $iterator->next();
452
+                continue;
453
+            }
454
+
455
+            foreach ($event->VALARM as $valarm) {
456
+                /** @var VAlarm $valarm */
457
+                $alarmHash = $this->getAlarmHash($valarm);
458
+                if ($alarmHash !== $reminder['alarm_hash']) {
459
+                    continue;
460
+                }
461
+
462
+                $triggerTime = $valarm->getEffectiveTriggerTime();
463
+
464
+                // If effective trigger time is in the past
465
+                // just skip and generate for next event
466
+                $diff = $now->diff($triggerTime);
467
+                if ($diff->invert === 1) {
468
+                    continue;
469
+                }
470
+
471
+                $this->backend->removeReminder($reminder['id']);
472
+                $alarms = $this->getRemindersForVAlarm($valarm, [
473
+                    'calendarid' => $reminder['calendar_id'],
474
+                    'id' => $reminder['object_id'],
475
+                ], $reminder['event_hash'], $alarmHash, true, false);
476
+                $this->writeRemindersToDatabase($alarms);
477
+
478
+                // Abort generating reminders after creating one successfully
479
+                return;
480
+            }
481
+
482
+            $iterator->next();
483
+        }
484
+
485
+        $this->backend->removeReminder($reminder['id']);
486
+    }
487
+
488
+    /**
489
+     * @param int $calendarId
490
+     * @return IUser[]
491
+     */
492
+    private function getAllUsersWithWriteAccessToCalendar(int $calendarId):array {
493
+        $shares = $this->caldavBackend->getShares($calendarId);
494
+
495
+        $users = [];
496
+        $userIds = [];
497
+        $groups = [];
498
+        foreach ($shares as $share) {
499
+            // Only consider writable shares
500
+            if ($share['readOnly']) {
501
+                continue;
502
+            }
503
+
504
+            $principal = explode('/', $share['{http://owncloud.org/ns}principal']);
505
+            if ($principal[1] === 'users') {
506
+                $user = $this->userManager->get($principal[2]);
507
+                if ($user) {
508
+                    $users[] = $user;
509
+                    $userIds[] = $principal[2];
510
+                }
511
+            } elseif ($principal[1] === 'groups') {
512
+                $groups[] = $principal[2];
513
+            }
514
+        }
515
+
516
+        foreach ($groups as $gid) {
517
+            $group = $this->groupManager->get($gid);
518
+            if ($group instanceof IGroup) {
519
+                foreach ($group->getUsers() as $user) {
520
+                    if (!\in_array($user->getUID(), $userIds, true)) {
521
+                        $users[] = $user;
522
+                        $userIds[] = $user->getUID();
523
+                    }
524
+                }
525
+            }
526
+        }
527
+
528
+        return $users;
529
+    }
530
+
531
+    /**
532
+     * Gets a hash of the event.
533
+     * If the hash changes, we have to update all relative alarms.
534
+     *
535
+     * @param VEvent $vevent
536
+     * @return string
537
+     */
538
+    private function getEventHash(VEvent $vevent):string {
539
+        $properties = [
540
+            (string) $vevent->DTSTART->serialize(),
541
+        ];
542
+
543
+        if ($vevent->DTEND) {
544
+            $properties[] = (string) $vevent->DTEND->serialize();
545
+        }
546
+        if ($vevent->DURATION) {
547
+            $properties[] = (string) $vevent->DURATION->serialize();
548
+        }
549
+        if ($vevent->{'RECURRENCE-ID'}) {
550
+            $properties[] = (string) $vevent->{'RECURRENCE-ID'}->serialize();
551
+        }
552
+        if ($vevent->RRULE) {
553
+            $properties[] = (string) $vevent->RRULE->serialize();
554
+        }
555
+        if ($vevent->EXDATE) {
556
+            $properties[] = (string) $vevent->EXDATE->serialize();
557
+        }
558
+        if ($vevent->RDATE) {
559
+            $properties[] = (string) $vevent->RDATE->serialize();
560
+        }
561
+
562
+        return md5(implode('::', $properties));
563
+    }
564
+
565
+    /**
566
+     * Gets a hash of the alarm.
567
+     * If the hash changes, we have to update oc_dav_reminders.
568
+     *
569
+     * @param VAlarm $valarm
570
+     * @return string
571
+     */
572
+    private function getAlarmHash(VAlarm $valarm):string {
573
+        $properties = [
574
+            (string) $valarm->ACTION->serialize(),
575
+            (string) $valarm->TRIGGER->serialize(),
576
+        ];
577
+
578
+        if ($valarm->DURATION) {
579
+            $properties[] = (string) $valarm->DURATION->serialize();
580
+        }
581
+        if ($valarm->REPEAT) {
582
+            $properties[] = (string) $valarm->REPEAT->serialize();
583
+        }
584
+
585
+        return md5(implode('::', $properties));
586
+    }
587
+
588
+    /**
589
+     * @param VObject\Component\VCalendar $vcalendar
590
+     * @param int $recurrenceId
591
+     * @param bool $isRecurrenceException
592
+     * @return VEvent|null
593
+     */
594
+    private function getVEventByRecurrenceId(VObject\Component\VCalendar $vcalendar,
595
+                                                int $recurrenceId,
596
+                                                bool $isRecurrenceException):?VEvent {
597
+        $vevents = $this->getAllVEventsFromVCalendar($vcalendar);
598
+        if (count($vevents) === 0) {
599
+            return null;
600
+        }
601
+
602
+        $uid = (string) $vevents[0]->UID;
603
+        $recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
604
+        $masterItem = $this->getMasterItemFromListOfVEvents($vevents);
605
+
606
+        // Handle recurrence-exceptions first, because recurrence-expansion is expensive
607
+        if ($isRecurrenceException) {
608
+            foreach ($recurrenceExceptions as $recurrenceException) {
609
+                if ($this->getEffectiveRecurrenceIdOfVEvent($recurrenceException) === $recurrenceId) {
610
+                    return $recurrenceException;
611
+                }
612
+            }
613
+
614
+            return null;
615
+        }
616
+
617
+        if ($masterItem) {
618
+            try {
619
+                $iterator = new EventIterator($vevents, $uid);
620
+            } catch (NoInstancesException $e) {
621
+                // This event is recurring, but it doesn't have a single
622
+                // instance. We are skipping this event from the output
623
+                // entirely.
624
+                return null;
625
+            }
626
+
627
+            while ($iterator->valid()) {
628
+                $event = $iterator->getEventObject();
629
+
630
+                // Recurrence-exceptions are handled separately, so just ignore them here
631
+                if (\in_array($event, $recurrenceExceptions, true)) {
632
+                    $iterator->next();
633
+                    continue;
634
+                }
635
+
636
+                if ($this->getEffectiveRecurrenceIdOfVEvent($event) === $recurrenceId) {
637
+                    return $event;
638
+                }
639
+
640
+                $iterator->next();
641
+            }
642
+        }
643
+
644
+        return null;
645
+    }
646
+
647
+    /**
648
+     * @param VEvent $vevent
649
+     * @return string
650
+     */
651
+    private function getStatusOfEvent(VEvent $vevent):string {
652
+        if ($vevent->STATUS) {
653
+            return (string) $vevent->STATUS;
654
+        }
655
+
656
+        // Doesn't say so in the standard,
657
+        // but we consider events without a status
658
+        // to be confirmed
659
+        return 'CONFIRMED';
660
+    }
661
+
662
+    /**
663
+     * @param VObject\Component\VEvent $vevent
664
+     * @return bool
665
+     */
666
+    private function wasEventCancelled(VObject\Component\VEvent $vevent):bool {
667
+        return $this->getStatusOfEvent($vevent) === 'CANCELLED';
668
+    }
669
+
670
+    /**
671
+     * @param string $calendarData
672
+     * @return VObject\Component\VCalendar|null
673
+     */
674
+    private function parseCalendarData(string $calendarData):?VObject\Component\VCalendar {
675
+        try {
676
+            return VObject\Reader::read($calendarData,
677
+                VObject\Reader::OPTION_FORGIVING);
678
+        } catch (ParseException $ex) {
679
+            return null;
680
+        }
681
+    }
682
+
683
+    /**
684
+     * @param string $principalUri
685
+     * @return IUser|null
686
+     */
687
+    private function getUserFromPrincipalURI(string $principalUri):?IUser {
688
+        if (!$principalUri) {
689
+            return null;
690
+        }
691
+
692
+        if (stripos($principalUri, 'principals/users/') !== 0) {
693
+            return null;
694
+        }
695
+
696
+        $userId = substr($principalUri, 17);
697
+        return $this->userManager->get($userId);
698
+    }
699
+
700
+    /**
701
+     * @param VObject\Component\VCalendar $vcalendar
702
+     * @return VObject\Component\VEvent[]
703
+     */
704
+    private function getAllVEventsFromVCalendar(VObject\Component\VCalendar $vcalendar):array {
705
+        $vevents = [];
706
+
707
+        foreach ($vcalendar->children() as $child) {
708
+            if (!($child instanceof VObject\Component)) {
709
+                continue;
710
+            }
711
+
712
+            if ($child->name !== 'VEVENT') {
713
+                continue;
714
+            }
715
+
716
+            $vevents[] = $child;
717
+        }
718
+
719
+        return $vevents;
720
+    }
721
+
722
+    /**
723
+     * @param array $vevents
724
+     * @return VObject\Component\VEvent[]
725
+     */
726
+    private function getRecurrenceExceptionFromListOfVEvents(array $vevents):array {
727
+        return array_values(array_filter($vevents, function (VEvent $vevent) {
728
+            return $vevent->{'RECURRENCE-ID'} !== null;
729
+        }));
730
+    }
731
+
732
+    /**
733
+     * @param array $vevents
734
+     * @return VEvent|null
735
+     */
736
+    private function getMasterItemFromListOfVEvents(array $vevents):?VEvent {
737
+        $elements = array_values(array_filter($vevents, function (VEvent $vevent) {
738
+            return $vevent->{'RECURRENCE-ID'} === null;
739
+        }));
740
+
741
+        if (count($elements) === 0) {
742
+            return null;
743
+        }
744
+        if (count($elements) > 1) {
745
+            throw new \TypeError('Multiple master objects');
746
+        }
747
+
748
+        return $elements[0];
749
+    }
750
+
751
+    /**
752
+     * @param VAlarm $valarm
753
+     * @return bool
754
+     */
755
+    private function isAlarmRelative(VAlarm $valarm):bool {
756
+        $trigger = $valarm->TRIGGER;
757
+        return $trigger instanceof VObject\Property\ICalendar\Duration;
758
+    }
759
+
760
+    /**
761
+     * @param VEvent $vevent
762
+     * @return int
763
+     */
764
+    private function getEffectiveRecurrenceIdOfVEvent(VEvent $vevent):int {
765
+        if (isset($vevent->{'RECURRENCE-ID'})) {
766
+            return $vevent->{'RECURRENCE-ID'}->getDateTime()->getTimestamp();
767
+        }
768
+
769
+        return $vevent->DTSTART->getDateTime()->getTimestamp();
770
+    }
771
+
772
+    /**
773
+     * @param VEvent $vevent
774
+     * @return bool
775
+     */
776
+    private function isRecurring(VEvent $vevent):bool {
777
+        return isset($vevent->RRULE) || isset($vevent->RDATE);
778
+    }
779 779
 }
Please login to merge, or discard this patch.
apps/dav/lib/CalDAV/Reminder/Backend.php 1 patch
Indentation   +181 added lines, -181 removed lines patch added patch discarded remove patch
@@ -38,185 +38,185 @@
 block discarded – undo
38 38
  */
39 39
 class Backend {
40 40
 
41
-	/** @var IDBConnection */
42
-	protected $db;
43
-
44
-	/** @var ITimeFactory */
45
-	private $timeFactory;
46
-
47
-	/**
48
-	 * Backend constructor.
49
-	 *
50
-	 * @param IDBConnection $db
51
-	 * @param ITimeFactory $timeFactory
52
-	 */
53
-	public function __construct(IDBConnection $db,
54
-								ITimeFactory $timeFactory) {
55
-		$this->db = $db;
56
-		$this->timeFactory = $timeFactory;
57
-	}
58
-
59
-	/**
60
-	 * Get all reminders with a notification date before now
61
-	 *
62
-	 * @return array
63
-	 * @throws \Exception
64
-	 */
65
-	public function getRemindersToProcess():array {
66
-		$query = $this->db->getQueryBuilder();
67
-		$query->select(['cr.*', 'co.calendardata', 'c.displayname', 'c.principaluri'])
68
-			->from('calendar_reminders', 'cr')
69
-			->where($query->expr()->lte('cr.notification_date', $query->createNamedParameter($this->timeFactory->getTime())))
70
-			->join('cr', 'calendarobjects', 'co', $query->expr()->eq('cr.object_id', 'co.id'))
71
-			->join('cr', 'calendars', 'c', $query->expr()->eq('cr.calendar_id', 'c.id'));
72
-		$stmt = $query->execute();
73
-
74
-		return array_map(
75
-			[$this, 'fixRowTyping'],
76
-			$stmt->fetchAll()
77
-		);
78
-	}
79
-
80
-	/**
81
-	 * Get all scheduled reminders for an event
82
-	 *
83
-	 * @param int $objectId
84
-	 * @return array
85
-	 */
86
-	public function getAllScheduledRemindersForEvent(int $objectId):array {
87
-		$query = $this->db->getQueryBuilder();
88
-		$query->select('*')
89
-			->from('calendar_reminders')
90
-			->where($query->expr()->eq('object_id', $query->createNamedParameter($objectId)));
91
-		$stmt = $query->execute();
92
-
93
-		return array_map(
94
-			[$this, 'fixRowTyping'],
95
-			$stmt->fetchAll()
96
-		);
97
-	}
98
-
99
-	/**
100
-	 * Insert a new reminder into the database
101
-	 *
102
-	 * @param int $calendarId
103
-	 * @param int $objectId
104
-	 * @param string $uid
105
-	 * @param bool $isRecurring
106
-	 * @param int $recurrenceId
107
-	 * @param bool $isRecurrenceException
108
-	 * @param string $eventHash
109
-	 * @param string $alarmHash
110
-	 * @param string $type
111
-	 * @param bool $isRelative
112
-	 * @param int $notificationDate
113
-	 * @param bool $isRepeatBased
114
-	 * @return int The insert id
115
-	 */
116
-	public function insertReminder(int $calendarId,
117
-								   int $objectId,
118
-								   string $uid,
119
-								   bool $isRecurring,
120
-								   int $recurrenceId,
121
-								   bool $isRecurrenceException,
122
-								   string $eventHash,
123
-								   string $alarmHash,
124
-								   string $type,
125
-								   bool $isRelative,
126
-								   int $notificationDate,
127
-								   bool $isRepeatBased):int {
128
-		$query = $this->db->getQueryBuilder();
129
-		$query->insert('calendar_reminders')
130
-			->values([
131
-				'calendar_id' => $query->createNamedParameter($calendarId),
132
-				'object_id' => $query->createNamedParameter($objectId),
133
-				'uid' => $query->createNamedParameter($uid),
134
-				'is_recurring' => $query->createNamedParameter($isRecurring ? 1 : 0),
135
-				'recurrence_id' => $query->createNamedParameter($recurrenceId),
136
-				'is_recurrence_exception' => $query->createNamedParameter($isRecurrenceException ? 1 : 0),
137
-				'event_hash' => $query->createNamedParameter($eventHash),
138
-				'alarm_hash' => $query->createNamedParameter($alarmHash),
139
-				'type' => $query->createNamedParameter($type),
140
-				'is_relative' => $query->createNamedParameter($isRelative ? 1 : 0),
141
-				'notification_date' => $query->createNamedParameter($notificationDate),
142
-				'is_repeat_based' => $query->createNamedParameter($isRepeatBased ? 1 : 0),
143
-			])
144
-			->execute();
145
-
146
-		return $query->getLastInsertId();
147
-	}
148
-
149
-	/**
150
-	 * Sets a new notificationDate on an existing reminder
151
-	 *
152
-	 * @param int $reminderId
153
-	 * @param int $newNotificationDate
154
-	 */
155
-	public function updateReminder(int $reminderId,
156
-								   int $newNotificationDate):void {
157
-		$query = $this->db->getQueryBuilder();
158
-		$query->update('calendar_reminders')
159
-			->set('notification_date', $query->createNamedParameter($newNotificationDate))
160
-			->where($query->expr()->eq('id', $query->createNamedParameter($reminderId)))
161
-			->execute();
162
-	}
163
-
164
-	/**
165
-	 * Remove a reminder by it's id
166
-	 *
167
-	 * @param integer $reminderId
168
-	 * @return void
169
-	 */
170
-	public function removeReminder(int $reminderId):void {
171
-		$query = $this->db->getQueryBuilder();
172
-
173
-		$query->delete('calendar_reminders')
174
-			->where($query->expr()->eq('id', $query->createNamedParameter($reminderId)))
175
-			->execute();
176
-	}
177
-
178
-	/**
179
-	 * Cleans reminders in database
180
-	 *
181
-	 * @param int $objectId
182
-	 */
183
-	public function cleanRemindersForEvent(int $objectId):void {
184
-		$query = $this->db->getQueryBuilder();
185
-
186
-		$query->delete('calendar_reminders')
187
-			->where($query->expr()->eq('object_id', $query->createNamedParameter($objectId)))
188
-			->execute();
189
-	}
190
-
191
-	/**
192
-	 * Remove all reminders for a calendar
193
-	 *
194
-	 * @param int $calendarId
195
-	 * @return void
196
-	 */
197
-	public function cleanRemindersForCalendar(int $calendarId):void {
198
-		$query = $this->db->getQueryBuilder();
199
-
200
-		$query->delete('calendar_reminders')
201
-			->where($query->expr()->eq('calendar_id', $query->createNamedParameter($calendarId)))
202
-			->execute();
203
-	}
204
-
205
-	/**
206
-	 * @param array $row
207
-	 * @return array
208
-	 */
209
-	private function fixRowTyping(array $row): array {
210
-		$row['id'] = (int) $row['id'];
211
-		$row['calendar_id'] = (int) $row['calendar_id'];
212
-		$row['object_id'] = (int) $row['object_id'];
213
-		$row['is_recurring'] = (bool) $row['is_recurring'];
214
-		$row['recurrence_id'] = (int) $row['recurrence_id'];
215
-		$row['is_recurrence_exception'] = (bool) $row['is_recurrence_exception'];
216
-		$row['is_relative'] = (bool) $row['is_relative'];
217
-		$row['notification_date'] = (int) $row['notification_date'];
218
-		$row['is_repeat_based'] = (bool) $row['is_repeat_based'];
219
-
220
-		return $row;
221
-	}
41
+    /** @var IDBConnection */
42
+    protected $db;
43
+
44
+    /** @var ITimeFactory */
45
+    private $timeFactory;
46
+
47
+    /**
48
+     * Backend constructor.
49
+     *
50
+     * @param IDBConnection $db
51
+     * @param ITimeFactory $timeFactory
52
+     */
53
+    public function __construct(IDBConnection $db,
54
+                                ITimeFactory $timeFactory) {
55
+        $this->db = $db;
56
+        $this->timeFactory = $timeFactory;
57
+    }
58
+
59
+    /**
60
+     * Get all reminders with a notification date before now
61
+     *
62
+     * @return array
63
+     * @throws \Exception
64
+     */
65
+    public function getRemindersToProcess():array {
66
+        $query = $this->db->getQueryBuilder();
67
+        $query->select(['cr.*', 'co.calendardata', 'c.displayname', 'c.principaluri'])
68
+            ->from('calendar_reminders', 'cr')
69
+            ->where($query->expr()->lte('cr.notification_date', $query->createNamedParameter($this->timeFactory->getTime())))
70
+            ->join('cr', 'calendarobjects', 'co', $query->expr()->eq('cr.object_id', 'co.id'))
71
+            ->join('cr', 'calendars', 'c', $query->expr()->eq('cr.calendar_id', 'c.id'));
72
+        $stmt = $query->execute();
73
+
74
+        return array_map(
75
+            [$this, 'fixRowTyping'],
76
+            $stmt->fetchAll()
77
+        );
78
+    }
79
+
80
+    /**
81
+     * Get all scheduled reminders for an event
82
+     *
83
+     * @param int $objectId
84
+     * @return array
85
+     */
86
+    public function getAllScheduledRemindersForEvent(int $objectId):array {
87
+        $query = $this->db->getQueryBuilder();
88
+        $query->select('*')
89
+            ->from('calendar_reminders')
90
+            ->where($query->expr()->eq('object_id', $query->createNamedParameter($objectId)));
91
+        $stmt = $query->execute();
92
+
93
+        return array_map(
94
+            [$this, 'fixRowTyping'],
95
+            $stmt->fetchAll()
96
+        );
97
+    }
98
+
99
+    /**
100
+     * Insert a new reminder into the database
101
+     *
102
+     * @param int $calendarId
103
+     * @param int $objectId
104
+     * @param string $uid
105
+     * @param bool $isRecurring
106
+     * @param int $recurrenceId
107
+     * @param bool $isRecurrenceException
108
+     * @param string $eventHash
109
+     * @param string $alarmHash
110
+     * @param string $type
111
+     * @param bool $isRelative
112
+     * @param int $notificationDate
113
+     * @param bool $isRepeatBased
114
+     * @return int The insert id
115
+     */
116
+    public function insertReminder(int $calendarId,
117
+                                    int $objectId,
118
+                                    string $uid,
119
+                                    bool $isRecurring,
120
+                                    int $recurrenceId,
121
+                                    bool $isRecurrenceException,
122
+                                    string $eventHash,
123
+                                    string $alarmHash,
124
+                                    string $type,
125
+                                    bool $isRelative,
126
+                                    int $notificationDate,
127
+                                    bool $isRepeatBased):int {
128
+        $query = $this->db->getQueryBuilder();
129
+        $query->insert('calendar_reminders')
130
+            ->values([
131
+                'calendar_id' => $query->createNamedParameter($calendarId),
132
+                'object_id' => $query->createNamedParameter($objectId),
133
+                'uid' => $query->createNamedParameter($uid),
134
+                'is_recurring' => $query->createNamedParameter($isRecurring ? 1 : 0),
135
+                'recurrence_id' => $query->createNamedParameter($recurrenceId),
136
+                'is_recurrence_exception' => $query->createNamedParameter($isRecurrenceException ? 1 : 0),
137
+                'event_hash' => $query->createNamedParameter($eventHash),
138
+                'alarm_hash' => $query->createNamedParameter($alarmHash),
139
+                'type' => $query->createNamedParameter($type),
140
+                'is_relative' => $query->createNamedParameter($isRelative ? 1 : 0),
141
+                'notification_date' => $query->createNamedParameter($notificationDate),
142
+                'is_repeat_based' => $query->createNamedParameter($isRepeatBased ? 1 : 0),
143
+            ])
144
+            ->execute();
145
+
146
+        return $query->getLastInsertId();
147
+    }
148
+
149
+    /**
150
+     * Sets a new notificationDate on an existing reminder
151
+     *
152
+     * @param int $reminderId
153
+     * @param int $newNotificationDate
154
+     */
155
+    public function updateReminder(int $reminderId,
156
+                                    int $newNotificationDate):void {
157
+        $query = $this->db->getQueryBuilder();
158
+        $query->update('calendar_reminders')
159
+            ->set('notification_date', $query->createNamedParameter($newNotificationDate))
160
+            ->where($query->expr()->eq('id', $query->createNamedParameter($reminderId)))
161
+            ->execute();
162
+    }
163
+
164
+    /**
165
+     * Remove a reminder by it's id
166
+     *
167
+     * @param integer $reminderId
168
+     * @return void
169
+     */
170
+    public function removeReminder(int $reminderId):void {
171
+        $query = $this->db->getQueryBuilder();
172
+
173
+        $query->delete('calendar_reminders')
174
+            ->where($query->expr()->eq('id', $query->createNamedParameter($reminderId)))
175
+            ->execute();
176
+    }
177
+
178
+    /**
179
+     * Cleans reminders in database
180
+     *
181
+     * @param int $objectId
182
+     */
183
+    public function cleanRemindersForEvent(int $objectId):void {
184
+        $query = $this->db->getQueryBuilder();
185
+
186
+        $query->delete('calendar_reminders')
187
+            ->where($query->expr()->eq('object_id', $query->createNamedParameter($objectId)))
188
+            ->execute();
189
+    }
190
+
191
+    /**
192
+     * Remove all reminders for a calendar
193
+     *
194
+     * @param int $calendarId
195
+     * @return void
196
+     */
197
+    public function cleanRemindersForCalendar(int $calendarId):void {
198
+        $query = $this->db->getQueryBuilder();
199
+
200
+        $query->delete('calendar_reminders')
201
+            ->where($query->expr()->eq('calendar_id', $query->createNamedParameter($calendarId)))
202
+            ->execute();
203
+    }
204
+
205
+    /**
206
+     * @param array $row
207
+     * @return array
208
+     */
209
+    private function fixRowTyping(array $row): array {
210
+        $row['id'] = (int) $row['id'];
211
+        $row['calendar_id'] = (int) $row['calendar_id'];
212
+        $row['object_id'] = (int) $row['object_id'];
213
+        $row['is_recurring'] = (bool) $row['is_recurring'];
214
+        $row['recurrence_id'] = (int) $row['recurrence_id'];
215
+        $row['is_recurrence_exception'] = (bool) $row['is_recurrence_exception'];
216
+        $row['is_relative'] = (bool) $row['is_relative'];
217
+        $row['notification_date'] = (int) $row['notification_date'];
218
+        $row['is_repeat_based'] = (bool) $row['is_repeat_based'];
219
+
220
+        return $row;
221
+    }
222 222
 }
Please login to merge, or discard this patch.
apps/dav/lib/AppInfo/Application.php 1 patch
Indentation   +277 added lines, -277 removed lines patch added patch discarded remove patch
@@ -97,290 +97,290 @@
 block discarded – undo
97 97
 use function is_null;
98 98
 
99 99
 class Application extends App implements IBootstrap {
100
-	public const APP_ID = 'dav';
100
+    public const APP_ID = 'dav';
101 101
 
102
-	public function __construct() {
103
-		parent::__construct(self::APP_ID);
104
-	}
102
+    public function __construct() {
103
+        parent::__construct(self::APP_ID);
104
+    }
105 105
 
106
-	public function register(IRegistrationContext $context): void {
107
-		$context->registerServiceAlias('CardDAVSyncService', SyncService::class);
108
-		$context->registerService(PhotoCache::class, function (ContainerInterface $c) {
109
-			/** @var IServerContainer $server */
110
-			$server = $c->get(IServerContainer::class);
106
+    public function register(IRegistrationContext $context): void {
107
+        $context->registerServiceAlias('CardDAVSyncService', SyncService::class);
108
+        $context->registerService(PhotoCache::class, function (ContainerInterface $c) {
109
+            /** @var IServerContainer $server */
110
+            $server = $c->get(IServerContainer::class);
111 111
 
112
-			return new PhotoCache(
113
-				$server->getAppDataDir('dav-photocache'),
114
-				$c->get(ILogger::class)
115
-			);
116
-		});
112
+            return new PhotoCache(
113
+                $server->getAppDataDir('dav-photocache'),
114
+                $c->get(ILogger::class)
115
+            );
116
+        });
117 117
 
118
-		/*
118
+        /*
119 119
 		 * Register capabilities
120 120
 		 */
121
-		$context->registerCapability(Capabilities::class);
121
+        $context->registerCapability(Capabilities::class);
122 122
 
123
-		/*
123
+        /*
124 124
 		 * Register Search Providers
125 125
 		 */
126
-		$context->registerSearchProvider(ContactsSearchProvider::class);
127
-		$context->registerSearchProvider(EventsSearchProvider::class);
128
-		$context->registerSearchProvider(TasksSearchProvider::class);
129
-
130
-		/**
131
-		 * Register event listeners
132
-		 */
133
-		$context->registerEventListener(CalendarCreatedEvent::class, ActivityUpdaterListener::class);
134
-		$context->registerEventListener(CalendarDeletedEvent::class, ActivityUpdaterListener::class);
135
-		$context->registerEventListener(CalendarDeletedEvent::class, CalendarObjectReminderUpdaterListener::class);
136
-		$context->registerEventListener(CalendarDeletedEvent::class, CalendarDeletionDefaultUpdaterListener::class);
137
-		$context->registerEventListener(CalendarMovedToTrashEvent::class, ActivityUpdaterListener::class);
138
-		$context->registerEventListener(CalendarMovedToTrashEvent::class, CalendarObjectReminderUpdaterListener::class);
139
-		$context->registerEventListener(CalendarUpdatedEvent::class, ActivityUpdaterListener::class);
140
-		$context->registerEventListener(CalendarRestoredEvent::class, ActivityUpdaterListener::class);
141
-		$context->registerEventListener(CalendarRestoredEvent::class, CalendarObjectReminderUpdaterListener::class);
142
-		$context->registerEventListener(CalendarObjectCreatedEvent::class, ActivityUpdaterListener::class);
143
-		$context->registerEventListener(CalendarObjectCreatedEvent::class, CalendarContactInteractionListener::class);
144
-		$context->registerEventListener(CalendarObjectCreatedEvent::class, CalendarObjectReminderUpdaterListener::class);
145
-		$context->registerEventListener(CalendarObjectUpdatedEvent::class, ActivityUpdaterListener::class);
146
-		$context->registerEventListener(CalendarObjectUpdatedEvent::class, CalendarContactInteractionListener::class);
147
-		$context->registerEventListener(CalendarObjectUpdatedEvent::class, CalendarObjectReminderUpdaterListener::class);
148
-		$context->registerEventListener(CalendarObjectDeletedEvent::class, ActivityUpdaterListener::class);
149
-		$context->registerEventListener(CalendarObjectDeletedEvent::class, CalendarObjectReminderUpdaterListener::class);
150
-		$context->registerEventListener(CalendarObjectMovedToTrashEvent::class, ActivityUpdaterListener::class);
151
-		$context->registerEventListener(CalendarObjectMovedToTrashEvent::class, CalendarObjectReminderUpdaterListener::class);
152
-		$context->registerEventListener(CalendarObjectRestoredEvent::class, ActivityUpdaterListener::class);
153
-		$context->registerEventListener(CalendarObjectRestoredEvent::class, CalendarObjectReminderUpdaterListener::class);
154
-		$context->registerEventListener(CalendarShareUpdatedEvent::class, CalendarContactInteractionListener::class);
155
-
156
-
157
-		$context->registerEventListener(AddressBookCreatedEvent::class, AddressbookListener::class);
158
-		$context->registerEventListener(AddressBookDeletedEvent::class, AddressbookListener::class);
159
-		$context->registerEventListener(AddressBookUpdatedEvent::class, AddressbookListener::class);
160
-		$context->registerEventListener(AddressBookShareUpdatedEvent::class, AddressbookListener::class);
161
-		$context->registerEventListener(CardCreatedEvent::class, CardListener::class);
162
-		$context->registerEventListener(CardDeletedEvent::class, CardListener::class);
163
-		$context->registerEventListener(CardUpdatedEvent::class, CardListener::class);
164
-
165
-		$context->registerNotifierService(Notifier::class);
166
-
167
-		$context->registerCalendarProvider(CalendarProvider::class);
168
-	}
169
-
170
-	public function boot(IBootContext $context): void {
171
-		// Load all dav apps
172
-		\OC_App::loadApps(['dav']);
173
-
174
-		$context->injectFn([$this, 'registerHooks']);
175
-		$context->injectFn([$this, 'registerContactsManager']);
176
-		$context->injectFn([$this, 'registerCalendarManager']);
177
-		$context->injectFn([$this, 'registerCalendarReminders']);
178
-	}
179
-
180
-	public function registerHooks(HookManager $hm,
181
-								   EventDispatcherInterface $dispatcher,
182
-								   IAppContainer $container,
183
-								   IServerContainer $serverContainer) {
184
-		$hm->setup();
185
-
186
-		// first time login event setup
187
-		$dispatcher->addListener(IUser::class . '::firstLogin', function ($event) use ($hm) {
188
-			if ($event instanceof GenericEvent) {
189
-				$hm->firstLogin($event->getSubject());
190
-			}
191
-		});
192
-
193
-		$birthdayListener = function ($event) use ($container): void {
194
-			if ($event instanceof GenericEvent) {
195
-				/** @var BirthdayService $b */
196
-				$b = $container->query(BirthdayService::class);
197
-				$b->onCardChanged(
198
-					(int) $event->getArgument('addressBookId'),
199
-					(string) $event->getArgument('cardUri'),
200
-					(string) $event->getArgument('cardData')
201
-				);
202
-			}
203
-		};
204
-
205
-		$dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::createCard', $birthdayListener);
206
-		$dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $birthdayListener);
207
-		$dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', function ($event) use ($container) {
208
-			if ($event instanceof GenericEvent) {
209
-				/** @var BirthdayService $b */
210
-				$b = $container->query(BirthdayService::class);
211
-				$b->onCardDeleted(
212
-					(int) $event->getArgument('addressBookId'),
213
-					(string) $event->getArgument('cardUri')
214
-				);
215
-			}
216
-		});
217
-
218
-		$clearPhotoCache = function ($event) use ($container): void {
219
-			if ($event instanceof GenericEvent) {
220
-				/** @var PhotoCache $p */
221
-				$p = $container->query(PhotoCache::class);
222
-				$p->delete(
223
-					$event->getArgument('addressBookId'),
224
-					$event->getArgument('cardUri')
225
-				);
226
-			}
227
-		};
228
-		$dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $clearPhotoCache);
229
-		$dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', $clearPhotoCache);
230
-
231
-		$dispatcher->addListener('OC\AccountManager::userUpdated', function (GenericEvent $event) use ($container) {
232
-			$user = $event->getSubject();
233
-			/** @var SyncService $syncService */
234
-			$syncService = $container->query(SyncService::class);
235
-			$syncService->updateUser($user);
236
-		});
237
-
238
-
239
-		$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateShares', function (GenericEvent $event) use ($container) {
240
-			/** @var Backend $backend */
241
-			$backend = $container->query(Backend::class);
242
-			$backend->onCalendarUpdateShares(
243
-				$event->getArgument('calendarData'),
244
-				$event->getArgument('shares'),
245
-				$event->getArgument('add'),
246
-				$event->getArgument('remove')
247
-			);
248
-
249
-			// Here we should recalculate if reminders should be sent to new or old sharees
250
-		});
251
-
252
-		$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::publishCalendar', function (GenericEvent $event) use ($container) {
253
-			/** @var Backend $backend */
254
-			$backend = $container->query(Backend::class);
255
-			$backend->onCalendarPublication(
256
-				$event->getArgument('calendarData'),
257
-				$event->getArgument('public')
258
-			);
259
-		});
260
-
261
-
262
-		$dispatcher->addListener('OCP\Federation\TrustedServerEvent::remove',
263
-			function (GenericEvent $event) {
264
-				/** @var CardDavBackend $cardDavBackend */
265
-				$cardDavBackend = \OC::$server->query(CardDavBackend::class);
266
-				$addressBookUri = $event->getSubject();
267
-				$addressBook = $cardDavBackend->getAddressBooksByUri('principals/system/system', $addressBookUri);
268
-				if (!is_null($addressBook)) {
269
-					$cardDavBackend->deleteAddressBook($addressBook['id']);
270
-				}
271
-			}
272
-		);
273
-
274
-		$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::createSubscription',
275
-			function (GenericEvent $event) use ($container, $serverContainer) {
276
-				$jobList = $serverContainer->getJobList();
277
-				$subscriptionData = $event->getArgument('subscriptionData');
278
-
279
-				/**
280
-				 * Initial subscription refetch
281
-				 *
282
-				 * @var RefreshWebcalService $refreshWebcalService
283
-				 */
284
-				$refreshWebcalService = $container->query(RefreshWebcalService::class);
285
-				$refreshWebcalService->refreshSubscription(
286
-					(string) $subscriptionData['principaluri'],
287
-					(string) $subscriptionData['uri']
288
-				);
289
-
290
-				$jobList->add(\OCA\DAV\BackgroundJob\RefreshWebcalJob::class, [
291
-					'principaluri' => $subscriptionData['principaluri'],
292
-					'uri' => $subscriptionData['uri']
293
-				]);
294
-			}
295
-		);
296
-
297
-		$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteSubscription',
298
-			function (GenericEvent $event) use ($container, $serverContainer) {
299
-				$jobList = $serverContainer->getJobList();
300
-				$subscriptionData = $event->getArgument('subscriptionData');
301
-
302
-				$jobList->remove(\OCA\DAV\BackgroundJob\RefreshWebcalJob::class, [
303
-					'principaluri' => $subscriptionData['principaluri'],
304
-					'uri' => $subscriptionData['uri']
305
-				]);
306
-
307
-				/** @var CalDavBackend $calDavBackend */
308
-				$calDavBackend = $container->query(CalDavBackend::class);
309
-				$calDavBackend->purgeAllCachedEventsForSubscription($subscriptionData['id']);
310
-				/** @var ReminderBackend $calDavBackend */
311
-				$reminderBackend = $container->query(ReminderBackend::class);
312
-				$reminderBackend->cleanRemindersForCalendar($subscriptionData['id']);
313
-			}
314
-		);
315
-
316
-		$eventHandler = function () use ($container, $serverContainer): void {
317
-			try {
318
-				/** @var UpdateCalendarResourcesRoomsBackgroundJob $job */
319
-				$job = $container->query(UpdateCalendarResourcesRoomsBackgroundJob::class);
320
-				$job->run([]);
321
-				$serverContainer->getJobList()->setLastRun($job);
322
-			} catch (Exception $ex) {
323
-				$serverContainer->getLogger()->logException($ex);
324
-			}
325
-		};
326
-
327
-		$dispatcher->addListener('\OCP\Calendar\Resource\ForceRefreshEvent', $eventHandler);
328
-		$dispatcher->addListener('\OCP\Calendar\Room\ForceRefreshEvent', $eventHandler);
329
-	}
330
-
331
-	public function registerContactsManager(IContactsManager $cm, IAppContainer $container): void {
332
-		$cm->register(function () use ($container, $cm): void {
333
-			$user = \OC::$server->getUserSession()->getUser();
334
-			if (!is_null($user)) {
335
-				$this->setupContactsProvider($cm, $container, $user->getUID());
336
-			} else {
337
-				$this->setupSystemContactsProvider($cm, $container);
338
-			}
339
-		});
340
-	}
341
-
342
-	private function setupContactsProvider(IContactsManager $contactsManager,
343
-										   IAppContainer $container,
344
-										   string $userID): void {
345
-		/** @var ContactsManager $cm */
346
-		$cm = $container->query(ContactsManager::class);
347
-		$urlGenerator = $container->getServer()->getURLGenerator();
348
-		$cm->setupContactsProvider($contactsManager, $userID, $urlGenerator);
349
-	}
350
-
351
-	private function setupSystemContactsProvider(IContactsManager $contactsManager,
352
-												 IAppContainer $container): void {
353
-		/** @var ContactsManager $cm */
354
-		$cm = $container->query(ContactsManager::class);
355
-		$urlGenerator = $container->getServer()->getURLGenerator();
356
-		$cm->setupSystemContactsProvider($contactsManager, $urlGenerator);
357
-	}
358
-
359
-	public function registerCalendarManager(ICalendarManager $calendarManager,
360
-											 IAppContainer $container): void {
361
-		$calendarManager->register(function () use ($container, $calendarManager) {
362
-			$user = \OC::$server->getUserSession()->getUser();
363
-			if ($user !== null) {
364
-				$this->setupCalendarProvider($calendarManager, $container, $user->getUID());
365
-			}
366
-		});
367
-	}
368
-
369
-	private function setupCalendarProvider(ICalendarManager $calendarManager,
370
-										   IAppContainer $container,
371
-										   $userId) {
372
-		$cm = $container->query(CalendarManager::class);
373
-		$cm->setupCalendarProvider($calendarManager, $userId);
374
-	}
375
-
376
-	public function registerCalendarReminders(NotificationProviderManager $manager,
377
-											   ILogger $logger): void {
378
-		try {
379
-			$manager->registerProvider(AudioProvider::class);
380
-			$manager->registerProvider(EmailProvider::class);
381
-			$manager->registerProvider(PushProvider::class);
382
-		} catch (Throwable $ex) {
383
-			$logger->logException($ex);
384
-		}
385
-	}
126
+        $context->registerSearchProvider(ContactsSearchProvider::class);
127
+        $context->registerSearchProvider(EventsSearchProvider::class);
128
+        $context->registerSearchProvider(TasksSearchProvider::class);
129
+
130
+        /**
131
+         * Register event listeners
132
+         */
133
+        $context->registerEventListener(CalendarCreatedEvent::class, ActivityUpdaterListener::class);
134
+        $context->registerEventListener(CalendarDeletedEvent::class, ActivityUpdaterListener::class);
135
+        $context->registerEventListener(CalendarDeletedEvent::class, CalendarObjectReminderUpdaterListener::class);
136
+        $context->registerEventListener(CalendarDeletedEvent::class, CalendarDeletionDefaultUpdaterListener::class);
137
+        $context->registerEventListener(CalendarMovedToTrashEvent::class, ActivityUpdaterListener::class);
138
+        $context->registerEventListener(CalendarMovedToTrashEvent::class, CalendarObjectReminderUpdaterListener::class);
139
+        $context->registerEventListener(CalendarUpdatedEvent::class, ActivityUpdaterListener::class);
140
+        $context->registerEventListener(CalendarRestoredEvent::class, ActivityUpdaterListener::class);
141
+        $context->registerEventListener(CalendarRestoredEvent::class, CalendarObjectReminderUpdaterListener::class);
142
+        $context->registerEventListener(CalendarObjectCreatedEvent::class, ActivityUpdaterListener::class);
143
+        $context->registerEventListener(CalendarObjectCreatedEvent::class, CalendarContactInteractionListener::class);
144
+        $context->registerEventListener(CalendarObjectCreatedEvent::class, CalendarObjectReminderUpdaterListener::class);
145
+        $context->registerEventListener(CalendarObjectUpdatedEvent::class, ActivityUpdaterListener::class);
146
+        $context->registerEventListener(CalendarObjectUpdatedEvent::class, CalendarContactInteractionListener::class);
147
+        $context->registerEventListener(CalendarObjectUpdatedEvent::class, CalendarObjectReminderUpdaterListener::class);
148
+        $context->registerEventListener(CalendarObjectDeletedEvent::class, ActivityUpdaterListener::class);
149
+        $context->registerEventListener(CalendarObjectDeletedEvent::class, CalendarObjectReminderUpdaterListener::class);
150
+        $context->registerEventListener(CalendarObjectMovedToTrashEvent::class, ActivityUpdaterListener::class);
151
+        $context->registerEventListener(CalendarObjectMovedToTrashEvent::class, CalendarObjectReminderUpdaterListener::class);
152
+        $context->registerEventListener(CalendarObjectRestoredEvent::class, ActivityUpdaterListener::class);
153
+        $context->registerEventListener(CalendarObjectRestoredEvent::class, CalendarObjectReminderUpdaterListener::class);
154
+        $context->registerEventListener(CalendarShareUpdatedEvent::class, CalendarContactInteractionListener::class);
155
+
156
+
157
+        $context->registerEventListener(AddressBookCreatedEvent::class, AddressbookListener::class);
158
+        $context->registerEventListener(AddressBookDeletedEvent::class, AddressbookListener::class);
159
+        $context->registerEventListener(AddressBookUpdatedEvent::class, AddressbookListener::class);
160
+        $context->registerEventListener(AddressBookShareUpdatedEvent::class, AddressbookListener::class);
161
+        $context->registerEventListener(CardCreatedEvent::class, CardListener::class);
162
+        $context->registerEventListener(CardDeletedEvent::class, CardListener::class);
163
+        $context->registerEventListener(CardUpdatedEvent::class, CardListener::class);
164
+
165
+        $context->registerNotifierService(Notifier::class);
166
+
167
+        $context->registerCalendarProvider(CalendarProvider::class);
168
+    }
169
+
170
+    public function boot(IBootContext $context): void {
171
+        // Load all dav apps
172
+        \OC_App::loadApps(['dav']);
173
+
174
+        $context->injectFn([$this, 'registerHooks']);
175
+        $context->injectFn([$this, 'registerContactsManager']);
176
+        $context->injectFn([$this, 'registerCalendarManager']);
177
+        $context->injectFn([$this, 'registerCalendarReminders']);
178
+    }
179
+
180
+    public function registerHooks(HookManager $hm,
181
+                                    EventDispatcherInterface $dispatcher,
182
+                                    IAppContainer $container,
183
+                                    IServerContainer $serverContainer) {
184
+        $hm->setup();
185
+
186
+        // first time login event setup
187
+        $dispatcher->addListener(IUser::class . '::firstLogin', function ($event) use ($hm) {
188
+            if ($event instanceof GenericEvent) {
189
+                $hm->firstLogin($event->getSubject());
190
+            }
191
+        });
192
+
193
+        $birthdayListener = function ($event) use ($container): void {
194
+            if ($event instanceof GenericEvent) {
195
+                /** @var BirthdayService $b */
196
+                $b = $container->query(BirthdayService::class);
197
+                $b->onCardChanged(
198
+                    (int) $event->getArgument('addressBookId'),
199
+                    (string) $event->getArgument('cardUri'),
200
+                    (string) $event->getArgument('cardData')
201
+                );
202
+            }
203
+        };
204
+
205
+        $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::createCard', $birthdayListener);
206
+        $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $birthdayListener);
207
+        $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', function ($event) use ($container) {
208
+            if ($event instanceof GenericEvent) {
209
+                /** @var BirthdayService $b */
210
+                $b = $container->query(BirthdayService::class);
211
+                $b->onCardDeleted(
212
+                    (int) $event->getArgument('addressBookId'),
213
+                    (string) $event->getArgument('cardUri')
214
+                );
215
+            }
216
+        });
217
+
218
+        $clearPhotoCache = function ($event) use ($container): void {
219
+            if ($event instanceof GenericEvent) {
220
+                /** @var PhotoCache $p */
221
+                $p = $container->query(PhotoCache::class);
222
+                $p->delete(
223
+                    $event->getArgument('addressBookId'),
224
+                    $event->getArgument('cardUri')
225
+                );
226
+            }
227
+        };
228
+        $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $clearPhotoCache);
229
+        $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', $clearPhotoCache);
230
+
231
+        $dispatcher->addListener('OC\AccountManager::userUpdated', function (GenericEvent $event) use ($container) {
232
+            $user = $event->getSubject();
233
+            /** @var SyncService $syncService */
234
+            $syncService = $container->query(SyncService::class);
235
+            $syncService->updateUser($user);
236
+        });
237
+
238
+
239
+        $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateShares', function (GenericEvent $event) use ($container) {
240
+            /** @var Backend $backend */
241
+            $backend = $container->query(Backend::class);
242
+            $backend->onCalendarUpdateShares(
243
+                $event->getArgument('calendarData'),
244
+                $event->getArgument('shares'),
245
+                $event->getArgument('add'),
246
+                $event->getArgument('remove')
247
+            );
248
+
249
+            // Here we should recalculate if reminders should be sent to new or old sharees
250
+        });
251
+
252
+        $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::publishCalendar', function (GenericEvent $event) use ($container) {
253
+            /** @var Backend $backend */
254
+            $backend = $container->query(Backend::class);
255
+            $backend->onCalendarPublication(
256
+                $event->getArgument('calendarData'),
257
+                $event->getArgument('public')
258
+            );
259
+        });
260
+
261
+
262
+        $dispatcher->addListener('OCP\Federation\TrustedServerEvent::remove',
263
+            function (GenericEvent $event) {
264
+                /** @var CardDavBackend $cardDavBackend */
265
+                $cardDavBackend = \OC::$server->query(CardDavBackend::class);
266
+                $addressBookUri = $event->getSubject();
267
+                $addressBook = $cardDavBackend->getAddressBooksByUri('principals/system/system', $addressBookUri);
268
+                if (!is_null($addressBook)) {
269
+                    $cardDavBackend->deleteAddressBook($addressBook['id']);
270
+                }
271
+            }
272
+        );
273
+
274
+        $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::createSubscription',
275
+            function (GenericEvent $event) use ($container, $serverContainer) {
276
+                $jobList = $serverContainer->getJobList();
277
+                $subscriptionData = $event->getArgument('subscriptionData');
278
+
279
+                /**
280
+                 * Initial subscription refetch
281
+                 *
282
+                 * @var RefreshWebcalService $refreshWebcalService
283
+                 */
284
+                $refreshWebcalService = $container->query(RefreshWebcalService::class);
285
+                $refreshWebcalService->refreshSubscription(
286
+                    (string) $subscriptionData['principaluri'],
287
+                    (string) $subscriptionData['uri']
288
+                );
289
+
290
+                $jobList->add(\OCA\DAV\BackgroundJob\RefreshWebcalJob::class, [
291
+                    'principaluri' => $subscriptionData['principaluri'],
292
+                    'uri' => $subscriptionData['uri']
293
+                ]);
294
+            }
295
+        );
296
+
297
+        $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteSubscription',
298
+            function (GenericEvent $event) use ($container, $serverContainer) {
299
+                $jobList = $serverContainer->getJobList();
300
+                $subscriptionData = $event->getArgument('subscriptionData');
301
+
302
+                $jobList->remove(\OCA\DAV\BackgroundJob\RefreshWebcalJob::class, [
303
+                    'principaluri' => $subscriptionData['principaluri'],
304
+                    'uri' => $subscriptionData['uri']
305
+                ]);
306
+
307
+                /** @var CalDavBackend $calDavBackend */
308
+                $calDavBackend = $container->query(CalDavBackend::class);
309
+                $calDavBackend->purgeAllCachedEventsForSubscription($subscriptionData['id']);
310
+                /** @var ReminderBackend $calDavBackend */
311
+                $reminderBackend = $container->query(ReminderBackend::class);
312
+                $reminderBackend->cleanRemindersForCalendar($subscriptionData['id']);
313
+            }
314
+        );
315
+
316
+        $eventHandler = function () use ($container, $serverContainer): void {
317
+            try {
318
+                /** @var UpdateCalendarResourcesRoomsBackgroundJob $job */
319
+                $job = $container->query(UpdateCalendarResourcesRoomsBackgroundJob::class);
320
+                $job->run([]);
321
+                $serverContainer->getJobList()->setLastRun($job);
322
+            } catch (Exception $ex) {
323
+                $serverContainer->getLogger()->logException($ex);
324
+            }
325
+        };
326
+
327
+        $dispatcher->addListener('\OCP\Calendar\Resource\ForceRefreshEvent', $eventHandler);
328
+        $dispatcher->addListener('\OCP\Calendar\Room\ForceRefreshEvent', $eventHandler);
329
+    }
330
+
331
+    public function registerContactsManager(IContactsManager $cm, IAppContainer $container): void {
332
+        $cm->register(function () use ($container, $cm): void {
333
+            $user = \OC::$server->getUserSession()->getUser();
334
+            if (!is_null($user)) {
335
+                $this->setupContactsProvider($cm, $container, $user->getUID());
336
+            } else {
337
+                $this->setupSystemContactsProvider($cm, $container);
338
+            }
339
+        });
340
+    }
341
+
342
+    private function setupContactsProvider(IContactsManager $contactsManager,
343
+                                            IAppContainer $container,
344
+                                            string $userID): void {
345
+        /** @var ContactsManager $cm */
346
+        $cm = $container->query(ContactsManager::class);
347
+        $urlGenerator = $container->getServer()->getURLGenerator();
348
+        $cm->setupContactsProvider($contactsManager, $userID, $urlGenerator);
349
+    }
350
+
351
+    private function setupSystemContactsProvider(IContactsManager $contactsManager,
352
+                                                    IAppContainer $container): void {
353
+        /** @var ContactsManager $cm */
354
+        $cm = $container->query(ContactsManager::class);
355
+        $urlGenerator = $container->getServer()->getURLGenerator();
356
+        $cm->setupSystemContactsProvider($contactsManager, $urlGenerator);
357
+    }
358
+
359
+    public function registerCalendarManager(ICalendarManager $calendarManager,
360
+                                                IAppContainer $container): void {
361
+        $calendarManager->register(function () use ($container, $calendarManager) {
362
+            $user = \OC::$server->getUserSession()->getUser();
363
+            if ($user !== null) {
364
+                $this->setupCalendarProvider($calendarManager, $container, $user->getUID());
365
+            }
366
+        });
367
+    }
368
+
369
+    private function setupCalendarProvider(ICalendarManager $calendarManager,
370
+                                            IAppContainer $container,
371
+                                            $userId) {
372
+        $cm = $container->query(CalendarManager::class);
373
+        $cm->setupCalendarProvider($calendarManager, $userId);
374
+    }
375
+
376
+    public function registerCalendarReminders(NotificationProviderManager $manager,
377
+                                                ILogger $logger): void {
378
+        try {
379
+            $manager->registerProvider(AudioProvider::class);
380
+            $manager->registerProvider(EmailProvider::class);
381
+            $manager->registerProvider(PushProvider::class);
382
+        } catch (Throwable $ex) {
383
+            $logger->logException($ex);
384
+        }
385
+    }
386 386
 }
Please login to merge, or discard this patch.