Completed
Push — master ( 88ba65...9a0892 )
by Christoph
24:20
created
tests/lib/Calendar/ManagerTest.php 1 patch
Indentation   +732 added lines, -732 removed lines patch added patch discarded remove patch
@@ -42,603 +42,603 @@  discard block
 block discarded – undo
42 42
 }
43 43
 
44 44
 class ManagerTest extends TestCase {
45
-	/** @var Coordinator&MockObject */
46
-	private $coordinator;
47
-
48
-	/** @var ContainerInterface&MockObject */
49
-	private $container;
50
-
51
-	/** @var LoggerInterface&MockObject */
52
-	private $logger;
53
-
54
-	/** @var Manager */
55
-	private $manager;
56
-
57
-	/** @var ITimeFactory&MockObject */
58
-	private $time;
59
-
60
-	/** @var ISecureRandom&MockObject */
61
-	private ISecureRandom $secureRandom;
62
-
63
-	private IUserManager&MockObject $userManager;
64
-	private ServerFactory&MockObject $serverFactory;
65
-	private PropertyMapper&MockObject $propertyMapper;
66
-
67
-	private VCalendar $vCalendar1a;
68
-	private VCalendar $vCalendar2a;
69
-	private VCalendar $vCalendar3a;
70
-
71
-	protected function setUp(): void {
72
-		parent::setUp();
73
-
74
-		$this->coordinator = $this->createMock(Coordinator::class);
75
-		$this->container = $this->createMock(ContainerInterface::class);
76
-		$this->logger = $this->createMock(LoggerInterface::class);
77
-		$this->time = $this->createMock(ITimeFactory::class);
78
-		$this->secureRandom = $this->createMock(ISecureRandom::class);
79
-		$this->userManager = $this->createMock(IUserManager::class);
80
-		$this->serverFactory = $this->createMock(ServerFactory::class);
81
-		$this->propertyMapper = $this->createMock(PropertyMapper::class);
82
-
83
-		$this->manager = new Manager(
84
-			$this->coordinator,
85
-			$this->container,
86
-			$this->logger,
87
-			$this->time,
88
-			$this->secureRandom,
89
-			$this->userManager,
90
-			$this->serverFactory,
91
-			$this->propertyMapper,
92
-		);
93
-
94
-		// construct calendar with a 1 hour event and same start/end time zones
95
-		$this->vCalendar1a = new VCalendar();
96
-		/** @var VEvent $vEvent */
97
-		$vEvent = $this->vCalendar1a->add('VEVENT', []);
98
-		$vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc');
99
-		$vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']);
100
-		$vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Toronto']);
101
-		$vEvent->add('SUMMARY', 'Test Event');
102
-		$vEvent->add('SEQUENCE', 3);
103
-		$vEvent->add('STATUS', 'CONFIRMED');
104
-		$vEvent->add('TRANSP', 'OPAQUE');
105
-		$vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'Organizer']);
106
-		$vEvent->add('ATTENDEE', 'mailto:[email protected]', [
107
-			'CN' => 'Attendee One',
108
-			'CUTYPE' => 'INDIVIDUAL',
109
-			'PARTSTAT' => 'NEEDS-ACTION',
110
-			'ROLE' => 'REQ-PARTICIPANT',
111
-			'RSVP' => 'TRUE'
112
-		]);
113
-
114
-		// construct calendar with a event for reply
115
-		$this->vCalendar2a = new VCalendar();
116
-		/** @var VEvent $vEvent */
117
-		$vEvent = $this->vCalendar2a->add('VEVENT', []);
118
-		$vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff');
119
-		$vEvent->add('DTSTART', '20210820');
120
-		$vEvent->add('DTEND', '20220821');
121
-		$vEvent->add('SUMMARY', 'berry basket');
122
-		$vEvent->add('SEQUENCE', 3);
123
-		$vEvent->add('STATUS', 'CONFIRMED');
124
-		$vEvent->add('TRANSP', 'OPAQUE');
125
-		$vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'admin']);
126
-		$vEvent->add('ATTENDEE', 'mailto:[email protected]', [
127
-			'CN' => '[email protected]',
128
-			'CUTYPE' => 'INDIVIDUAL',
129
-			'ROLE' => 'REQ-PARTICIPANT',
130
-			'PARTSTAT' => 'ACCEPTED',
131
-		]);
132
-
133
-		// construct calendar with a event for reply
134
-		$this->vCalendar3a = new VCalendar();
135
-		/** @var VEvent $vEvent */
136
-		$vEvent = $this->vCalendar3a->add('VEVENT', []);
137
-		$vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff');
138
-		$vEvent->add('DTSTART', '20210820');
139
-		$vEvent->add('DTEND', '20220821');
140
-		$vEvent->add('SUMMARY', 'berry basket');
141
-		$vEvent->add('SEQUENCE', 3);
142
-		$vEvent->add('STATUS', 'CANCELLED');
143
-		$vEvent->add('TRANSP', 'OPAQUE');
144
-		$vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'admin']);
145
-		$vEvent->add('ATTENDEE', 'mailto:[email protected]', [
146
-			'CN' => '[email protected]',
147
-			'CUTYPE' => 'INDIVIDUAL',
148
-			'ROLE' => 'REQ-PARTICIPANT',
149
-			'PARTSTAT' => 'ACCEPTED',
150
-		]);
151
-
152
-	}
153
-
154
-	#[\PHPUnit\Framework\Attributes\DataProvider('searchProvider')]
155
-	public function testSearch($search1, $search2, $expected): void {
156
-		/** @var ICalendar | MockObject $calendar1 */
157
-		$calendar1 = $this->createMock(ICalendar::class);
158
-		$calendar1->method('getKey')->willReturn('simple:1');
159
-		$calendar1->expects($this->once())
160
-			->method('search')
161
-			->with('', [], [], null, null)
162
-			->willReturn($search1);
163
-
164
-		/** @var ICalendar | MockObject $calendar2 */
165
-		$calendar2 = $this->createMock(ICalendar::class);
166
-		$calendar2->method('getKey')->willReturn('simple:2');
167
-		$calendar2->expects($this->once())
168
-			->method('search')
169
-			->with('', [], [], null, null)
170
-			->willReturn($search2);
171
-
172
-		$this->manager->registerCalendar($calendar1);
173
-		$this->manager->registerCalendar($calendar2);
174
-
175
-		$result = $this->manager->search('');
176
-		$this->assertEquals($expected, $result);
177
-	}
178
-
179
-	#[\PHPUnit\Framework\Attributes\DataProvider('searchProvider')]
180
-	public function testSearchOptions($search1, $search2, $expected): void {
181
-		/** @var ICalendar | MockObject $calendar1 */
182
-		$calendar1 = $this->createMock(ICalendar::class);
183
-		$calendar1->method('getKey')->willReturn('simple:1');
184
-		$calendar1->expects($this->once())
185
-			->method('search')
186
-			->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
187
-				['timerange' => ['start' => null, 'end' => null]], 5, 20)
188
-			->willReturn($search1);
189
-
190
-		/** @var ICalendar | MockObject $calendar2 */
191
-		$calendar2 = $this->createMock(ICalendar::class);
192
-		$calendar2->method('getKey')->willReturn('simple:2');
193
-		$calendar2->expects($this->once())
194
-			->method('search')
195
-			->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
196
-				['timerange' => ['start' => null, 'end' => null]], 5, 20)
197
-			->willReturn($search2);
198
-
199
-		$this->manager->registerCalendar($calendar1);
200
-		$this->manager->registerCalendar($calendar2);
201
-
202
-		$result = $this->manager->search('searchTerm', ['SUMMARY', 'DESCRIPTION'],
203
-			['timerange' => ['start' => null, 'end' => null]], 5, 20);
204
-		$this->assertEquals($expected, $result);
205
-	}
206
-
207
-	public static function searchProvider(): array {
208
-		$search1 = [
209
-			[
210
-				'id' => 1,
211
-				'data' => 'foobar',
212
-			],
213
-			[
214
-				'id' => 2,
215
-				'data' => 'barfoo',
216
-			]
217
-		];
218
-		$search2 = [
219
-			[
220
-				'id' => 3,
221
-				'data' => 'blablub',
222
-			],
223
-			[
224
-				'id' => 4,
225
-				'data' => 'blubbla',
226
-			]
227
-		];
228
-
229
-		$expected = [
230
-			[
231
-				'id' => 1,
232
-				'data' => 'foobar',
233
-				'calendar-key' => 'simple:1',
234
-			],
235
-			[
236
-				'id' => 2,
237
-				'data' => 'barfoo',
238
-				'calendar-key' => 'simple:1',
239
-			],
240
-			[
241
-				'id' => 3,
242
-				'data' => 'blablub',
243
-				'calendar-key' => 'simple:2',
244
-			],
245
-			[
246
-				'id' => 4,
247
-				'data' => 'blubbla',
248
-				'calendar-key' => 'simple:2',
249
-			]
250
-		];
251
-
252
-		return [
253
-			[
254
-				$search1,
255
-				$search2,
256
-				$expected
257
-			]
258
-		];
259
-	}
260
-
261
-	public function testRegisterUnregister(): void {
262
-		/** @var ICalendar | MockObject $calendar1 */
263
-		$calendar1 = $this->createMock(ICalendar::class);
264
-		$calendar1->method('getKey')->willReturn('key1');
265
-
266
-		/** @var ICalendar | MockObject $calendar2 */
267
-		$calendar2 = $this->createMock(ICalendar::class);
268
-		$calendar2->method('getKey')->willReturn('key2');
269
-
270
-		$this->manager->registerCalendar($calendar1);
271
-		$this->manager->registerCalendar($calendar2);
272
-
273
-		$result = $this->manager->getCalendars();
274
-		$this->assertCount(2, $result);
275
-		$this->assertContains($calendar1, $result);
276
-		$this->assertContains($calendar2, $result);
277
-
278
-		$this->manager->unregisterCalendar($calendar1);
279
-
280
-		$result = $this->manager->getCalendars();
281
-		$this->assertCount(1, $result);
282
-		$this->assertContains($calendar2, $result);
283
-	}
284
-
285
-	public function testGetCalendars(): void {
286
-		/** @var ICalendar | MockObject $calendar1 */
287
-		$calendar1 = $this->createMock(ICalendar::class);
288
-		$calendar1->method('getKey')->willReturn('key1');
289
-
290
-		/** @var ICalendar | MockObject $calendar2 */
291
-		$calendar2 = $this->createMock(ICalendar::class);
292
-		$calendar2->method('getKey')->willReturn('key2');
293
-
294
-		$this->manager->registerCalendar($calendar1);
295
-		$this->manager->registerCalendar($calendar2);
296
-
297
-		$result = $this->manager->getCalendars();
298
-		$this->assertCount(2, $result);
299
-		$this->assertContains($calendar1, $result);
300
-		$this->assertContains($calendar2, $result);
301
-
302
-		$this->manager->clear();
303
-
304
-		$result = $this->manager->getCalendars();
305
-
306
-		$this->assertCount(0, $result);
307
-	}
308
-
309
-	public function testEnabledIfNot(): void {
310
-		$isEnabled = $this->manager->isEnabled();
311
-		$this->assertFalse($isEnabled);
312
-	}
313
-
314
-	public function testIfEnabledIfSo(): void {
315
-		/** @var ICalendar | MockObject $calendar */
316
-		$calendar = $this->createMock(ICalendar::class);
317
-		$this->manager->registerCalendar($calendar);
318
-
319
-		$isEnabled = $this->manager->isEnabled();
320
-		$this->assertTrue($isEnabled);
321
-	}
322
-
323
-	public function testHandleImipWithNoCalendars(): void {
324
-		// construct calendar manager returns
325
-		/** @var Manager&MockObject $manager */
326
-		$manager = $this->getMockBuilder(Manager::class)
327
-			->setConstructorArgs([
328
-				$this->coordinator,
329
-				$this->container,
330
-				$this->logger,
331
-				$this->time,
332
-				$this->secureRandom,
333
-				$this->userManager,
334
-				$this->serverFactory,
335
-				$this->propertyMapper,
336
-			])
337
-			->onlyMethods(['getCalendarsForPrincipal'])
338
-			->getMock();
339
-		$manager->expects(self::once())
340
-			->method('getCalendarsForPrincipal')
341
-			->willReturn([]);
342
-		// construct logger returns
343
-		$this->logger->expects(self::once())->method('warning')
344
-			->with('iMip message could not be processed because user has no calendars');
345
-		// construct parameters
346
-		$userId = 'attendee1';
347
-		$calendar = $this->vCalendar1a;
348
-		$calendar->add('METHOD', 'REQUEST');
349
-		// test method
350
-		$result = $manager->handleIMip($userId, $calendar->serialize());
351
-		// Assert
352
-		$this->assertFalse($result);
353
-	}
354
-
355
-	public function testHandleImipWithNoEvent(): void {
356
-		// construct mock user calendar
357
-		$userCalendar = $this->createMock(ITestCalendar::class);
358
-		// construct mock calendar manager and returns
359
-		/** @var Manager&MockObject $manager */
360
-		$manager = $this->getMockBuilder(Manager::class)
361
-			->setConstructorArgs([
362
-				$this->coordinator,
363
-				$this->container,
364
-				$this->logger,
365
-				$this->time,
366
-				$this->secureRandom,
367
-				$this->userManager,
368
-				$this->serverFactory,
369
-				$this->propertyMapper,
370
-			])
371
-			->onlyMethods(['getCalendarsForPrincipal'])
372
-			->getMock();
373
-		$manager->expects(self::once())
374
-			->method('getCalendarsForPrincipal')
375
-			->willReturn([$userCalendar]);
376
-		// construct logger returns
377
-		$this->logger->expects(self::once())->method('warning')
378
-			->with('iMip message does not contain any event(s)');
379
-		// construct parameters
380
-		$userId = 'attendee1';
381
-		$calendar = $this->vCalendar1a;
382
-		$calendar->add('METHOD', 'REQUEST');
383
-		$calendar->remove('VEVENT');
384
-		// Act
385
-		$result = $manager->handleIMip($userId, $calendar->serialize());
386
-		// Assert
387
-		$this->assertFalse($result);
388
-	}
389
-
390
-	public function testHandleImipWithNoUid(): void {
391
-		// construct mock user calendar
392
-		$userCalendar = $this->createMock(ITestCalendar::class);
393
-		// construct mock calendar manager and returns
394
-		/** @var Manager&MockObject $manager */
395
-		$manager = $this->getMockBuilder(Manager::class)
396
-			->setConstructorArgs([
397
-				$this->coordinator,
398
-				$this->container,
399
-				$this->logger,
400
-				$this->time,
401
-				$this->secureRandom,
402
-				$this->userManager,
403
-				$this->serverFactory,
404
-				$this->propertyMapper,
405
-			])
406
-			->onlyMethods(['getCalendarsForPrincipal'])
407
-			->getMock();
408
-		$manager->expects(self::once())
409
-			->method('getCalendarsForPrincipal')
410
-			->willReturn([$userCalendar]);
411
-		// construct logger returns
412
-		$this->logger->expects(self::once())->method('warning')
413
-			->with('iMip message event dose not contains a UID');
414
-		// construct parameters
415
-		$userId = 'attendee1';
416
-		$calendar = $this->vCalendar1a;
417
-		$calendar->add('METHOD', 'REQUEST');
418
-		$calendar->VEVENT->remove('UID');
419
-		// test method
420
-		$result = $manager->handleIMip($userId, $calendar->serialize());
421
-		// Assert
422
-		$this->assertFalse($result);
423
-	}
424
-
425
-	public function testHandleImipWithNoMatch(): void {
426
-		// construct mock user calendar
427
-		$userCalendar = $this->createMock(ITestCalendar::class);
428
-		$userCalendar->expects(self::once())
429
-			->method('isDeleted')
430
-			->willReturn(false);
431
-		$userCalendar->expects(self::once())
432
-			->method('isWritable')
433
-			->willReturn(true);
434
-		$userCalendar->expects(self::once())
435
-			->method('search')
436
-			->willReturn([]);
437
-		// construct mock calendar manager and returns
438
-		/** @var Manager&MockObject $manager */
439
-		$manager = $this->getMockBuilder(Manager::class)
440
-			->setConstructorArgs([
441
-				$this->coordinator,
442
-				$this->container,
443
-				$this->logger,
444
-				$this->time,
445
-				$this->secureRandom,
446
-				$this->userManager,
447
-				$this->serverFactory,
448
-				$this->propertyMapper,
449
-			])
450
-			->onlyMethods(['getCalendarsForPrincipal'])
451
-			->getMock();
452
-		$manager->expects(self::once())
453
-			->method('getCalendarsForPrincipal')
454
-			->willReturn([$userCalendar]);
455
-		// construct logger returns
456
-		$this->logger->expects(self::once())->method('warning')
457
-			->with('iMip message could not be processed because no corresponding event was found in any calendar');
458
-		// construct parameters
459
-		$userId = 'attendee1';
460
-		$calendar = $this->vCalendar1a;
461
-		$calendar->add('METHOD', 'REQUEST');
462
-		// test method
463
-		$result = $manager->handleIMip($userId, $calendar->serialize());
464
-		// Assert
465
-		$this->assertFalse($result);
466
-	}
467
-
468
-	public function testHandleImip(): void {
469
-		// construct mock user calendar
470
-		$userCalendar = $this->createMock(ITestCalendar::class);
471
-		$userCalendar->expects(self::once())
472
-			->method('isDeleted')
473
-			->willReturn(false);
474
-		$userCalendar->expects(self::once())
475
-			->method('isWritable')
476
-			->willReturn(true);
477
-		$userCalendar->expects(self::once())
478
-			->method('search')
479
-			->willReturn([['uri' => 'principals/user/attendee1/personal']]);
480
-		// construct mock calendar manager and returns
481
-		/** @var Manager&MockObject $manager */
482
-		$manager = $this->getMockBuilder(Manager::class)
483
-			->setConstructorArgs([
484
-				$this->coordinator,
485
-				$this->container,
486
-				$this->logger,
487
-				$this->time,
488
-				$this->secureRandom,
489
-				$this->userManager,
490
-				$this->serverFactory,
491
-				$this->propertyMapper,
492
-			])
493
-			->onlyMethods(['getCalendarsForPrincipal'])
494
-			->getMock();
495
-		$manager->expects(self::once())
496
-			->method('getCalendarsForPrincipal')
497
-			->willReturn([$userCalendar]);
498
-		// construct parameters
499
-		$userId = 'attendee1';
500
-		$calendar = $this->vCalendar1a;
501
-		$calendar->add('METHOD', 'REQUEST');
502
-		// construct user calendar returns
503
-		$userCalendar->expects(self::once())
504
-			->method('handleIMipMessage');
505
-		// test method
506
-		$result = $manager->handleIMip($userId, $calendar->serialize());
507
-	}
508
-
509
-	public function testhandleIMipRequestWithInvalidPrincipal() {
510
-		$invalidPrincipal = 'invalid-principal-uri';
511
-		$sender = '[email protected]';
512
-		$recipient = '[email protected]';
513
-		$calendarData = $this->vCalendar1a->serialize();
514
-
515
-		$this->logger->expects(self::once())
516
-			->method('error')
517
-			->with('Invalid principal URI provided for iMip request');
518
-
519
-		$result = $this->manager->handleIMipRequest($invalidPrincipal, $sender, $recipient, $calendarData);
520
-		$this->assertFalse($result);
521
-	}
522
-
523
-	public function testhandleIMipRequest() {
524
-		$principalUri = 'principals/users/attendee1';
525
-		$sender = '[email protected]';
526
-		$recipient = '[email protected]';
527
-		$calendarData = $this->vCalendar1a->serialize();
528
-
529
-		/** @var Manager&MockObject $manager */
530
-		$manager = $this->getMockBuilder(Manager::class)
531
-			->setConstructorArgs([
532
-				$this->coordinator,
533
-				$this->container,
534
-				$this->logger,
535
-				$this->time,
536
-				$this->secureRandom,
537
-				$this->userManager,
538
-				$this->serverFactory,
539
-				$this->propertyMapper,
540
-			])
541
-			->onlyMethods(['handleIMip'])
542
-			->getMock();
543
-		$manager->expects(self::once())
544
-			->method('handleIMip')
545
-			->with('attendee1', $calendarData)
546
-			->willReturn(true);
547
-
548
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendarData);
549
-		$this->assertTrue($result);
550
-	}
551
-
552
-	public function testhandleIMipReplyWithInvalidPrincipal() {
553
-		$invalidPrincipal = 'invalid-principal-uri';
554
-		$sender = '[email protected]';
555
-		$recipient = '[email protected]';
556
-		$calendarData = $this->vCalendar2a->serialize();
557
-
558
-		$this->logger->expects(self::once())
559
-			->method('error')
560
-			->with('Invalid principal URI provided for iMip reply');
561
-
562
-		$result = $this->manager->handleIMipReply($invalidPrincipal, $sender, $recipient, $calendarData);
563
-		$this->assertFalse($result);
564
-	}
565
-
566
-	public function testhandleIMipReply() {
567
-		$principalUri = 'principals/users/attendee2';
568
-		$sender = '[email protected]';
569
-		$recipient = '[email protected]';
570
-		$calendarData = $this->vCalendar2a->serialize();
571
-
572
-		/** @var Manager&MockObject $manager */
573
-		$manager = $this->getMockBuilder(Manager::class)
574
-			->setConstructorArgs([
575
-				$this->coordinator,
576
-				$this->container,
577
-				$this->logger,
578
-				$this->time,
579
-				$this->secureRandom,
580
-				$this->userManager,
581
-				$this->serverFactory,
582
-				$this->propertyMapper,
583
-			])
584
-			->onlyMethods(['handleIMip'])
585
-			->getMock();
586
-		$manager->expects(self::once())
587
-			->method('handleIMip')
588
-			->with('attendee2', $calendarData)
589
-			->willReturn(true);
590
-
591
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData);
592
-		$this->assertTrue($result);
593
-	}
594
-
595
-	public function testhandleIMipCancelWithInvalidPrincipal() {
596
-		$invalidPrincipal = 'invalid-principal-uri';
597
-		$sender = '[email protected]';
598
-		$replyTo = null;
599
-		$recipient = '[email protected]';
600
-		$calendarData = $this->vCalendar3a->serialize();
601
-
602
-		$this->logger->expects(self::once())
603
-			->method('error')
604
-			->with('Invalid principal URI provided for iMip cancel');
605
-
606
-		$result = $this->manager->handleIMipCancel($invalidPrincipal, $sender, $replyTo, $recipient, $calendarData);
607
-		$this->assertFalse($result);
608
-	}
609
-
610
-	public function testhandleIMipCancel() {
611
-		$principalUri = 'principals/users/attendee3';
612
-		$sender = '[email protected]';
613
-		$replyTo = null;
614
-		$recipient = '[email protected]';
615
-		$calendarData = $this->vCalendar3a->serialize();
616
-
617
-		/** @var Manager&MockObject $manager */
618
-		$manager = $this->getMockBuilder(Manager::class)
619
-			->setConstructorArgs([
620
-				$this->coordinator,
621
-				$this->container,
622
-				$this->logger,
623
-				$this->time,
624
-				$this->secureRandom,
625
-				$this->userManager,
626
-				$this->serverFactory,
627
-				$this->propertyMapper,
628
-			])
629
-			->onlyMethods(['handleIMip'])
630
-			->getMock();
631
-		$manager->expects(self::once())
632
-			->method('handleIMip')
633
-			->with('attendee3', $calendarData)
634
-			->willReturn(true);
635
-
636
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData);
637
-		$this->assertTrue($result);
638
-	}
639
-
640
-	private function getFreeBusyResponse(): string {
641
-		return <<<EOF
45
+    /** @var Coordinator&MockObject */
46
+    private $coordinator;
47
+
48
+    /** @var ContainerInterface&MockObject */
49
+    private $container;
50
+
51
+    /** @var LoggerInterface&MockObject */
52
+    private $logger;
53
+
54
+    /** @var Manager */
55
+    private $manager;
56
+
57
+    /** @var ITimeFactory&MockObject */
58
+    private $time;
59
+
60
+    /** @var ISecureRandom&MockObject */
61
+    private ISecureRandom $secureRandom;
62
+
63
+    private IUserManager&MockObject $userManager;
64
+    private ServerFactory&MockObject $serverFactory;
65
+    private PropertyMapper&MockObject $propertyMapper;
66
+
67
+    private VCalendar $vCalendar1a;
68
+    private VCalendar $vCalendar2a;
69
+    private VCalendar $vCalendar3a;
70
+
71
+    protected function setUp(): void {
72
+        parent::setUp();
73
+
74
+        $this->coordinator = $this->createMock(Coordinator::class);
75
+        $this->container = $this->createMock(ContainerInterface::class);
76
+        $this->logger = $this->createMock(LoggerInterface::class);
77
+        $this->time = $this->createMock(ITimeFactory::class);
78
+        $this->secureRandom = $this->createMock(ISecureRandom::class);
79
+        $this->userManager = $this->createMock(IUserManager::class);
80
+        $this->serverFactory = $this->createMock(ServerFactory::class);
81
+        $this->propertyMapper = $this->createMock(PropertyMapper::class);
82
+
83
+        $this->manager = new Manager(
84
+            $this->coordinator,
85
+            $this->container,
86
+            $this->logger,
87
+            $this->time,
88
+            $this->secureRandom,
89
+            $this->userManager,
90
+            $this->serverFactory,
91
+            $this->propertyMapper,
92
+        );
93
+
94
+        // construct calendar with a 1 hour event and same start/end time zones
95
+        $this->vCalendar1a = new VCalendar();
96
+        /** @var VEvent $vEvent */
97
+        $vEvent = $this->vCalendar1a->add('VEVENT', []);
98
+        $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc');
99
+        $vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']);
100
+        $vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Toronto']);
101
+        $vEvent->add('SUMMARY', 'Test Event');
102
+        $vEvent->add('SEQUENCE', 3);
103
+        $vEvent->add('STATUS', 'CONFIRMED');
104
+        $vEvent->add('TRANSP', 'OPAQUE');
105
+        $vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'Organizer']);
106
+        $vEvent->add('ATTENDEE', 'mailto:[email protected]', [
107
+            'CN' => 'Attendee One',
108
+            'CUTYPE' => 'INDIVIDUAL',
109
+            'PARTSTAT' => 'NEEDS-ACTION',
110
+            'ROLE' => 'REQ-PARTICIPANT',
111
+            'RSVP' => 'TRUE'
112
+        ]);
113
+
114
+        // construct calendar with a event for reply
115
+        $this->vCalendar2a = new VCalendar();
116
+        /** @var VEvent $vEvent */
117
+        $vEvent = $this->vCalendar2a->add('VEVENT', []);
118
+        $vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff');
119
+        $vEvent->add('DTSTART', '20210820');
120
+        $vEvent->add('DTEND', '20220821');
121
+        $vEvent->add('SUMMARY', 'berry basket');
122
+        $vEvent->add('SEQUENCE', 3);
123
+        $vEvent->add('STATUS', 'CONFIRMED');
124
+        $vEvent->add('TRANSP', 'OPAQUE');
125
+        $vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'admin']);
126
+        $vEvent->add('ATTENDEE', 'mailto:[email protected]', [
127
+            'CN' => '[email protected]',
128
+            'CUTYPE' => 'INDIVIDUAL',
129
+            'ROLE' => 'REQ-PARTICIPANT',
130
+            'PARTSTAT' => 'ACCEPTED',
131
+        ]);
132
+
133
+        // construct calendar with a event for reply
134
+        $this->vCalendar3a = new VCalendar();
135
+        /** @var VEvent $vEvent */
136
+        $vEvent = $this->vCalendar3a->add('VEVENT', []);
137
+        $vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff');
138
+        $vEvent->add('DTSTART', '20210820');
139
+        $vEvent->add('DTEND', '20220821');
140
+        $vEvent->add('SUMMARY', 'berry basket');
141
+        $vEvent->add('SEQUENCE', 3);
142
+        $vEvent->add('STATUS', 'CANCELLED');
143
+        $vEvent->add('TRANSP', 'OPAQUE');
144
+        $vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'admin']);
145
+        $vEvent->add('ATTENDEE', 'mailto:[email protected]', [
146
+            'CN' => '[email protected]',
147
+            'CUTYPE' => 'INDIVIDUAL',
148
+            'ROLE' => 'REQ-PARTICIPANT',
149
+            'PARTSTAT' => 'ACCEPTED',
150
+        ]);
151
+
152
+    }
153
+
154
+    #[\PHPUnit\Framework\Attributes\DataProvider('searchProvider')]
155
+    public function testSearch($search1, $search2, $expected): void {
156
+        /** @var ICalendar | MockObject $calendar1 */
157
+        $calendar1 = $this->createMock(ICalendar::class);
158
+        $calendar1->method('getKey')->willReturn('simple:1');
159
+        $calendar1->expects($this->once())
160
+            ->method('search')
161
+            ->with('', [], [], null, null)
162
+            ->willReturn($search1);
163
+
164
+        /** @var ICalendar | MockObject $calendar2 */
165
+        $calendar2 = $this->createMock(ICalendar::class);
166
+        $calendar2->method('getKey')->willReturn('simple:2');
167
+        $calendar2->expects($this->once())
168
+            ->method('search')
169
+            ->with('', [], [], null, null)
170
+            ->willReturn($search2);
171
+
172
+        $this->manager->registerCalendar($calendar1);
173
+        $this->manager->registerCalendar($calendar2);
174
+
175
+        $result = $this->manager->search('');
176
+        $this->assertEquals($expected, $result);
177
+    }
178
+
179
+    #[\PHPUnit\Framework\Attributes\DataProvider('searchProvider')]
180
+    public function testSearchOptions($search1, $search2, $expected): void {
181
+        /** @var ICalendar | MockObject $calendar1 */
182
+        $calendar1 = $this->createMock(ICalendar::class);
183
+        $calendar1->method('getKey')->willReturn('simple:1');
184
+        $calendar1->expects($this->once())
185
+            ->method('search')
186
+            ->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
187
+                ['timerange' => ['start' => null, 'end' => null]], 5, 20)
188
+            ->willReturn($search1);
189
+
190
+        /** @var ICalendar | MockObject $calendar2 */
191
+        $calendar2 = $this->createMock(ICalendar::class);
192
+        $calendar2->method('getKey')->willReturn('simple:2');
193
+        $calendar2->expects($this->once())
194
+            ->method('search')
195
+            ->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
196
+                ['timerange' => ['start' => null, 'end' => null]], 5, 20)
197
+            ->willReturn($search2);
198
+
199
+        $this->manager->registerCalendar($calendar1);
200
+        $this->manager->registerCalendar($calendar2);
201
+
202
+        $result = $this->manager->search('searchTerm', ['SUMMARY', 'DESCRIPTION'],
203
+            ['timerange' => ['start' => null, 'end' => null]], 5, 20);
204
+        $this->assertEquals($expected, $result);
205
+    }
206
+
207
+    public static function searchProvider(): array {
208
+        $search1 = [
209
+            [
210
+                'id' => 1,
211
+                'data' => 'foobar',
212
+            ],
213
+            [
214
+                'id' => 2,
215
+                'data' => 'barfoo',
216
+            ]
217
+        ];
218
+        $search2 = [
219
+            [
220
+                'id' => 3,
221
+                'data' => 'blablub',
222
+            ],
223
+            [
224
+                'id' => 4,
225
+                'data' => 'blubbla',
226
+            ]
227
+        ];
228
+
229
+        $expected = [
230
+            [
231
+                'id' => 1,
232
+                'data' => 'foobar',
233
+                'calendar-key' => 'simple:1',
234
+            ],
235
+            [
236
+                'id' => 2,
237
+                'data' => 'barfoo',
238
+                'calendar-key' => 'simple:1',
239
+            ],
240
+            [
241
+                'id' => 3,
242
+                'data' => 'blablub',
243
+                'calendar-key' => 'simple:2',
244
+            ],
245
+            [
246
+                'id' => 4,
247
+                'data' => 'blubbla',
248
+                'calendar-key' => 'simple:2',
249
+            ]
250
+        ];
251
+
252
+        return [
253
+            [
254
+                $search1,
255
+                $search2,
256
+                $expected
257
+            ]
258
+        ];
259
+    }
260
+
261
+    public function testRegisterUnregister(): void {
262
+        /** @var ICalendar | MockObject $calendar1 */
263
+        $calendar1 = $this->createMock(ICalendar::class);
264
+        $calendar1->method('getKey')->willReturn('key1');
265
+
266
+        /** @var ICalendar | MockObject $calendar2 */
267
+        $calendar2 = $this->createMock(ICalendar::class);
268
+        $calendar2->method('getKey')->willReturn('key2');
269
+
270
+        $this->manager->registerCalendar($calendar1);
271
+        $this->manager->registerCalendar($calendar2);
272
+
273
+        $result = $this->manager->getCalendars();
274
+        $this->assertCount(2, $result);
275
+        $this->assertContains($calendar1, $result);
276
+        $this->assertContains($calendar2, $result);
277
+
278
+        $this->manager->unregisterCalendar($calendar1);
279
+
280
+        $result = $this->manager->getCalendars();
281
+        $this->assertCount(1, $result);
282
+        $this->assertContains($calendar2, $result);
283
+    }
284
+
285
+    public function testGetCalendars(): void {
286
+        /** @var ICalendar | MockObject $calendar1 */
287
+        $calendar1 = $this->createMock(ICalendar::class);
288
+        $calendar1->method('getKey')->willReturn('key1');
289
+
290
+        /** @var ICalendar | MockObject $calendar2 */
291
+        $calendar2 = $this->createMock(ICalendar::class);
292
+        $calendar2->method('getKey')->willReturn('key2');
293
+
294
+        $this->manager->registerCalendar($calendar1);
295
+        $this->manager->registerCalendar($calendar2);
296
+
297
+        $result = $this->manager->getCalendars();
298
+        $this->assertCount(2, $result);
299
+        $this->assertContains($calendar1, $result);
300
+        $this->assertContains($calendar2, $result);
301
+
302
+        $this->manager->clear();
303
+
304
+        $result = $this->manager->getCalendars();
305
+
306
+        $this->assertCount(0, $result);
307
+    }
308
+
309
+    public function testEnabledIfNot(): void {
310
+        $isEnabled = $this->manager->isEnabled();
311
+        $this->assertFalse($isEnabled);
312
+    }
313
+
314
+    public function testIfEnabledIfSo(): void {
315
+        /** @var ICalendar | MockObject $calendar */
316
+        $calendar = $this->createMock(ICalendar::class);
317
+        $this->manager->registerCalendar($calendar);
318
+
319
+        $isEnabled = $this->manager->isEnabled();
320
+        $this->assertTrue($isEnabled);
321
+    }
322
+
323
+    public function testHandleImipWithNoCalendars(): void {
324
+        // construct calendar manager returns
325
+        /** @var Manager&MockObject $manager */
326
+        $manager = $this->getMockBuilder(Manager::class)
327
+            ->setConstructorArgs([
328
+                $this->coordinator,
329
+                $this->container,
330
+                $this->logger,
331
+                $this->time,
332
+                $this->secureRandom,
333
+                $this->userManager,
334
+                $this->serverFactory,
335
+                $this->propertyMapper,
336
+            ])
337
+            ->onlyMethods(['getCalendarsForPrincipal'])
338
+            ->getMock();
339
+        $manager->expects(self::once())
340
+            ->method('getCalendarsForPrincipal')
341
+            ->willReturn([]);
342
+        // construct logger returns
343
+        $this->logger->expects(self::once())->method('warning')
344
+            ->with('iMip message could not be processed because user has no calendars');
345
+        // construct parameters
346
+        $userId = 'attendee1';
347
+        $calendar = $this->vCalendar1a;
348
+        $calendar->add('METHOD', 'REQUEST');
349
+        // test method
350
+        $result = $manager->handleIMip($userId, $calendar->serialize());
351
+        // Assert
352
+        $this->assertFalse($result);
353
+    }
354
+
355
+    public function testHandleImipWithNoEvent(): void {
356
+        // construct mock user calendar
357
+        $userCalendar = $this->createMock(ITestCalendar::class);
358
+        // construct mock calendar manager and returns
359
+        /** @var Manager&MockObject $manager */
360
+        $manager = $this->getMockBuilder(Manager::class)
361
+            ->setConstructorArgs([
362
+                $this->coordinator,
363
+                $this->container,
364
+                $this->logger,
365
+                $this->time,
366
+                $this->secureRandom,
367
+                $this->userManager,
368
+                $this->serverFactory,
369
+                $this->propertyMapper,
370
+            ])
371
+            ->onlyMethods(['getCalendarsForPrincipal'])
372
+            ->getMock();
373
+        $manager->expects(self::once())
374
+            ->method('getCalendarsForPrincipal')
375
+            ->willReturn([$userCalendar]);
376
+        // construct logger returns
377
+        $this->logger->expects(self::once())->method('warning')
378
+            ->with('iMip message does not contain any event(s)');
379
+        // construct parameters
380
+        $userId = 'attendee1';
381
+        $calendar = $this->vCalendar1a;
382
+        $calendar->add('METHOD', 'REQUEST');
383
+        $calendar->remove('VEVENT');
384
+        // Act
385
+        $result = $manager->handleIMip($userId, $calendar->serialize());
386
+        // Assert
387
+        $this->assertFalse($result);
388
+    }
389
+
390
+    public function testHandleImipWithNoUid(): void {
391
+        // construct mock user calendar
392
+        $userCalendar = $this->createMock(ITestCalendar::class);
393
+        // construct mock calendar manager and returns
394
+        /** @var Manager&MockObject $manager */
395
+        $manager = $this->getMockBuilder(Manager::class)
396
+            ->setConstructorArgs([
397
+                $this->coordinator,
398
+                $this->container,
399
+                $this->logger,
400
+                $this->time,
401
+                $this->secureRandom,
402
+                $this->userManager,
403
+                $this->serverFactory,
404
+                $this->propertyMapper,
405
+            ])
406
+            ->onlyMethods(['getCalendarsForPrincipal'])
407
+            ->getMock();
408
+        $manager->expects(self::once())
409
+            ->method('getCalendarsForPrincipal')
410
+            ->willReturn([$userCalendar]);
411
+        // construct logger returns
412
+        $this->logger->expects(self::once())->method('warning')
413
+            ->with('iMip message event dose not contains a UID');
414
+        // construct parameters
415
+        $userId = 'attendee1';
416
+        $calendar = $this->vCalendar1a;
417
+        $calendar->add('METHOD', 'REQUEST');
418
+        $calendar->VEVENT->remove('UID');
419
+        // test method
420
+        $result = $manager->handleIMip($userId, $calendar->serialize());
421
+        // Assert
422
+        $this->assertFalse($result);
423
+    }
424
+
425
+    public function testHandleImipWithNoMatch(): void {
426
+        // construct mock user calendar
427
+        $userCalendar = $this->createMock(ITestCalendar::class);
428
+        $userCalendar->expects(self::once())
429
+            ->method('isDeleted')
430
+            ->willReturn(false);
431
+        $userCalendar->expects(self::once())
432
+            ->method('isWritable')
433
+            ->willReturn(true);
434
+        $userCalendar->expects(self::once())
435
+            ->method('search')
436
+            ->willReturn([]);
437
+        // construct mock calendar manager and returns
438
+        /** @var Manager&MockObject $manager */
439
+        $manager = $this->getMockBuilder(Manager::class)
440
+            ->setConstructorArgs([
441
+                $this->coordinator,
442
+                $this->container,
443
+                $this->logger,
444
+                $this->time,
445
+                $this->secureRandom,
446
+                $this->userManager,
447
+                $this->serverFactory,
448
+                $this->propertyMapper,
449
+            ])
450
+            ->onlyMethods(['getCalendarsForPrincipal'])
451
+            ->getMock();
452
+        $manager->expects(self::once())
453
+            ->method('getCalendarsForPrincipal')
454
+            ->willReturn([$userCalendar]);
455
+        // construct logger returns
456
+        $this->logger->expects(self::once())->method('warning')
457
+            ->with('iMip message could not be processed because no corresponding event was found in any calendar');
458
+        // construct parameters
459
+        $userId = 'attendee1';
460
+        $calendar = $this->vCalendar1a;
461
+        $calendar->add('METHOD', 'REQUEST');
462
+        // test method
463
+        $result = $manager->handleIMip($userId, $calendar->serialize());
464
+        // Assert
465
+        $this->assertFalse($result);
466
+    }
467
+
468
+    public function testHandleImip(): void {
469
+        // construct mock user calendar
470
+        $userCalendar = $this->createMock(ITestCalendar::class);
471
+        $userCalendar->expects(self::once())
472
+            ->method('isDeleted')
473
+            ->willReturn(false);
474
+        $userCalendar->expects(self::once())
475
+            ->method('isWritable')
476
+            ->willReturn(true);
477
+        $userCalendar->expects(self::once())
478
+            ->method('search')
479
+            ->willReturn([['uri' => 'principals/user/attendee1/personal']]);
480
+        // construct mock calendar manager and returns
481
+        /** @var Manager&MockObject $manager */
482
+        $manager = $this->getMockBuilder(Manager::class)
483
+            ->setConstructorArgs([
484
+                $this->coordinator,
485
+                $this->container,
486
+                $this->logger,
487
+                $this->time,
488
+                $this->secureRandom,
489
+                $this->userManager,
490
+                $this->serverFactory,
491
+                $this->propertyMapper,
492
+            ])
493
+            ->onlyMethods(['getCalendarsForPrincipal'])
494
+            ->getMock();
495
+        $manager->expects(self::once())
496
+            ->method('getCalendarsForPrincipal')
497
+            ->willReturn([$userCalendar]);
498
+        // construct parameters
499
+        $userId = 'attendee1';
500
+        $calendar = $this->vCalendar1a;
501
+        $calendar->add('METHOD', 'REQUEST');
502
+        // construct user calendar returns
503
+        $userCalendar->expects(self::once())
504
+            ->method('handleIMipMessage');
505
+        // test method
506
+        $result = $manager->handleIMip($userId, $calendar->serialize());
507
+    }
508
+
509
+    public function testhandleIMipRequestWithInvalidPrincipal() {
510
+        $invalidPrincipal = 'invalid-principal-uri';
511
+        $sender = '[email protected]';
512
+        $recipient = '[email protected]';
513
+        $calendarData = $this->vCalendar1a->serialize();
514
+
515
+        $this->logger->expects(self::once())
516
+            ->method('error')
517
+            ->with('Invalid principal URI provided for iMip request');
518
+
519
+        $result = $this->manager->handleIMipRequest($invalidPrincipal, $sender, $recipient, $calendarData);
520
+        $this->assertFalse($result);
521
+    }
522
+
523
+    public function testhandleIMipRequest() {
524
+        $principalUri = 'principals/users/attendee1';
525
+        $sender = '[email protected]';
526
+        $recipient = '[email protected]';
527
+        $calendarData = $this->vCalendar1a->serialize();
528
+
529
+        /** @var Manager&MockObject $manager */
530
+        $manager = $this->getMockBuilder(Manager::class)
531
+            ->setConstructorArgs([
532
+                $this->coordinator,
533
+                $this->container,
534
+                $this->logger,
535
+                $this->time,
536
+                $this->secureRandom,
537
+                $this->userManager,
538
+                $this->serverFactory,
539
+                $this->propertyMapper,
540
+            ])
541
+            ->onlyMethods(['handleIMip'])
542
+            ->getMock();
543
+        $manager->expects(self::once())
544
+            ->method('handleIMip')
545
+            ->with('attendee1', $calendarData)
546
+            ->willReturn(true);
547
+
548
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendarData);
549
+        $this->assertTrue($result);
550
+    }
551
+
552
+    public function testhandleIMipReplyWithInvalidPrincipal() {
553
+        $invalidPrincipal = 'invalid-principal-uri';
554
+        $sender = '[email protected]';
555
+        $recipient = '[email protected]';
556
+        $calendarData = $this->vCalendar2a->serialize();
557
+
558
+        $this->logger->expects(self::once())
559
+            ->method('error')
560
+            ->with('Invalid principal URI provided for iMip reply');
561
+
562
+        $result = $this->manager->handleIMipReply($invalidPrincipal, $sender, $recipient, $calendarData);
563
+        $this->assertFalse($result);
564
+    }
565
+
566
+    public function testhandleIMipReply() {
567
+        $principalUri = 'principals/users/attendee2';
568
+        $sender = '[email protected]';
569
+        $recipient = '[email protected]';
570
+        $calendarData = $this->vCalendar2a->serialize();
571
+
572
+        /** @var Manager&MockObject $manager */
573
+        $manager = $this->getMockBuilder(Manager::class)
574
+            ->setConstructorArgs([
575
+                $this->coordinator,
576
+                $this->container,
577
+                $this->logger,
578
+                $this->time,
579
+                $this->secureRandom,
580
+                $this->userManager,
581
+                $this->serverFactory,
582
+                $this->propertyMapper,
583
+            ])
584
+            ->onlyMethods(['handleIMip'])
585
+            ->getMock();
586
+        $manager->expects(self::once())
587
+            ->method('handleIMip')
588
+            ->with('attendee2', $calendarData)
589
+            ->willReturn(true);
590
+
591
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData);
592
+        $this->assertTrue($result);
593
+    }
594
+
595
+    public function testhandleIMipCancelWithInvalidPrincipal() {
596
+        $invalidPrincipal = 'invalid-principal-uri';
597
+        $sender = '[email protected]';
598
+        $replyTo = null;
599
+        $recipient = '[email protected]';
600
+        $calendarData = $this->vCalendar3a->serialize();
601
+
602
+        $this->logger->expects(self::once())
603
+            ->method('error')
604
+            ->with('Invalid principal URI provided for iMip cancel');
605
+
606
+        $result = $this->manager->handleIMipCancel($invalidPrincipal, $sender, $replyTo, $recipient, $calendarData);
607
+        $this->assertFalse($result);
608
+    }
609
+
610
+    public function testhandleIMipCancel() {
611
+        $principalUri = 'principals/users/attendee3';
612
+        $sender = '[email protected]';
613
+        $replyTo = null;
614
+        $recipient = '[email protected]';
615
+        $calendarData = $this->vCalendar3a->serialize();
616
+
617
+        /** @var Manager&MockObject $manager */
618
+        $manager = $this->getMockBuilder(Manager::class)
619
+            ->setConstructorArgs([
620
+                $this->coordinator,
621
+                $this->container,
622
+                $this->logger,
623
+                $this->time,
624
+                $this->secureRandom,
625
+                $this->userManager,
626
+                $this->serverFactory,
627
+                $this->propertyMapper,
628
+            ])
629
+            ->onlyMethods(['handleIMip'])
630
+            ->getMock();
631
+        $manager->expects(self::once())
632
+            ->method('handleIMip')
633
+            ->with('attendee3', $calendarData)
634
+            ->willReturn(true);
635
+
636
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData);
637
+        $this->assertTrue($result);
638
+    }
639
+
640
+    private function getFreeBusyResponse(): string {
641
+        return <<<EOF
642 642
 <?xml version="1.0" encoding="utf-8"?>
643 643
 <cal:schedule-response xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">
644 644
   <cal:response>
@@ -716,139 +716,139 @@  discard block
 block discarded – undo
716 716
   </cal:response>
717 717
 </cal:schedule-response>
718 718
 EOF;
719
-	}
720
-
721
-	public function testCheckAvailability(): void {
722
-		$organizer = $this->createMock(IUser::class);
723
-		$organizer->expects(self::once())
724
-			->method('getUID')
725
-			->willReturn('admin');
726
-		$organizer->expects(self::once())
727
-			->method('getEMailAddress')
728
-			->willReturn('[email protected]');
729
-
730
-		$user1 = $this->createMock(IUser::class);
731
-		$user2 = $this->createMock(IUser::class);
732
-
733
-		$this->userManager->expects(self::exactly(3))
734
-			->method('getByEmail')
735
-			->willReturnMap([
736
-				['[email protected]', [$user1]],
737
-				['[email protected]', [$user2]],
738
-				['[email protected]', []],
739
-			]);
740
-
741
-		$authPlugin = $this->createMock(CustomPrincipalPlugin::class);
742
-		$authPlugin->expects(self::once())
743
-			->method('setCurrentPrincipal')
744
-			->with('principals/users/admin');
745
-
746
-		$server = $this->createMock(Server::class);
747
-		$server->expects(self::once())
748
-			->method('getPlugin')
749
-			->with('auth')
750
-			->willReturn($authPlugin);
751
-		$server->expects(self::once())
752
-			->method('invokeMethod')
753
-			->willReturnCallback(function (
754
-				RequestInterface $request,
755
-				ResponseInterface $response,
756
-				bool $sendResponse,
757
-			): void {
758
-				$requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
759
-				$this->assertEquals('POST', $request->getMethod());
760
-				$this->assertEquals('calendars/admin/outbox', $request->getPath());
761
-				$this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
762
-				$this->assertEquals('0', $request->getHeader('Depth'));
763
-				$this->assertEquals($requestBody, $request->getBodyAsString());
764
-				$this->assertFalse($sendResponse);
765
-				$response->setStatus(200);
766
-				$response->setBody($this->getFreeBusyResponse());
767
-			});
768
-
769
-		$this->serverFactory->expects(self::once())
770
-			->method('createAttendeeAvailabilityServer')
771
-			->willReturn($server);
772
-
773
-		$start = new DateTimeImmutable('2025-01-16T06:00:00Z');
774
-		$end = new DateTimeImmutable('2025-01-17T06:00:00Z');
775
-		$actual = $this->manager->checkAvailability($start, $end, $organizer, [
776
-			'[email protected]',
777
-			'[email protected]',
778
-			'[email protected]',
779
-		]);
780
-		$expected = [
781
-			new AvailabilityResult('[email protected]', false),
782
-			new AvailabilityResult('[email protected]', true),
783
-			new AvailabilityResult('[email protected]', false),
784
-		];
785
-		$this->assertEquals($expected, $actual);
786
-	}
787
-
788
-	public function testCheckAvailabilityWithMailtoPrefix(): void {
789
-		$organizer = $this->createMock(IUser::class);
790
-		$organizer->expects(self::once())
791
-			->method('getUID')
792
-			->willReturn('admin');
793
-		$organizer->expects(self::once())
794
-			->method('getEMailAddress')
795
-			->willReturn('[email protected]');
796
-
797
-		$user1 = $this->createMock(IUser::class);
798
-		$user2 = $this->createMock(IUser::class);
799
-
800
-		$this->userManager->expects(self::exactly(3))
801
-			->method('getByEmail')
802
-			->willReturnMap([
803
-				['[email protected]', [$user1]],
804
-				['[email protected]', [$user2]],
805
-				['[email protected]', []],
806
-			]);
807
-
808
-		$authPlugin = $this->createMock(CustomPrincipalPlugin::class);
809
-		$authPlugin->expects(self::once())
810
-			->method('setCurrentPrincipal')
811
-			->with('principals/users/admin');
812
-
813
-		$server = $this->createMock(Server::class);
814
-		$server->expects(self::once())
815
-			->method('getPlugin')
816
-			->with('auth')
817
-			->willReturn($authPlugin);
818
-		$server->expects(self::once())
819
-			->method('invokeMethod')
820
-			->willReturnCallback(function (
821
-				RequestInterface $request,
822
-				ResponseInterface $response,
823
-				bool $sendResponse,
824
-			): void {
825
-				$requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
826
-				$this->assertEquals('POST', $request->getMethod());
827
-				$this->assertEquals('calendars/admin/outbox', $request->getPath());
828
-				$this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
829
-				$this->assertEquals('0', $request->getHeader('Depth'));
830
-				$this->assertEquals($requestBody, $request->getBodyAsString());
831
-				$this->assertFalse($sendResponse);
832
-				$response->setStatus(200);
833
-				$response->setBody($this->getFreeBusyResponse());
834
-			});
835
-
836
-		$this->serverFactory->expects(self::once())
837
-			->method('createAttendeeAvailabilityServer')
838
-			->willReturn($server);
839
-
840
-		$start = new DateTimeImmutable('2025-01-16T06:00:00Z');
841
-		$end = new DateTimeImmutable('2025-01-17T06:00:00Z');
842
-		$actual = $this->manager->checkAvailability($start, $end, $organizer, [
843
-			'mailto:[email protected]',
844
-			'mailto:[email protected]',
845
-			'mailto:[email protected]',
846
-		]);
847
-		$expected = [
848
-			new AvailabilityResult('[email protected]', false),
849
-			new AvailabilityResult('[email protected]', true),
850
-			new AvailabilityResult('[email protected]', false),
851
-		];
852
-		$this->assertEquals($expected, $actual);
853
-	}
719
+    }
720
+
721
+    public function testCheckAvailability(): void {
722
+        $organizer = $this->createMock(IUser::class);
723
+        $organizer->expects(self::once())
724
+            ->method('getUID')
725
+            ->willReturn('admin');
726
+        $organizer->expects(self::once())
727
+            ->method('getEMailAddress')
728
+            ->willReturn('[email protected]');
729
+
730
+        $user1 = $this->createMock(IUser::class);
731
+        $user2 = $this->createMock(IUser::class);
732
+
733
+        $this->userManager->expects(self::exactly(3))
734
+            ->method('getByEmail')
735
+            ->willReturnMap([
736
+                ['[email protected]', [$user1]],
737
+                ['[email protected]', [$user2]],
738
+                ['[email protected]', []],
739
+            ]);
740
+
741
+        $authPlugin = $this->createMock(CustomPrincipalPlugin::class);
742
+        $authPlugin->expects(self::once())
743
+            ->method('setCurrentPrincipal')
744
+            ->with('principals/users/admin');
745
+
746
+        $server = $this->createMock(Server::class);
747
+        $server->expects(self::once())
748
+            ->method('getPlugin')
749
+            ->with('auth')
750
+            ->willReturn($authPlugin);
751
+        $server->expects(self::once())
752
+            ->method('invokeMethod')
753
+            ->willReturnCallback(function (
754
+                RequestInterface $request,
755
+                ResponseInterface $response,
756
+                bool $sendResponse,
757
+            ): void {
758
+                $requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
759
+                $this->assertEquals('POST', $request->getMethod());
760
+                $this->assertEquals('calendars/admin/outbox', $request->getPath());
761
+                $this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
762
+                $this->assertEquals('0', $request->getHeader('Depth'));
763
+                $this->assertEquals($requestBody, $request->getBodyAsString());
764
+                $this->assertFalse($sendResponse);
765
+                $response->setStatus(200);
766
+                $response->setBody($this->getFreeBusyResponse());
767
+            });
768
+
769
+        $this->serverFactory->expects(self::once())
770
+            ->method('createAttendeeAvailabilityServer')
771
+            ->willReturn($server);
772
+
773
+        $start = new DateTimeImmutable('2025-01-16T06:00:00Z');
774
+        $end = new DateTimeImmutable('2025-01-17T06:00:00Z');
775
+        $actual = $this->manager->checkAvailability($start, $end, $organizer, [
776
+            '[email protected]',
777
+            '[email protected]',
778
+            '[email protected]',
779
+        ]);
780
+        $expected = [
781
+            new AvailabilityResult('[email protected]', false),
782
+            new AvailabilityResult('[email protected]', true),
783
+            new AvailabilityResult('[email protected]', false),
784
+        ];
785
+        $this->assertEquals($expected, $actual);
786
+    }
787
+
788
+    public function testCheckAvailabilityWithMailtoPrefix(): void {
789
+        $organizer = $this->createMock(IUser::class);
790
+        $organizer->expects(self::once())
791
+            ->method('getUID')
792
+            ->willReturn('admin');
793
+        $organizer->expects(self::once())
794
+            ->method('getEMailAddress')
795
+            ->willReturn('[email protected]');
796
+
797
+        $user1 = $this->createMock(IUser::class);
798
+        $user2 = $this->createMock(IUser::class);
799
+
800
+        $this->userManager->expects(self::exactly(3))
801
+            ->method('getByEmail')
802
+            ->willReturnMap([
803
+                ['[email protected]', [$user1]],
804
+                ['[email protected]', [$user2]],
805
+                ['[email protected]', []],
806
+            ]);
807
+
808
+        $authPlugin = $this->createMock(CustomPrincipalPlugin::class);
809
+        $authPlugin->expects(self::once())
810
+            ->method('setCurrentPrincipal')
811
+            ->with('principals/users/admin');
812
+
813
+        $server = $this->createMock(Server::class);
814
+        $server->expects(self::once())
815
+            ->method('getPlugin')
816
+            ->with('auth')
817
+            ->willReturn($authPlugin);
818
+        $server->expects(self::once())
819
+            ->method('invokeMethod')
820
+            ->willReturnCallback(function (
821
+                RequestInterface $request,
822
+                ResponseInterface $response,
823
+                bool $sendResponse,
824
+            ): void {
825
+                $requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
826
+                $this->assertEquals('POST', $request->getMethod());
827
+                $this->assertEquals('calendars/admin/outbox', $request->getPath());
828
+                $this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
829
+                $this->assertEquals('0', $request->getHeader('Depth'));
830
+                $this->assertEquals($requestBody, $request->getBodyAsString());
831
+                $this->assertFalse($sendResponse);
832
+                $response->setStatus(200);
833
+                $response->setBody($this->getFreeBusyResponse());
834
+            });
835
+
836
+        $this->serverFactory->expects(self::once())
837
+            ->method('createAttendeeAvailabilityServer')
838
+            ->willReturn($server);
839
+
840
+        $start = new DateTimeImmutable('2025-01-16T06:00:00Z');
841
+        $end = new DateTimeImmutable('2025-01-17T06:00:00Z');
842
+        $actual = $this->manager->checkAvailability($start, $end, $organizer, [
843
+            'mailto:[email protected]',
844
+            'mailto:[email protected]',
845
+            'mailto:[email protected]',
846
+        ]);
847
+        $expected = [
848
+            new AvailabilityResult('[email protected]', false),
849
+            new AvailabilityResult('[email protected]', true),
850
+            new AvailabilityResult('[email protected]', false),
851
+        ];
852
+        $this->assertEquals($expected, $actual);
853
+    }
854 854
 }
Please login to merge, or discard this patch.
lib/private/Calendar/Manager.php 2 patches
Indentation   +450 added lines, -450 removed lines patch added patch discarded remove patch
@@ -41,455 +41,455 @@
 block discarded – undo
41 41
 use function array_merge;
42 42
 
43 43
 class Manager implements IManager {
44
-	/**
45
-	 * @var ICalendar[] holds all registered calendars
46
-	 */
47
-	private array $calendars = [];
48
-
49
-	/**
50
-	 * @var \Closure[] to call to load/register calendar providers
51
-	 */
52
-	private array $calendarLoaders = [];
53
-
54
-	public function __construct(
55
-		private Coordinator $coordinator,
56
-		private ContainerInterface $container,
57
-		private LoggerInterface $logger,
58
-		private ITimeFactory $timeFactory,
59
-		private ISecureRandom $random,
60
-		private IUserManager $userManager,
61
-		private ServerFactory $serverFactory,
62
-		private PropertyMapper $propertyMapper,
63
-	) {
64
-	}
65
-
66
-	/**
67
-	 * This function is used to search and find objects within the user's calendars.
68
-	 * In case $pattern is empty all events/journals/todos will be returned.
69
-	 *
70
-	 * @param string $pattern which should match within the $searchProperties
71
-	 * @param array $searchProperties defines the properties within the query pattern should match
72
-	 * @param array $options - optional parameters:
73
-	 *                       ['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]]
74
-	 * @param integer|null $limit - limit number of search results
75
-	 * @param integer|null $offset - offset for paging of search results
76
-	 * @return array an array of events/journals/todos which are arrays of arrays of key-value-pairs
77
-	 * @since 13.0.0
78
-	 */
79
-	public function search(
80
-		$pattern,
81
-		array $searchProperties = [],
82
-		array $options = [],
83
-		$limit = null,
84
-		$offset = null,
85
-	): array {
86
-		$this->loadCalendars();
87
-		$result = [];
88
-		foreach ($this->calendars as $calendar) {
89
-			$r = $calendar->search($pattern, $searchProperties, $options, $limit, $offset);
90
-			foreach ($r as $o) {
91
-				$o['calendar-key'] = $calendar->getKey();
92
-				$result[] = $o;
93
-			}
94
-		}
95
-
96
-		return $result;
97
-	}
98
-
99
-	/**
100
-	 * Check if calendars are available
101
-	 *
102
-	 * @return bool true if enabled, false if not
103
-	 * @since 13.0.0
104
-	 */
105
-	public function isEnabled(): bool {
106
-		return !empty($this->calendars) || !empty($this->calendarLoaders);
107
-	}
108
-
109
-	/**
110
-	 * Registers a calendar
111
-	 *
112
-	 * @since 13.0.0
113
-	 */
114
-	public function registerCalendar(ICalendar $calendar): void {
115
-		$this->calendars[$calendar->getKey()] = $calendar;
116
-	}
117
-
118
-	/**
119
-	 * Unregisters a calendar
120
-	 *
121
-	 * @since 13.0.0
122
-	 */
123
-	public function unregisterCalendar(ICalendar $calendar): void {
124
-		unset($this->calendars[$calendar->getKey()]);
125
-	}
126
-
127
-	/**
128
-	 * In order to improve lazy loading a closure can be registered which will be called in case
129
-	 * calendars are actually requested
130
-	 *
131
-	 * @since 13.0.0
132
-	 */
133
-	public function register(\Closure $callable): void {
134
-		$this->calendarLoaders[] = $callable;
135
-	}
136
-
137
-	/**
138
-	 * @return ICalendar[]
139
-	 *
140
-	 * @since 13.0.0
141
-	 */
142
-	public function getCalendars(): array {
143
-		$this->loadCalendars();
144
-
145
-		return array_values($this->calendars);
146
-	}
147
-
148
-	/**
149
-	 * removes all registered calendar instances
150
-	 *
151
-	 * @since 13.0.0
152
-	 */
153
-	public function clear(): void {
154
-		$this->calendars = [];
155
-		$this->calendarLoaders = [];
156
-	}
157
-
158
-	/**
159
-	 * loads all calendars
160
-	 */
161
-	private function loadCalendars(): void {
162
-		foreach ($this->calendarLoaders as $callable) {
163
-			$callable($this);
164
-		}
165
-		$this->calendarLoaders = [];
166
-	}
167
-
168
-	/**
169
-	 * @return ICreateFromString[]
170
-	 */
171
-	public function getCalendarsForPrincipal(string $principalUri, array $calendarUris = []): array {
172
-		$context = $this->coordinator->getRegistrationContext();
173
-		if ($context === null) {
174
-			return [];
175
-		}
176
-
177
-		return array_merge(
178
-			...array_map(function ($registration) use ($principalUri, $calendarUris) {
179
-				try {
180
-					/** @var ICalendarProvider $provider */
181
-					$provider = $this->container->get($registration->getService());
182
-				} catch (Throwable $e) {
183
-					$this->logger->error('Could not load calendar provider ' . $registration->getService() . ': ' . $e->getMessage(), [
184
-						'exception' => $e,
185
-					]);
186
-					return [];
187
-				}
188
-
189
-				return $provider->getCalendars($principalUri, $calendarUris);
190
-			}, $context->getCalendarProviders())
191
-		);
192
-	}
193
-
194
-	public function searchForPrincipal(ICalendarQuery $query): array {
195
-		/** @var CalendarQuery $query */
196
-		$calendars = $this->getCalendarsForPrincipal(
197
-			$query->getPrincipalUri(),
198
-			$query->getCalendarUris(),
199
-		);
200
-
201
-		$results = [];
202
-		foreach ($calendars as $calendar) {
203
-			$r = $calendar->search(
204
-				$query->getSearchPattern() ?? '',
205
-				$query->getSearchProperties(),
206
-				$query->getOptions(),
207
-				$query->getLimit(),
208
-				$query->getOffset()
209
-			);
210
-
211
-			foreach ($r as $o) {
212
-				$o['calendar-key'] = $calendar->getKey();
213
-				$o['calendar-uri'] = $calendar->getUri();
214
-				$results[] = $o;
215
-			}
216
-		}
217
-		return $results;
218
-	}
219
-
220
-	public function newQuery(string $principalUri): ICalendarQuery {
221
-		return new CalendarQuery($principalUri);
222
-	}
223
-
224
-	/**
225
-	 * @since 32.0.0
226
-	 *
227
-	 * @throws \OCP\DB\Exception
228
-	 */
229
-	public function handleIMip(
230
-		string $userId,
231
-		string $message,
232
-		array $options = [],
233
-	): bool {
234
-
235
-		$userUri = 'principals/users/' . $userId;
236
-
237
-		$userCalendars = $this->getCalendarsForPrincipal($userUri);
238
-		if (empty($userCalendars)) {
239
-			$this->logger->warning('iMip message could not be processed because user has no calendars');
240
-			return false;
241
-		}
242
-
243
-		try {
244
-			/** @var VCalendar $vObject|null */
245
-			$vObject = Reader::read($message);
246
-		} catch (ParseException $e) {
247
-			$this->logger->error('iMip message could not be processed because an error occurred while parsing the iMip message', ['exception' => $e]);
248
-			return false;
249
-		}
250
-
251
-		if (!isset($vObject->VEVENT)) {
252
-			$this->logger->warning('iMip message does not contain any event(s)');
253
-			return false;
254
-		}
255
-		/** @var VEvent $vEvent */
256
-		$vEvent = $vObject->VEVENT;
257
-
258
-		if (!isset($vEvent->UID)) {
259
-			$this->logger->warning('iMip message event dose not contains a UID');
260
-			return false;
261
-		}
262
-
263
-		if (!isset($vEvent->ORGANIZER)) {
264
-			$this->logger->warning('iMip message event dose not contains an organizer');
265
-			return false;
266
-		}
267
-
268
-		if (!isset($vEvent->ATTENDEE)) {
269
-			$this->logger->warning('iMip message event dose not contains any attendees');
270
-			return false;
271
-		}
272
-
273
-		foreach ($userCalendars as $calendar) {
274
-			if (!$calendar instanceof ICalendarIsWritable) {
275
-				continue;
276
-			}
277
-			if ($calendar->isDeleted() || !$calendar->isWritable()) {
278
-				continue;
279
-			}
280
-			if (!empty($calendar->search('', [], ['uid' => $vEvent->UID->getValue()]))) {
281
-				try {
282
-					if ($calendar instanceof IHandleImipMessage) {
283
-						$calendar->handleIMipMessage($userId, $vObject->serialize());
284
-					}
285
-					return true;
286
-				} catch (CalendarException $e) {
287
-					$this->logger->error('iMip message could not be processed because an error occurred', ['exception' => $e]);
288
-					return false;
289
-				}
290
-			}
291
-		}
292
-
293
-		if ($options['absent'] === 'create') {
294
-			// retrieve the primary calendar for the user
295
-			$calendar = $this->getPrimaryCalendar($userId);
296
-			if ($calendar !== null && (
297
-				!$calendar instanceof IHandleImipMessage || !$calendar instanceof ICalendarIsWritable || $calendar->isDeleted() || !$calendar->isWritable()
298
-			)) {
299
-				$calendar = null;
300
-			}
301
-			// if no primary calendar is set, use the first writable calendar
302
-			if ($calendar === null) {
303
-				foreach ($userCalendars as $userCalendar) {
304
-					if ($userCalendar instanceof IHandleImipMessage && $userCalendar instanceof ICalendarIsWritable && !$userCalendar->isDeleted() && $userCalendar->isWritable()) {
305
-						$calendar = $userCalendar;
306
-						break;
307
-					}
308
-				}
309
-			}
310
-			if ($calendar === null) {
311
-				$this->logger->warning('iMip message could not be processed because no writable calendar was found');
312
-				return false;
313
-			}
314
-			$calendar->handleIMipMessage($userId, $vObject->serialize());
315
-		}
316
-
317
-		$this->logger->warning('iMip message could not be processed because no corresponding event was found in any calendar');
318
-
319
-		return false;
320
-	}
321
-
322
-	/**
323
-	 * @since 31.0.0
324
-	 *
325
-	 * @throws \OCP\DB\Exception
326
-	 */
327
-	public function handleIMipRequest(
328
-		string $principalUri,
329
-		string $sender,
330
-		string $recipient,
331
-		string $calendarData,
332
-	): bool {
333
-		if (empty($principalUri) || !str_starts_with($principalUri, 'principals/users/')) {
334
-			$this->logger->error('Invalid principal URI provided for iMip request');
335
-			return false;
336
-		}
337
-		$userId = substr($principalUri, 17);
338
-		return $this->handleIMip($userId, $calendarData);
339
-	}
340
-
341
-	/**
342
-	 * @since 25.0.0
343
-	 *
344
-	 * @throws \OCP\DB\Exception
345
-	 */
346
-	public function handleIMipReply(
347
-		string $principalUri,
348
-		string $sender,
349
-		string $recipient,
350
-		string $calendarData,
351
-	): bool {
352
-		if (empty($principalUri) || !str_starts_with($principalUri, 'principals/users/')) {
353
-			$this->logger->error('Invalid principal URI provided for iMip reply');
354
-			return false;
355
-		}
356
-		$userId = substr($principalUri, 17);
357
-		return $this->handleIMip($userId, $calendarData);
358
-	}
359
-
360
-	/**
361
-	 * @since 25.0.0
362
-	 *
363
-	 * @throws \OCP\DB\Exception
364
-	 */
365
-	public function handleIMipCancel(
366
-		string $principalUri,
367
-		string $sender,
368
-		?string $replyTo,
369
-		string $recipient,
370
-		string $calendarData,
371
-	): bool {
372
-		if (empty($principalUri) || !str_starts_with($principalUri, 'principals/users/')) {
373
-			$this->logger->error('Invalid principal URI provided for iMip cancel');
374
-			return false;
375
-		}
376
-		$userId = substr($principalUri, 17);
377
-		return $this->handleIMip($userId, $calendarData);
378
-	}
379
-
380
-	public function createEventBuilder(): ICalendarEventBuilder {
381
-		$uid = $this->random->generate(32, ISecureRandom::CHAR_ALPHANUMERIC);
382
-		return new CalendarEventBuilder($uid, $this->timeFactory);
383
-	}
384
-
385
-	public function checkAvailability(
386
-		DateTimeInterface $start,
387
-		DateTimeInterface $end,
388
-		IUser $organizer,
389
-		array $attendees,
390
-	): array {
391
-		$organizerMailto = 'mailto:' . $organizer->getEMailAddress();
392
-		$request = new VCalendar();
393
-		$request->METHOD = 'REQUEST';
394
-		$request->add('VFREEBUSY', [
395
-			'DTSTART' => $start,
396
-			'DTEND' => $end,
397
-			'ORGANIZER' => $organizerMailto,
398
-			'ATTENDEE' => $organizerMailto,
399
-		]);
400
-
401
-		$mailtoLen = strlen('mailto:');
402
-		foreach ($attendees as $attendee) {
403
-			if (str_starts_with($attendee, 'mailto:')) {
404
-				$attendee = substr($attendee, $mailtoLen);
405
-			}
406
-
407
-			$attendeeUsers = $this->userManager->getByEmail($attendee);
408
-			if ($attendeeUsers === []) {
409
-				continue;
410
-			}
411
-
412
-			$request->VFREEBUSY->add('ATTENDEE', "mailto:$attendee");
413
-		}
414
-
415
-		$organizerUid = $organizer->getUID();
416
-		$server = $this->serverFactory->createAttendeeAvailabilityServer();
417
-		/** @var CustomPrincipalPlugin $plugin */
418
-		$plugin = $server->getPlugin('auth');
419
-		$plugin->setCurrentPrincipal("principals/users/$organizerUid");
420
-
421
-		$request = new Request(
422
-			'POST',
423
-			"/calendars/$organizerUid/outbox/",
424
-			[
425
-				'Content-Type' => 'text/calendar',
426
-				'Depth' => 0,
427
-			],
428
-			$request->serialize(),
429
-		);
430
-		$response = new Response();
431
-		$server->invokeMethod($request, $response, false);
432
-
433
-		$xmlService = new \Sabre\Xml\Service();
434
-		$xmlService->elementMap = [
435
-			'{urn:ietf:params:xml:ns:caldav}response' => 'Sabre\Xml\Deserializer\keyValue',
436
-			'{urn:ietf:params:xml:ns:caldav}recipient' => 'Sabre\Xml\Deserializer\keyValue',
437
-		];
438
-		$parsedResponse = $xmlService->parse($response->getBodyAsString());
439
-
440
-		$result = [];
441
-		foreach ($parsedResponse as $freeBusyResponse) {
442
-			$freeBusyResponse = $freeBusyResponse['value'];
443
-			if ($freeBusyResponse['{urn:ietf:params:xml:ns:caldav}request-status'] !== '2.0;Success') {
444
-				continue;
445
-			}
446
-
447
-			$freeBusyResponseData = \Sabre\VObject\Reader::read(
448
-				$freeBusyResponse['{urn:ietf:params:xml:ns:caldav}calendar-data']
449
-			);
450
-
451
-			$attendee = substr(
452
-				$freeBusyResponse['{urn:ietf:params:xml:ns:caldav}recipient']['{DAV:}href'],
453
-				$mailtoLen,
454
-			);
455
-
456
-			$vFreeBusy = $freeBusyResponseData->VFREEBUSY;
457
-			if (!($vFreeBusy instanceof VFreeBusy)) {
458
-				continue;
459
-			}
460
-
461
-			// TODO: actually check values of FREEBUSY properties to find a free slot
462
-			$result[] = new AvailabilityResult($attendee, $vFreeBusy->isFree($start, $end));
463
-		}
464
-
465
-		return $result;
466
-	}
467
-
468
-	public function getPrimaryCalendar(string $userId): ?ICalendar {
469
-		// determine if the principal has a default calendar configured
470
-		$properties = $this->propertyMapper->findPropertyByPathAndName(
471
-			$userId,
472
-			'principals/users/' . $userId,
473
-			'{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL'
474
-		);
475
-		if ($properties === []) {
476
-			return null;
477
-		}
478
-		// extract the calendar URI from the property value
479
-		$propertyValue = $properties[0]->getPropertyvalue() ?? null;
480
-		if (str_starts_with($propertyValue, 'calendars/' . $userId)) {
481
-			$calendarUri = rtrim(str_replace('calendars/' . $userId . '/', '', $propertyValue), '/');
482
-		}
483
-		if (empty($calendarUri)) {
484
-			return null;
485
-		}
486
-		// retrieve the calendar by URI
487
-		$calendars = $this->getCalendarsForPrincipal('principals/users/' . $userId, [$calendarUri]);
488
-		if ($calendars === []) {
489
-			return null;
490
-		}
491
-
492
-		return $calendars[0];
493
-	}
44
+    /**
45
+     * @var ICalendar[] holds all registered calendars
46
+     */
47
+    private array $calendars = [];
48
+
49
+    /**
50
+     * @var \Closure[] to call to load/register calendar providers
51
+     */
52
+    private array $calendarLoaders = [];
53
+
54
+    public function __construct(
55
+        private Coordinator $coordinator,
56
+        private ContainerInterface $container,
57
+        private LoggerInterface $logger,
58
+        private ITimeFactory $timeFactory,
59
+        private ISecureRandom $random,
60
+        private IUserManager $userManager,
61
+        private ServerFactory $serverFactory,
62
+        private PropertyMapper $propertyMapper,
63
+    ) {
64
+    }
65
+
66
+    /**
67
+     * This function is used to search and find objects within the user's calendars.
68
+     * In case $pattern is empty all events/journals/todos will be returned.
69
+     *
70
+     * @param string $pattern which should match within the $searchProperties
71
+     * @param array $searchProperties defines the properties within the query pattern should match
72
+     * @param array $options - optional parameters:
73
+     *                       ['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]]
74
+     * @param integer|null $limit - limit number of search results
75
+     * @param integer|null $offset - offset for paging of search results
76
+     * @return array an array of events/journals/todos which are arrays of arrays of key-value-pairs
77
+     * @since 13.0.0
78
+     */
79
+    public function search(
80
+        $pattern,
81
+        array $searchProperties = [],
82
+        array $options = [],
83
+        $limit = null,
84
+        $offset = null,
85
+    ): array {
86
+        $this->loadCalendars();
87
+        $result = [];
88
+        foreach ($this->calendars as $calendar) {
89
+            $r = $calendar->search($pattern, $searchProperties, $options, $limit, $offset);
90
+            foreach ($r as $o) {
91
+                $o['calendar-key'] = $calendar->getKey();
92
+                $result[] = $o;
93
+            }
94
+        }
95
+
96
+        return $result;
97
+    }
98
+
99
+    /**
100
+     * Check if calendars are available
101
+     *
102
+     * @return bool true if enabled, false if not
103
+     * @since 13.0.0
104
+     */
105
+    public function isEnabled(): bool {
106
+        return !empty($this->calendars) || !empty($this->calendarLoaders);
107
+    }
108
+
109
+    /**
110
+     * Registers a calendar
111
+     *
112
+     * @since 13.0.0
113
+     */
114
+    public function registerCalendar(ICalendar $calendar): void {
115
+        $this->calendars[$calendar->getKey()] = $calendar;
116
+    }
117
+
118
+    /**
119
+     * Unregisters a calendar
120
+     *
121
+     * @since 13.0.0
122
+     */
123
+    public function unregisterCalendar(ICalendar $calendar): void {
124
+        unset($this->calendars[$calendar->getKey()]);
125
+    }
126
+
127
+    /**
128
+     * In order to improve lazy loading a closure can be registered which will be called in case
129
+     * calendars are actually requested
130
+     *
131
+     * @since 13.0.0
132
+     */
133
+    public function register(\Closure $callable): void {
134
+        $this->calendarLoaders[] = $callable;
135
+    }
136
+
137
+    /**
138
+     * @return ICalendar[]
139
+     *
140
+     * @since 13.0.0
141
+     */
142
+    public function getCalendars(): array {
143
+        $this->loadCalendars();
144
+
145
+        return array_values($this->calendars);
146
+    }
147
+
148
+    /**
149
+     * removes all registered calendar instances
150
+     *
151
+     * @since 13.0.0
152
+     */
153
+    public function clear(): void {
154
+        $this->calendars = [];
155
+        $this->calendarLoaders = [];
156
+    }
157
+
158
+    /**
159
+     * loads all calendars
160
+     */
161
+    private function loadCalendars(): void {
162
+        foreach ($this->calendarLoaders as $callable) {
163
+            $callable($this);
164
+        }
165
+        $this->calendarLoaders = [];
166
+    }
167
+
168
+    /**
169
+     * @return ICreateFromString[]
170
+     */
171
+    public function getCalendarsForPrincipal(string $principalUri, array $calendarUris = []): array {
172
+        $context = $this->coordinator->getRegistrationContext();
173
+        if ($context === null) {
174
+            return [];
175
+        }
176
+
177
+        return array_merge(
178
+            ...array_map(function ($registration) use ($principalUri, $calendarUris) {
179
+                try {
180
+                    /** @var ICalendarProvider $provider */
181
+                    $provider = $this->container->get($registration->getService());
182
+                } catch (Throwable $e) {
183
+                    $this->logger->error('Could not load calendar provider ' . $registration->getService() . ': ' . $e->getMessage(), [
184
+                        'exception' => $e,
185
+                    ]);
186
+                    return [];
187
+                }
188
+
189
+                return $provider->getCalendars($principalUri, $calendarUris);
190
+            }, $context->getCalendarProviders())
191
+        );
192
+    }
193
+
194
+    public function searchForPrincipal(ICalendarQuery $query): array {
195
+        /** @var CalendarQuery $query */
196
+        $calendars = $this->getCalendarsForPrincipal(
197
+            $query->getPrincipalUri(),
198
+            $query->getCalendarUris(),
199
+        );
200
+
201
+        $results = [];
202
+        foreach ($calendars as $calendar) {
203
+            $r = $calendar->search(
204
+                $query->getSearchPattern() ?? '',
205
+                $query->getSearchProperties(),
206
+                $query->getOptions(),
207
+                $query->getLimit(),
208
+                $query->getOffset()
209
+            );
210
+
211
+            foreach ($r as $o) {
212
+                $o['calendar-key'] = $calendar->getKey();
213
+                $o['calendar-uri'] = $calendar->getUri();
214
+                $results[] = $o;
215
+            }
216
+        }
217
+        return $results;
218
+    }
219
+
220
+    public function newQuery(string $principalUri): ICalendarQuery {
221
+        return new CalendarQuery($principalUri);
222
+    }
223
+
224
+    /**
225
+     * @since 32.0.0
226
+     *
227
+     * @throws \OCP\DB\Exception
228
+     */
229
+    public function handleIMip(
230
+        string $userId,
231
+        string $message,
232
+        array $options = [],
233
+    ): bool {
234
+
235
+        $userUri = 'principals/users/' . $userId;
236
+
237
+        $userCalendars = $this->getCalendarsForPrincipal($userUri);
238
+        if (empty($userCalendars)) {
239
+            $this->logger->warning('iMip message could not be processed because user has no calendars');
240
+            return false;
241
+        }
242
+
243
+        try {
244
+            /** @var VCalendar $vObject|null */
245
+            $vObject = Reader::read($message);
246
+        } catch (ParseException $e) {
247
+            $this->logger->error('iMip message could not be processed because an error occurred while parsing the iMip message', ['exception' => $e]);
248
+            return false;
249
+        }
250
+
251
+        if (!isset($vObject->VEVENT)) {
252
+            $this->logger->warning('iMip message does not contain any event(s)');
253
+            return false;
254
+        }
255
+        /** @var VEvent $vEvent */
256
+        $vEvent = $vObject->VEVENT;
257
+
258
+        if (!isset($vEvent->UID)) {
259
+            $this->logger->warning('iMip message event dose not contains a UID');
260
+            return false;
261
+        }
262
+
263
+        if (!isset($vEvent->ORGANIZER)) {
264
+            $this->logger->warning('iMip message event dose not contains an organizer');
265
+            return false;
266
+        }
267
+
268
+        if (!isset($vEvent->ATTENDEE)) {
269
+            $this->logger->warning('iMip message event dose not contains any attendees');
270
+            return false;
271
+        }
272
+
273
+        foreach ($userCalendars as $calendar) {
274
+            if (!$calendar instanceof ICalendarIsWritable) {
275
+                continue;
276
+            }
277
+            if ($calendar->isDeleted() || !$calendar->isWritable()) {
278
+                continue;
279
+            }
280
+            if (!empty($calendar->search('', [], ['uid' => $vEvent->UID->getValue()]))) {
281
+                try {
282
+                    if ($calendar instanceof IHandleImipMessage) {
283
+                        $calendar->handleIMipMessage($userId, $vObject->serialize());
284
+                    }
285
+                    return true;
286
+                } catch (CalendarException $e) {
287
+                    $this->logger->error('iMip message could not be processed because an error occurred', ['exception' => $e]);
288
+                    return false;
289
+                }
290
+            }
291
+        }
292
+
293
+        if ($options['absent'] === 'create') {
294
+            // retrieve the primary calendar for the user
295
+            $calendar = $this->getPrimaryCalendar($userId);
296
+            if ($calendar !== null && (
297
+                !$calendar instanceof IHandleImipMessage || !$calendar instanceof ICalendarIsWritable || $calendar->isDeleted() || !$calendar->isWritable()
298
+            )) {
299
+                $calendar = null;
300
+            }
301
+            // if no primary calendar is set, use the first writable calendar
302
+            if ($calendar === null) {
303
+                foreach ($userCalendars as $userCalendar) {
304
+                    if ($userCalendar instanceof IHandleImipMessage && $userCalendar instanceof ICalendarIsWritable && !$userCalendar->isDeleted() && $userCalendar->isWritable()) {
305
+                        $calendar = $userCalendar;
306
+                        break;
307
+                    }
308
+                }
309
+            }
310
+            if ($calendar === null) {
311
+                $this->logger->warning('iMip message could not be processed because no writable calendar was found');
312
+                return false;
313
+            }
314
+            $calendar->handleIMipMessage($userId, $vObject->serialize());
315
+        }
316
+
317
+        $this->logger->warning('iMip message could not be processed because no corresponding event was found in any calendar');
318
+
319
+        return false;
320
+    }
321
+
322
+    /**
323
+     * @since 31.0.0
324
+     *
325
+     * @throws \OCP\DB\Exception
326
+     */
327
+    public function handleIMipRequest(
328
+        string $principalUri,
329
+        string $sender,
330
+        string $recipient,
331
+        string $calendarData,
332
+    ): bool {
333
+        if (empty($principalUri) || !str_starts_with($principalUri, 'principals/users/')) {
334
+            $this->logger->error('Invalid principal URI provided for iMip request');
335
+            return false;
336
+        }
337
+        $userId = substr($principalUri, 17);
338
+        return $this->handleIMip($userId, $calendarData);
339
+    }
340
+
341
+    /**
342
+     * @since 25.0.0
343
+     *
344
+     * @throws \OCP\DB\Exception
345
+     */
346
+    public function handleIMipReply(
347
+        string $principalUri,
348
+        string $sender,
349
+        string $recipient,
350
+        string $calendarData,
351
+    ): bool {
352
+        if (empty($principalUri) || !str_starts_with($principalUri, 'principals/users/')) {
353
+            $this->logger->error('Invalid principal URI provided for iMip reply');
354
+            return false;
355
+        }
356
+        $userId = substr($principalUri, 17);
357
+        return $this->handleIMip($userId, $calendarData);
358
+    }
359
+
360
+    /**
361
+     * @since 25.0.0
362
+     *
363
+     * @throws \OCP\DB\Exception
364
+     */
365
+    public function handleIMipCancel(
366
+        string $principalUri,
367
+        string $sender,
368
+        ?string $replyTo,
369
+        string $recipient,
370
+        string $calendarData,
371
+    ): bool {
372
+        if (empty($principalUri) || !str_starts_with($principalUri, 'principals/users/')) {
373
+            $this->logger->error('Invalid principal URI provided for iMip cancel');
374
+            return false;
375
+        }
376
+        $userId = substr($principalUri, 17);
377
+        return $this->handleIMip($userId, $calendarData);
378
+    }
379
+
380
+    public function createEventBuilder(): ICalendarEventBuilder {
381
+        $uid = $this->random->generate(32, ISecureRandom::CHAR_ALPHANUMERIC);
382
+        return new CalendarEventBuilder($uid, $this->timeFactory);
383
+    }
384
+
385
+    public function checkAvailability(
386
+        DateTimeInterface $start,
387
+        DateTimeInterface $end,
388
+        IUser $organizer,
389
+        array $attendees,
390
+    ): array {
391
+        $organizerMailto = 'mailto:' . $organizer->getEMailAddress();
392
+        $request = new VCalendar();
393
+        $request->METHOD = 'REQUEST';
394
+        $request->add('VFREEBUSY', [
395
+            'DTSTART' => $start,
396
+            'DTEND' => $end,
397
+            'ORGANIZER' => $organizerMailto,
398
+            'ATTENDEE' => $organizerMailto,
399
+        ]);
400
+
401
+        $mailtoLen = strlen('mailto:');
402
+        foreach ($attendees as $attendee) {
403
+            if (str_starts_with($attendee, 'mailto:')) {
404
+                $attendee = substr($attendee, $mailtoLen);
405
+            }
406
+
407
+            $attendeeUsers = $this->userManager->getByEmail($attendee);
408
+            if ($attendeeUsers === []) {
409
+                continue;
410
+            }
411
+
412
+            $request->VFREEBUSY->add('ATTENDEE', "mailto:$attendee");
413
+        }
414
+
415
+        $organizerUid = $organizer->getUID();
416
+        $server = $this->serverFactory->createAttendeeAvailabilityServer();
417
+        /** @var CustomPrincipalPlugin $plugin */
418
+        $plugin = $server->getPlugin('auth');
419
+        $plugin->setCurrentPrincipal("principals/users/$organizerUid");
420
+
421
+        $request = new Request(
422
+            'POST',
423
+            "/calendars/$organizerUid/outbox/",
424
+            [
425
+                'Content-Type' => 'text/calendar',
426
+                'Depth' => 0,
427
+            ],
428
+            $request->serialize(),
429
+        );
430
+        $response = new Response();
431
+        $server->invokeMethod($request, $response, false);
432
+
433
+        $xmlService = new \Sabre\Xml\Service();
434
+        $xmlService->elementMap = [
435
+            '{urn:ietf:params:xml:ns:caldav}response' => 'Sabre\Xml\Deserializer\keyValue',
436
+            '{urn:ietf:params:xml:ns:caldav}recipient' => 'Sabre\Xml\Deserializer\keyValue',
437
+        ];
438
+        $parsedResponse = $xmlService->parse($response->getBodyAsString());
439
+
440
+        $result = [];
441
+        foreach ($parsedResponse as $freeBusyResponse) {
442
+            $freeBusyResponse = $freeBusyResponse['value'];
443
+            if ($freeBusyResponse['{urn:ietf:params:xml:ns:caldav}request-status'] !== '2.0;Success') {
444
+                continue;
445
+            }
446
+
447
+            $freeBusyResponseData = \Sabre\VObject\Reader::read(
448
+                $freeBusyResponse['{urn:ietf:params:xml:ns:caldav}calendar-data']
449
+            );
450
+
451
+            $attendee = substr(
452
+                $freeBusyResponse['{urn:ietf:params:xml:ns:caldav}recipient']['{DAV:}href'],
453
+                $mailtoLen,
454
+            );
455
+
456
+            $vFreeBusy = $freeBusyResponseData->VFREEBUSY;
457
+            if (!($vFreeBusy instanceof VFreeBusy)) {
458
+                continue;
459
+            }
460
+
461
+            // TODO: actually check values of FREEBUSY properties to find a free slot
462
+            $result[] = new AvailabilityResult($attendee, $vFreeBusy->isFree($start, $end));
463
+        }
464
+
465
+        return $result;
466
+    }
467
+
468
+    public function getPrimaryCalendar(string $userId): ?ICalendar {
469
+        // determine if the principal has a default calendar configured
470
+        $properties = $this->propertyMapper->findPropertyByPathAndName(
471
+            $userId,
472
+            'principals/users/' . $userId,
473
+            '{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL'
474
+        );
475
+        if ($properties === []) {
476
+            return null;
477
+        }
478
+        // extract the calendar URI from the property value
479
+        $propertyValue = $properties[0]->getPropertyvalue() ?? null;
480
+        if (str_starts_with($propertyValue, 'calendars/' . $userId)) {
481
+            $calendarUri = rtrim(str_replace('calendars/' . $userId . '/', '', $propertyValue), '/');
482
+        }
483
+        if (empty($calendarUri)) {
484
+            return null;
485
+        }
486
+        // retrieve the calendar by URI
487
+        $calendars = $this->getCalendarsForPrincipal('principals/users/' . $userId, [$calendarUri]);
488
+        if ($calendars === []) {
489
+            return null;
490
+        }
491
+
492
+        return $calendars[0];
493
+    }
494 494
 
495 495
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -175,12 +175,12 @@  discard block
 block discarded – undo
175 175
 		}
176 176
 
177 177
 		return array_merge(
178
-			...array_map(function ($registration) use ($principalUri, $calendarUris) {
178
+			...array_map(function($registration) use ($principalUri, $calendarUris) {
179 179
 				try {
180 180
 					/** @var ICalendarProvider $provider */
181 181
 					$provider = $this->container->get($registration->getService());
182 182
 				} catch (Throwable $e) {
183
-					$this->logger->error('Could not load calendar provider ' . $registration->getService() . ': ' . $e->getMessage(), [
183
+					$this->logger->error('Could not load calendar provider '.$registration->getService().': '.$e->getMessage(), [
184 184
 						'exception' => $e,
185 185
 					]);
186 186
 					return [];
@@ -232,7 +232,7 @@  discard block
 block discarded – undo
232 232
 		array $options = [],
233 233
 	): bool {
234 234
 
235
-		$userUri = 'principals/users/' . $userId;
235
+		$userUri = 'principals/users/'.$userId;
236 236
 
237 237
 		$userCalendars = $this->getCalendarsForPrincipal($userUri);
238 238
 		if (empty($userCalendars)) {
@@ -388,7 +388,7 @@  discard block
 block discarded – undo
388 388
 		IUser $organizer,
389 389
 		array $attendees,
390 390
 	): array {
391
-		$organizerMailto = 'mailto:' . $organizer->getEMailAddress();
391
+		$organizerMailto = 'mailto:'.$organizer->getEMailAddress();
392 392
 		$request = new VCalendar();
393 393
 		$request->METHOD = 'REQUEST';
394 394
 		$request->add('VFREEBUSY', [
@@ -469,7 +469,7 @@  discard block
 block discarded – undo
469 469
 		// determine if the principal has a default calendar configured
470 470
 		$properties = $this->propertyMapper->findPropertyByPathAndName(
471 471
 			$userId,
472
-			'principals/users/' . $userId,
472
+			'principals/users/'.$userId,
473 473
 			'{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL'
474 474
 		);
475 475
 		if ($properties === []) {
@@ -477,14 +477,14 @@  discard block
 block discarded – undo
477 477
 		}
478 478
 		// extract the calendar URI from the property value
479 479
 		$propertyValue = $properties[0]->getPropertyvalue() ?? null;
480
-		if (str_starts_with($propertyValue, 'calendars/' . $userId)) {
481
-			$calendarUri = rtrim(str_replace('calendars/' . $userId . '/', '', $propertyValue), '/');
480
+		if (str_starts_with($propertyValue, 'calendars/'.$userId)) {
481
+			$calendarUri = rtrim(str_replace('calendars/'.$userId.'/', '', $propertyValue), '/');
482 482
 		}
483 483
 		if (empty($calendarUri)) {
484 484
 			return null;
485 485
 		}
486 486
 		// retrieve the calendar by URI
487
-		$calendars = $this->getCalendarsForPrincipal('principals/users/' . $userId, [$calendarUri]);
487
+		$calendars = $this->getCalendarsForPrincipal('principals/users/'.$userId, [$calendarUri]);
488 488
 		if ($calendars === []) {
489 489
 			return null;
490 490
 		}
Please login to merge, or discard this patch.
lib/public/Calendar/IManager.php 1 patch
Indentation   +153 added lines, -153 removed lines patch added patch discarded remove patch
@@ -42,157 +42,157 @@
 block discarded – undo
42 42
  * @since 13.0.0
43 43
  */
44 44
 interface IManager {
45
-	/**
46
-	 * This function is used to search and find objects within the user's calendars.
47
-	 * In case $pattern is empty all events/journals/todos will be returned.
48
-	 *
49
-	 * @param string $pattern which should match within the $searchProperties
50
-	 * @param array $searchProperties defines the properties within the query pattern should match
51
-	 * @param array $options - optional parameters:
52
-	 *                       ['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]]
53
-	 * @param integer|null $limit - limit number of search results
54
-	 * @param integer|null $offset - offset for paging of search results
55
-	 * @return array an array of events/journals/todos which are arrays of arrays of key-value-pairs
56
-	 * @since 13.0.0
57
-	 * @deprecated 23.0.0 use \OCP\Calendar\IManager::searchForPrincipal
58
-	 */
59
-	public function search($pattern, array $searchProperties = [], array $options = [], $limit = null, $offset = null);
60
-
61
-	/**
62
-	 * Check if calendars are available
63
-	 *
64
-	 * @return bool true if enabled, false if not
65
-	 * @since 13.0.0
66
-	 * @deprecated 23.0.0
67
-	 */
68
-	public function isEnabled();
69
-
70
-	/**
71
-	 * Registers a calendar
72
-	 *
73
-	 * @param ICalendar $calendar
74
-	 * @return void
75
-	 * @since 13.0.0
76
-	 * @deprecated 23.0.0 use \OCP\AppFramework\Bootstrap\IRegistrationContext::registerCalendarProvider
77
-	 */
78
-	public function registerCalendar(ICalendar $calendar);
79
-
80
-	/**
81
-	 * Unregisters a calendar
82
-	 *
83
-	 * @param ICalendar $calendar
84
-	 * @return void
85
-	 * @since 13.0.0
86
-	 * @deprecated 23.0.0
87
-	 */
88
-	public function unregisterCalendar(ICalendar $calendar);
89
-
90
-	/**
91
-	 * In order to improve lazy loading a closure can be registered which will be called in case
92
-	 * calendars are actually requested
93
-	 *
94
-	 * @param \Closure $callable
95
-	 * @return void
96
-	 * @since 13.0.0
97
-	 * @deprecated 23.0.0 use \OCP\AppFramework\Bootstrap\IRegistrationContext::registerCalendarProvider
98
-	 */
99
-	public function register(\Closure $callable);
100
-
101
-	/**
102
-	 * @return ICalendar[]
103
-	 * @since 13.0.0
104
-	 * @deprecated 23.0.0 use \OCP\Calendar\IManager::getCalendarsForPrincipal
105
-	 */
106
-	public function getCalendars();
107
-
108
-	/**
109
-	 * removes all registered calendar instances
110
-	 *
111
-	 * @return void
112
-	 * @since 13.0.0
113
-	 * @deprecated 23.0.0
114
-	 */
115
-	public function clear();
116
-
117
-	/**
118
-	 * @param string $principalUri URI of the principal
119
-	 * @param string[] $calendarUris optionally specify which calendars to load, or all if this array is empty
120
-	 *
121
-	 * @return ICalendar[]
122
-	 * @since 23.0.0
123
-	 */
124
-	public function getCalendarsForPrincipal(string $principalUri, array $calendarUris = []): array;
125
-
126
-	/**
127
-	 * Query a principals calendar(s)
128
-	 *
129
-	 * @param ICalendarQuery $query
130
-	 * @return array[]
131
-	 * @since 23.0.0
132
-	 */
133
-	public function searchForPrincipal(ICalendarQuery $query): array;
134
-
135
-	/**
136
-	 * Build a new query for searchForPrincipal
137
-	 *
138
-	 * @return ICalendarQuery
139
-	 * @since 23.0.0
140
-	 */
141
-	public function newQuery(string $principalUri) : ICalendarQuery;
142
-
143
-	/**
144
-	 * Handles a iMip message
145
-	 *
146
-	 * @param array{absent?: "create"} $options
147
-	 *
148
-	 * @throws \OCP\DB\Exception
149
-	 *
150
-	 * @since 32.0.0
151
-	 */
152
-	public function handleIMip(string $userId, string $message, array $options = []): bool;
153
-
154
-	/**
155
-	 * Handle a iMip REQUEST message
156
-	 *
157
-	 * @since 31.0.0
158
-	 */
159
-	public function handleIMipRequest(string $principalUri, string $sender, string $recipient, string $calendarData): bool;
160
-
161
-	/**
162
-	 * Handle a iMip REPLY message
163
-	 *
164
-	 * @since 25.0.0
165
-	 */
166
-	public function handleIMipReply(string $principalUri, string $sender, string $recipient, string $calendarData): bool;
167
-
168
-	/**
169
-	 * Handle a iMip CANCEL message
170
-	 *
171
-	 * @since 25.0.0
172
-	 */
173
-	public function handleIMipCancel(string $principalUri, string $sender, ?string $replyTo, string $recipient, string $calendarData): bool;
174
-
175
-	/**
176
-	 * Create a new event builder instance. Please have a look at its documentation and the
177
-	 * \OCP\Calendar\ICreateFromString interface on how to use it.
178
-	 *
179
-	 * @since 31.0.0
180
-	 */
181
-	public function createEventBuilder(): ICalendarEventBuilder;
182
-
183
-	/**
184
-	 * Check the availability of the given organizer and attendees in the given time range.
185
-	 *
186
-	 * @since 31.0.0
187
-	 *
188
-	 * @param IUser $organizer The organizing user from whose perspective to do the availability check.
189
-	 * @param string[] $attendees Email addresses of attendees to check for (with or without a "mailto:" prefix). Only users on this instance can be checked. The rest will be silently ignored.
190
-	 * @return IAvailabilityResult[] Availabilities of the organizer and all attendees which are also users on this instance. As such, the array might not contain an entry for each given attendee.
191
-	 */
192
-	public function checkAvailability(
193
-		DateTimeInterface $start,
194
-		DateTimeInterface $end,
195
-		IUser $organizer,
196
-		array $attendees,
197
-	): array;
45
+    /**
46
+     * This function is used to search and find objects within the user's calendars.
47
+     * In case $pattern is empty all events/journals/todos will be returned.
48
+     *
49
+     * @param string $pattern which should match within the $searchProperties
50
+     * @param array $searchProperties defines the properties within the query pattern should match
51
+     * @param array $options - optional parameters:
52
+     *                       ['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]]
53
+     * @param integer|null $limit - limit number of search results
54
+     * @param integer|null $offset - offset for paging of search results
55
+     * @return array an array of events/journals/todos which are arrays of arrays of key-value-pairs
56
+     * @since 13.0.0
57
+     * @deprecated 23.0.0 use \OCP\Calendar\IManager::searchForPrincipal
58
+     */
59
+    public function search($pattern, array $searchProperties = [], array $options = [], $limit = null, $offset = null);
60
+
61
+    /**
62
+     * Check if calendars are available
63
+     *
64
+     * @return bool true if enabled, false if not
65
+     * @since 13.0.0
66
+     * @deprecated 23.0.0
67
+     */
68
+    public function isEnabled();
69
+
70
+    /**
71
+     * Registers a calendar
72
+     *
73
+     * @param ICalendar $calendar
74
+     * @return void
75
+     * @since 13.0.0
76
+     * @deprecated 23.0.0 use \OCP\AppFramework\Bootstrap\IRegistrationContext::registerCalendarProvider
77
+     */
78
+    public function registerCalendar(ICalendar $calendar);
79
+
80
+    /**
81
+     * Unregisters a calendar
82
+     *
83
+     * @param ICalendar $calendar
84
+     * @return void
85
+     * @since 13.0.0
86
+     * @deprecated 23.0.0
87
+     */
88
+    public function unregisterCalendar(ICalendar $calendar);
89
+
90
+    /**
91
+     * In order to improve lazy loading a closure can be registered which will be called in case
92
+     * calendars are actually requested
93
+     *
94
+     * @param \Closure $callable
95
+     * @return void
96
+     * @since 13.0.0
97
+     * @deprecated 23.0.0 use \OCP\AppFramework\Bootstrap\IRegistrationContext::registerCalendarProvider
98
+     */
99
+    public function register(\Closure $callable);
100
+
101
+    /**
102
+     * @return ICalendar[]
103
+     * @since 13.0.0
104
+     * @deprecated 23.0.0 use \OCP\Calendar\IManager::getCalendarsForPrincipal
105
+     */
106
+    public function getCalendars();
107
+
108
+    /**
109
+     * removes all registered calendar instances
110
+     *
111
+     * @return void
112
+     * @since 13.0.0
113
+     * @deprecated 23.0.0
114
+     */
115
+    public function clear();
116
+
117
+    /**
118
+     * @param string $principalUri URI of the principal
119
+     * @param string[] $calendarUris optionally specify which calendars to load, or all if this array is empty
120
+     *
121
+     * @return ICalendar[]
122
+     * @since 23.0.0
123
+     */
124
+    public function getCalendarsForPrincipal(string $principalUri, array $calendarUris = []): array;
125
+
126
+    /**
127
+     * Query a principals calendar(s)
128
+     *
129
+     * @param ICalendarQuery $query
130
+     * @return array[]
131
+     * @since 23.0.0
132
+     */
133
+    public function searchForPrincipal(ICalendarQuery $query): array;
134
+
135
+    /**
136
+     * Build a new query for searchForPrincipal
137
+     *
138
+     * @return ICalendarQuery
139
+     * @since 23.0.0
140
+     */
141
+    public function newQuery(string $principalUri) : ICalendarQuery;
142
+
143
+    /**
144
+     * Handles a iMip message
145
+     *
146
+     * @param array{absent?: "create"} $options
147
+     *
148
+     * @throws \OCP\DB\Exception
149
+     *
150
+     * @since 32.0.0
151
+     */
152
+    public function handleIMip(string $userId, string $message, array $options = []): bool;
153
+
154
+    /**
155
+     * Handle a iMip REQUEST message
156
+     *
157
+     * @since 31.0.0
158
+     */
159
+    public function handleIMipRequest(string $principalUri, string $sender, string $recipient, string $calendarData): bool;
160
+
161
+    /**
162
+     * Handle a iMip REPLY message
163
+     *
164
+     * @since 25.0.0
165
+     */
166
+    public function handleIMipReply(string $principalUri, string $sender, string $recipient, string $calendarData): bool;
167
+
168
+    /**
169
+     * Handle a iMip CANCEL message
170
+     *
171
+     * @since 25.0.0
172
+     */
173
+    public function handleIMipCancel(string $principalUri, string $sender, ?string $replyTo, string $recipient, string $calendarData): bool;
174
+
175
+    /**
176
+     * Create a new event builder instance. Please have a look at its documentation and the
177
+     * \OCP\Calendar\ICreateFromString interface on how to use it.
178
+     *
179
+     * @since 31.0.0
180
+     */
181
+    public function createEventBuilder(): ICalendarEventBuilder;
182
+
183
+    /**
184
+     * Check the availability of the given organizer and attendees in the given time range.
185
+     *
186
+     * @since 31.0.0
187
+     *
188
+     * @param IUser $organizer The organizing user from whose perspective to do the availability check.
189
+     * @param string[] $attendees Email addresses of attendees to check for (with or without a "mailto:" prefix). Only users on this instance can be checked. The rest will be silently ignored.
190
+     * @return IAvailabilityResult[] Availabilities of the organizer and all attendees which are also users on this instance. As such, the array might not contain an entry for each given attendee.
191
+     */
192
+    public function checkAvailability(
193
+        DateTimeInterface $start,
194
+        DateTimeInterface $end,
195
+        IUser $organizer,
196
+        array $attendees,
197
+    ): array;
198 198
 }
Please login to merge, or discard this patch.