ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Broker.php
Go to the documentation of this file.
1<?php
2
4
9
38class Broker {
39
56
70 'DTSTART',
71 'DTEND',
72 'DURATION',
73 'DUE',
74 'RRULE',
75 'RDATE',
76 'EXDATE',
77 'STATUS',
78 ];
79
112 function processMessage(Message $itipMessage, VCalendar $existingObject = null) {
113
114 // We only support events at the moment.
115 if ($itipMessage->component !== 'VEVENT') {
116 return false;
117 }
118
119 switch ($itipMessage->method) {
120
121 case 'REQUEST' :
122 return $this->processMessageRequest($itipMessage, $existingObject);
123
124 case 'CANCEL' :
125 return $this->processMessageCancel($itipMessage, $existingObject);
126
127 case 'REPLY' :
128 return $this->processMessageReply($itipMessage, $existingObject);
129
130 default :
131 // Unsupported iTip message
132 return;
133
134 }
135
136 return $existingObject;
137
138 }
139
169 function parseEvent($calendar = null, $userHref, $oldCalendar = null) {
170
171 if ($oldCalendar) {
172 if (is_string($oldCalendar)) {
173 $oldCalendar = Reader::read($oldCalendar);
174 }
175 if (!isset($oldCalendar->VEVENT)) {
176 // We only support events at the moment
177 return [];
178 }
179
180 $oldEventInfo = $this->parseEventInfo($oldCalendar);
181 } else {
182 $oldEventInfo = [
183 'organizer' => null,
184 'significantChangeHash' => '',
185 'attendees' => [],
186 ];
187 }
188
189 $userHref = (array)$userHref;
190
191 if (!is_null($calendar)) {
192
193 if (is_string($calendar)) {
195 }
196 if (!isset($calendar->VEVENT)) {
197 // We only support events at the moment
198 return [];
199 }
200 $eventInfo = $this->parseEventInfo($calendar);
201 if (!$eventInfo['attendees'] && !$oldEventInfo['attendees']) {
202 // If there were no attendees on either side of the equation,
203 // we don't need to do anything.
204 return [];
205 }
206 if (!$eventInfo['organizer'] && !$oldEventInfo['organizer']) {
207 // There was no organizer before or after the change.
208 return [];
209 }
210
211 $baseCalendar = $calendar;
212
213 // If the new object didn't have an organizer, the organizer
214 // changed the object from a scheduling object to a non-scheduling
215 // object. We just copy the info from the old object.
216 if (!$eventInfo['organizer'] && $oldEventInfo['organizer']) {
217 $eventInfo['organizer'] = $oldEventInfo['organizer'];
218 $eventInfo['organizerName'] = $oldEventInfo['organizerName'];
219 }
220
221 } else {
222 // The calendar object got deleted, we need to process this as a
223 // cancellation / decline.
224 if (!$oldCalendar) {
225 // No old and no new calendar, there's no thing to do.
226 return [];
227 }
228
229 $eventInfo = $oldEventInfo;
230
231 if (in_array($eventInfo['organizer'], $userHref)) {
232 // This is an organizer deleting the event.
233 $eventInfo['attendees'] = [];
234 // Increasing the sequence, but only if the organizer deleted
235 // the event.
236 $eventInfo['sequence']++;
237 } else {
238 // This is an attendee deleting the event.
239 foreach ($eventInfo['attendees'] as $key => $attendee) {
240 if (in_array($attendee['href'], $userHref)) {
241 $eventInfo['attendees'][$key]['instances'] = ['master' =>
242 ['id' => 'master', 'partstat' => 'DECLINED']
243 ];
244 }
245 }
246 }
247 $baseCalendar = $oldCalendar;
248
249 }
250
251 if (in_array($eventInfo['organizer'], $userHref)) {
252 return $this->parseEventForOrganizer($baseCalendar, $eventInfo, $oldEventInfo);
253 } elseif ($oldCalendar) {
254 // We need to figure out if the user is an attendee, but we're only
255 // doing so if there's an oldCalendar, because we only want to
256 // process updates, not creation of new events.
257 foreach ($eventInfo['attendees'] as $attendee) {
258 if (in_array($attendee['href'], $userHref)) {
259 return $this->parseEventForAttendee($baseCalendar, $eventInfo, $oldEventInfo, $attendee['href']);
260 }
261 }
262 }
263 return [];
264
265 }
266
279 protected function processMessageRequest(Message $itipMessage, VCalendar $existingObject = null) {
280
281 if (!$existingObject) {
282 // This is a new invite, and we're just going to copy over
283 // all the components from the invite.
284 $existingObject = new VCalendar();
285 foreach ($itipMessage->message->getComponents() as $component) {
286 $existingObject->add(clone $component);
287 }
288 } else {
289 // We need to update an existing object with all the new
290 // information. We can just remove all existing components
291 // and create new ones.
292 foreach ($existingObject->getComponents() as $component) {
293 $existingObject->remove($component);
294 }
295 foreach ($itipMessage->message->getComponents() as $component) {
296 $existingObject->add(clone $component);
297 }
298 }
299 return $existingObject;
300
301 }
302
315 protected function processMessageCancel(Message $itipMessage, VCalendar $existingObject = null) {
316
317 if (!$existingObject) {
318 // The event didn't exist in the first place, so we're just
319 // ignoring this message.
320 } else {
321 foreach ($existingObject->VEVENT as $vevent) {
322 $vevent->STATUS = 'CANCELLED';
323 $vevent->SEQUENCE = $itipMessage->sequence;
324 }
325 }
326 return $existingObject;
327
328 }
329
341 protected function processMessageReply(Message $itipMessage, VCalendar $existingObject = null) {
342
343 // A reply can only be processed based on an existing object.
344 // If the object is not available, the reply is ignored.
345 if (!$existingObject) {
346 return;
347 }
348 $instances = [];
349 $requestStatus = '2.0';
350
351 // Finding all the instances the attendee replied to.
352 foreach ($itipMessage->message->VEVENT as $vevent) {
353 $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master';
354 $attendee = $vevent->ATTENDEE;
355 $instances[$recurId] = $attendee['PARTSTAT']->getValue();
356 if (isset($vevent->{'REQUEST-STATUS'})) {
357 $requestStatus = $vevent->{'REQUEST-STATUS'}->getValue();
358 list($requestStatus) = explode(';', $requestStatus);
359 }
360 }
361
362 // Now we need to loop through the original organizer event, to find
363 // all the instances where we have a reply for.
364 $masterObject = null;
365 foreach ($existingObject->VEVENT as $vevent) {
366 $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master';
367 if ($recurId === 'master') {
368 $masterObject = $vevent;
369 }
370 if (isset($instances[$recurId])) {
371 $attendeeFound = false;
372 if (isset($vevent->ATTENDEE)) {
373 foreach ($vevent->ATTENDEE as $attendee) {
374 if ($attendee->getValue() === $itipMessage->sender) {
375 $attendeeFound = true;
376 $attendee['PARTSTAT'] = $instances[$recurId];
377 $attendee['SCHEDULE-STATUS'] = $requestStatus;
378 // Un-setting the RSVP status, because we now know
379 // that the attendee already replied.
380 unset($attendee['RSVP']);
381 break;
382 }
383 }
384 }
385 if (!$attendeeFound) {
386 // Adding a new attendee. The iTip documentation calls this
387 // a party crasher.
388 $attendee = $vevent->add('ATTENDEE', $itipMessage->sender, [
389 'PARTSTAT' => $instances[$recurId]
390 ]);
391 if ($itipMessage->senderName) $attendee['CN'] = $itipMessage->senderName;
392 }
393 unset($instances[$recurId]);
394 }
395 }
396
397 if (!$masterObject) {
398 // No master object, we can't add new instances.
399 return;
400 }
401 // If we got replies to instances that did not exist in the
402 // original list, it means that new exceptions must be created.
403 foreach ($instances as $recurId => $partstat) {
404
405 $recurrenceIterator = new EventIterator($existingObject, $itipMessage->uid);
406 $found = false;
407 $iterations = 1000;
408 do {
409
410 $newObject = $recurrenceIterator->getEventObject();
411 $recurrenceIterator->next();
412
413 if (isset($newObject->{'RECURRENCE-ID'}) && $newObject->{'RECURRENCE-ID'}->getValue() === $recurId) {
414 $found = true;
415 }
416 $iterations--;
417
418 } while ($recurrenceIterator->valid() && !$found && $iterations);
419
420 // Invalid recurrence id. Skipping this object.
421 if (!$found) continue;
422
423 unset(
424 $newObject->RRULE,
425 $newObject->EXDATE,
426 $newObject->RDATE
427 );
428 $attendeeFound = false;
429 if (isset($newObject->ATTENDEE)) {
430 foreach ($newObject->ATTENDEE as $attendee) {
431 if ($attendee->getValue() === $itipMessage->sender) {
432 $attendeeFound = true;
433 $attendee['PARTSTAT'] = $partstat;
434 break;
435 }
436 }
437 }
438 if (!$attendeeFound) {
439 // Adding a new attendee
440 $attendee = $newObject->add('ATTENDEE', $itipMessage->sender, [
441 'PARTSTAT' => $partstat
442 ]);
443 if ($itipMessage->senderName) {
444 $attendee['CN'] = $itipMessage->senderName;
445 }
446 }
447 $existingObject->add($newObject);
448
449 }
450 return $existingObject;
451
452 }
453
468 protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, array $oldEventInfo) {
469
470 // Merging attendee lists.
471 $attendees = [];
472 foreach ($oldEventInfo['attendees'] as $attendee) {
473 $attendees[$attendee['href']] = [
474 'href' => $attendee['href'],
475 'oldInstances' => $attendee['instances'],
476 'newInstances' => [],
477 'name' => $attendee['name'],
478 'forceSend' => null,
479 ];
480 }
481 foreach ($eventInfo['attendees'] as $attendee) {
482 if (isset($attendees[$attendee['href']])) {
483 $attendees[$attendee['href']]['name'] = $attendee['name'];
484 $attendees[$attendee['href']]['newInstances'] = $attendee['instances'];
485 $attendees[$attendee['href']]['forceSend'] = $attendee['forceSend'];
486 } else {
487 $attendees[$attendee['href']] = [
488 'href' => $attendee['href'],
489 'oldInstances' => [],
490 'newInstances' => $attendee['instances'],
491 'name' => $attendee['name'],
492 'forceSend' => $attendee['forceSend'],
493 ];
494 }
495 }
496
497 $messages = [];
498
499 foreach ($attendees as $attendee) {
500
501 // An organizer can also be an attendee. We should not generate any
502 // messages for those.
503 if ($attendee['href'] === $eventInfo['organizer']) {
504 continue;
505 }
506
507 $message = new Message();
508 $message->uid = $eventInfo['uid'];
509 $message->component = 'VEVENT';
510 $message->sequence = $eventInfo['sequence'];
511 $message->sender = $eventInfo['organizer'];
512 $message->senderName = $eventInfo['organizerName'];
513 $message->recipient = $attendee['href'];
514 $message->recipientName = $attendee['name'];
515
516 if (!$attendee['newInstances']) {
517
518 // If there are no instances the attendee is a part of, it
519 // means the attendee was removed and we need to send him a
520 // CANCEL.
521 $message->method = 'CANCEL';
522
523 // Creating the new iCalendar body.
524 $icalMsg = new VCalendar();
525 $icalMsg->METHOD = $message->method;
526 $event = $icalMsg->add('VEVENT', [
527 'UID' => $message->uid,
528 'SEQUENCE' => $message->sequence,
529 ]);
530 if (isset($calendar->VEVENT->SUMMARY)) {
531 $event->add('SUMMARY', $calendar->VEVENT->SUMMARY->getValue());
532 }
533 $event->add(clone $calendar->VEVENT->DTSTART);
534 if (isset($calendar->VEVENT->DTEND)) {
535 $event->add(clone $calendar->VEVENT->DTEND);
536 } elseif (isset($calendar->VEVENT->DURATION)) {
537 $event->add(clone $calendar->VEVENT->DURATION);
538 }
539 $org = $event->add('ORGANIZER', $eventInfo['organizer']);
540 if ($eventInfo['organizerName']) $org['CN'] = $eventInfo['organizerName'];
541 $event->add('ATTENDEE', $attendee['href'], [
542 'CN' => $attendee['name'],
543 ]);
544 $message->significantChange = true;
545
546 } else {
547
548 // The attendee gets the updated event body
549 $message->method = 'REQUEST';
550
551 // Creating the new iCalendar body.
552 $icalMsg = new VCalendar();
553 $icalMsg->METHOD = $message->method;
554
555 foreach ($calendar->select('VTIMEZONE') as $timezone) {
556 $icalMsg->add(clone $timezone);
557 }
558
559 // We need to find out that this change is significant. If it's
560 // not, systems may opt to not send messages.
561 //
562 // We do this based on the 'significantChangeHash' which is
563 // some value that changes if there's a certain set of
564 // properties changed in the event, or simply if there's a
565 // difference in instances that the attendee is invited to.
566
567 $message->significantChange =
568 $attendee['forceSend'] === 'REQUEST' ||
569 array_keys($attendee['oldInstances']) != array_keys($attendee['newInstances']) ||
570 $oldEventInfo['significantChangeHash'] !== $eventInfo['significantChangeHash'];
571
572 foreach ($attendee['newInstances'] as $instanceId => $instanceInfo) {
573
574 $currentEvent = clone $eventInfo['instances'][$instanceId];
575 if ($instanceId === 'master') {
576
577 // We need to find a list of events that the attendee
578 // is not a part of to add to the list of exceptions.
579 $exceptions = [];
580 foreach ($eventInfo['instances'] as $instanceId => $vevent) {
581 if (!isset($attendee['newInstances'][$instanceId])) {
582 $exceptions[] = $instanceId;
583 }
584 }
585
586 // If there were exceptions, we need to add it to an
587 // existing EXDATE property, if it exists.
588 if ($exceptions) {
589 if (isset($currentEvent->EXDATE)) {
590 $currentEvent->EXDATE->setParts(array_merge(
591 $currentEvent->EXDATE->getParts(),
593 ));
594 } else {
595 $currentEvent->EXDATE = $exceptions;
596 }
597 }
598
599 // Cleaning up any scheduling information that
600 // shouldn't be sent along.
601 unset($currentEvent->ORGANIZER['SCHEDULE-FORCE-SEND']);
602 unset($currentEvent->ORGANIZER['SCHEDULE-STATUS']);
603
604 foreach ($currentEvent->ATTENDEE as $attendee) {
605 unset($attendee['SCHEDULE-FORCE-SEND']);
606 unset($attendee['SCHEDULE-STATUS']);
607
608 // We're adding PARTSTAT=NEEDS-ACTION to ensure that
609 // iOS shows an "Inbox Item"
610 if (!isset($attendee['PARTSTAT'])) {
611 $attendee['PARTSTAT'] = 'NEEDS-ACTION';
612 }
613
614 }
615
616 }
617
618 $icalMsg->add($currentEvent);
619
620 }
621
622 }
623
624 $message->message = $icalMsg;
626
627 }
628
629 return $messages;
630
631 }
632
645 protected function parseEventForAttendee(VCalendar $calendar, array $eventInfo, array $oldEventInfo, $attendee) {
646
647 if ($this->scheduleAgentServerRules && $eventInfo['organizerScheduleAgent'] === 'CLIENT') {
648 return [];
649 }
650
651 // Don't bother generating messages for events that have already been
652 // cancelled.
653 if ($eventInfo['status'] === 'CANCELLED') {
654 return [];
655 }
656
657 $oldInstances = !empty($oldEventInfo['attendees'][$attendee]['instances']) ?
658 $oldEventInfo['attendees'][$attendee]['instances'] :
659 [];
660
661 $instances = [];
662 foreach ($oldInstances as $instance) {
663
664 $instances[$instance['id']] = [
665 'id' => $instance['id'],
666 'oldstatus' => $instance['partstat'],
667 'newstatus' => null,
668 ];
669
670 }
671 foreach ($eventInfo['attendees'][$attendee]['instances'] as $instance) {
672
673 if (isset($instances[$instance['id']])) {
674 $instances[$instance['id']]['newstatus'] = $instance['partstat'];
675 } else {
676 $instances[$instance['id']] = [
677 'id' => $instance['id'],
678 'oldstatus' => null,
679 'newstatus' => $instance['partstat'],
680 ];
681 }
682
683 }
684
685 // We need to also look for differences in EXDATE. If there are new
686 // items in EXDATE, it means that an attendee deleted instances of an
687 // event, which means we need to send DECLINED specifically for those
688 // instances.
689 // We only need to do that though, if the master event is not declined.
690 if (isset($instances['master']) && $instances['master']['newstatus'] !== 'DECLINED') {
691 foreach ($eventInfo['exdate'] as $exDate) {
692
693 if (!in_array($exDate, $oldEventInfo['exdate'])) {
694 if (isset($instances[$exDate])) {
695 $instances[$exDate]['newstatus'] = 'DECLINED';
696 } else {
697 $instances[$exDate] = [
698 'id' => $exDate,
699 'oldstatus' => null,
700 'newstatus' => 'DECLINED',
701 ];
702 }
703 }
704
705 }
706 }
707
708 // Gathering a few extra properties for each instance.
709 foreach ($instances as $recurId => $instanceInfo) {
710
711 if (isset($eventInfo['instances'][$recurId])) {
712 $instances[$recurId]['dtstart'] = clone $eventInfo['instances'][$recurId]->DTSTART;
713 } else {
714 $instances[$recurId]['dtstart'] = $recurId;
715 }
716
717 }
718
719 $message = new Message();
720 $message->uid = $eventInfo['uid'];
721 $message->method = 'REPLY';
722 $message->component = 'VEVENT';
723 $message->sequence = $eventInfo['sequence'];
724 $message->sender = $attendee;
725 $message->senderName = $eventInfo['attendees'][$attendee]['name'];
726 $message->recipient = $eventInfo['organizer'];
727 $message->recipientName = $eventInfo['organizerName'];
728
729 $icalMsg = new VCalendar();
730 $icalMsg->METHOD = 'REPLY';
731
732 $hasReply = false;
733
734 foreach ($instances as $instance) {
735
736 if ($instance['oldstatus'] == $instance['newstatus'] && $eventInfo['organizerForceSend'] !== 'REPLY') {
737 // Skip
738 continue;
739 }
740
741 $event = $icalMsg->add('VEVENT', [
742 'UID' => $message->uid,
743 'SEQUENCE' => $message->sequence,
744 ]);
745 $summary = isset($calendar->VEVENT->SUMMARY) ? $calendar->VEVENT->SUMMARY->getValue() : '';
746 // Adding properties from the correct source instance
747 if (isset($eventInfo['instances'][$instance['id']])) {
748 $instanceObj = $eventInfo['instances'][$instance['id']];
749 $event->add(clone $instanceObj->DTSTART);
750 if (isset($instanceObj->DTEND)) {
751 $event->add(clone $instanceObj->DTEND);
752 } elseif (isset($instanceObj->DURATION)) {
753 $event->add(clone $instanceObj->DURATION);
754 }
755 if (isset($instanceObj->SUMMARY)) {
756 $event->add('SUMMARY', $instanceObj->SUMMARY->getValue());
757 } elseif ($summary) {
758 $event->add('SUMMARY', $summary);
759 }
760 } else {
761 // This branch of the code is reached, when a reply is
762 // generated for an instance of a recurring event, through the
763 // fact that the instance has disappeared by showing up in
764 // EXDATE
765 $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']);
766 // Treat is as a DATE field
767 if (strlen($instance['id']) <= 8) {
768 $event->add('DTSTART', $dt, ['VALUE' => 'DATE']);
769 } else {
770 $event->add('DTSTART', $dt);
771 }
772 if ($summary) {
773 $event->add('SUMMARY', $summary);
774 }
775 }
776 if ($instance['id'] !== 'master') {
777 $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']);
778 // Treat is as a DATE field
779 if (strlen($instance['id']) <= 8) {
780 $event->add('RECURRENCE-ID', $dt, ['VALUE' => 'DATE']);
781 } else {
782 $event->add('RECURRENCE-ID', $dt);
783 }
784 }
785 $organizer = $event->add('ORGANIZER', $message->recipient);
786 if ($message->recipientName) {
787 $organizer['CN'] = $message->recipientName;
788 }
789 $attendee = $event->add('ATTENDEE', $message->sender, [
790 'PARTSTAT' => $instance['newstatus']
791 ]);
792 if ($message->senderName) {
793 $attendee['CN'] = $message->senderName;
794 }
795 $hasReply = true;
796
797 }
798
799 if ($hasReply) {
800 $message->message = $icalMsg;
801 return [$message];
802 } else {
803 return [];
804 }
805
806 }
807
831 protected function parseEventInfo(VCalendar $calendar = null) {
832
833 $uid = null;
834 $organizer = null;
835 $organizerName = null;
836 $organizerForceSend = null;
837 $sequence = null;
838 $timezone = null;
839 $status = null;
840 $organizerScheduleAgent = 'SERVER';
841
842 $significantChangeHash = '';
843
844 // Now we need to collect a list of attendees, and which instances they
845 // are a part of.
846 $attendees = [];
847
848 $instances = [];
849 $exdate = [];
850
851 foreach ($calendar->VEVENT as $vevent) {
852 $rrule = [];
853
854 if (is_null($uid)) {
855 $uid = $vevent->UID->getValue();
856 } else {
857 if ($uid !== $vevent->UID->getValue()) {
858 throw new ITipException('If a calendar contained more than one event, they must have the same UID.');
859 }
860 }
861
862 if (!isset($vevent->DTSTART)) {
863 throw new ITipException('An event MUST have a DTSTART property.');
864 }
865
866 if (isset($vevent->ORGANIZER)) {
867 if (is_null($organizer)) {
868 $organizer = $vevent->ORGANIZER->getNormalizedValue();
869 $organizerName = isset($vevent->ORGANIZER['CN']) ? $vevent->ORGANIZER['CN'] : null;
870 } else {
871 if ($organizer !== $vevent->ORGANIZER->getNormalizedValue()) {
872 throw new SameOrganizerForAllComponentsException('Every instance of the event must have the same organizer.');
873 }
874 }
875 $organizerForceSend =
876 isset($vevent->ORGANIZER['SCHEDULE-FORCE-SEND']) ?
877 strtoupper($vevent->ORGANIZER['SCHEDULE-FORCE-SEND']) :
878 null;
879 $organizerScheduleAgent =
880 isset($vevent->ORGANIZER['SCHEDULE-AGENT']) ?
881 strtoupper((string)$vevent->ORGANIZER['SCHEDULE-AGENT']) :
882 'SERVER';
883 }
884 if (is_null($sequence) && isset($vevent->SEQUENCE)) {
885 $sequence = $vevent->SEQUENCE->getValue();
886 }
887 if (isset($vevent->EXDATE)) {
888 foreach ($vevent->select('EXDATE') as $val) {
889 $exdate = array_merge($exdate, $val->getParts());
890 }
891 sort($exdate);
892 }
893 if (isset($vevent->RRULE)) {
894 foreach ($vevent->select('RRULE') as $rr) {
895 foreach ($rr->getParts() as $key => $val) {
896 // ignore default values (https://github.com/sabre-io/vobject/issues/126)
897 if ($key === 'INTERVAL' && $val == 1) {
898 continue;
899 }
900 if (is_array($val)) {
901 $val = implode(',', $val);
902 }
903 $rrule[] = "$key=$val";
904 }
905 }
906 sort($rrule);
907 }
908 if (isset($vevent->STATUS)) {
909 $status = strtoupper($vevent->STATUS->getValue());
910 }
911
912 $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master';
913 if (is_null($timezone)) {
914 if ($recurId === 'master') {
915 $timezone = $vevent->DTSTART->getDateTime()->getTimeZone();
916 } else {
917 $timezone = $vevent->{'RECURRENCE-ID'}->getDateTime()->getTimeZone();
918 }
919 }
920 if (isset($vevent->ATTENDEE)) {
921 foreach ($vevent->ATTENDEE as $attendee) {
922
923 if ($this->scheduleAgentServerRules &&
924 isset($attendee['SCHEDULE-AGENT']) &&
925 strtoupper($attendee['SCHEDULE-AGENT']->getValue()) === 'CLIENT'
926 ) {
927 continue;
928 }
929 $partStat =
930 isset($attendee['PARTSTAT']) ?
931 strtoupper($attendee['PARTSTAT']) :
932 'NEEDS-ACTION';
933
934 $forceSend =
935 isset($attendee['SCHEDULE-FORCE-SEND']) ?
936 strtoupper($attendee['SCHEDULE-FORCE-SEND']) :
937 null;
938
939
940 if (isset($attendees[$attendee->getNormalizedValue()])) {
941 $attendees[$attendee->getNormalizedValue()]['instances'][$recurId] = [
942 'id' => $recurId,
943 'partstat' => $partStat,
944 'forceSend' => $forceSend,
945 ];
946 } else {
947 $attendees[$attendee->getNormalizedValue()] = [
948 'href' => $attendee->getNormalizedValue(),
949 'instances' => [
950 $recurId => [
951 'id' => $recurId,
952 'partstat' => $partStat,
953 ],
954 ],
955 'name' => isset($attendee['CN']) ? (string)$attendee['CN'] : null,
956 'forceSend' => $forceSend,
957 ];
958 }
959
960 }
961 $instances[$recurId] = $vevent;
962
963 }
964
965 foreach ($this->significantChangeProperties as $prop) {
966 if (isset($vevent->$prop)) {
967 $propertyValues = $vevent->select($prop);
968
969 $significantChangeHash .= $prop . ':';
970
971 if ($prop === 'EXDATE') {
972 $significantChangeHash .= implode(',', $exdate) . ';';
973 } elseif ($prop === 'RRULE') {
974 $significantChangeHash .= implode(',', $rrule) . ';';
975 } else {
976 foreach ($propertyValues as $val) {
977 $significantChangeHash .= $val->getValue() . ';';
978 }
979 }
980 }
981 }
982 }
983 $significantChangeHash = md5($significantChangeHash);
984
985 return compact(
986 'uid',
987 'organizer',
988 'organizerName',
989 'organizerScheduleAgent',
990 'organizerForceSend',
991 'instances',
992 'attendees',
993 'sequence',
994 'exdate',
995 'timezone',
996 'significantChangeHash',
997 'status'
998 );
999
1000 }
1001
1002}
$exceptions
Definition: Utf8Test.php:67
An exception for terminatinating execution or to throw for unit testing.
The VCalendar component.
Definition: VCalendar.php:23
The ITip\Broker class is a utility class that helps with processing so-called iTip messages.
Definition: Broker.php:38
parseEventInfo(VCalendar $calendar=null)
Returns attendee information and information about instances of an event.
Definition: Broker.php:831
processMessageReply(Message $itipMessage, VCalendar $existingObject=null)
Processes incoming REPLY messages.
Definition: Broker.php:341
processMessage(Message $itipMessage, VCalendar $existingObject=null)
This method is used to process an incoming itip message.
Definition: Broker.php:112
processMessageCancel(Message $itipMessage, VCalendar $existingObject=null)
Processes incoming CANCEL messages.
Definition: Broker.php:315
parseEventForAttendee(VCalendar $calendar, array $eventInfo, array $oldEventInfo, $attendee)
Parse an event update for an attendee.
Definition: Broker.php:645
parseEvent($calendar=null, $userHref, $oldCalendar=null)
This function parses a VCALENDAR object and figure out if any messages need to be sent.
Definition: Broker.php:169
processMessageRequest(Message $itipMessage, VCalendar $existingObject=null)
Processes incoming REQUEST messages.
Definition: Broker.php:279
parseEventForOrganizer(VCalendar $calendar, array $eventInfo, array $oldEventInfo)
This method is used in cases where an event got updated, and we potentially need to send emails to at...
Definition: Broker.php:468
This message is emitted in case of serious problems with iTip messages.
This class represents an iTip message.
Definition: Message.php:17
iCalendar/vCard/jCal/jCard/xCal/xCard reader object.
Definition: Reader.php:15
static read($data, $options=0, $charset='UTF-8')
Parses a vCard or iCalendar object, and returns the top component.
Definition: Reader.php:42
$key
Definition: croninfo.php:18
$messages
Definition: en.php:5
$summary
Definition: cron.php:24
catch(Exception $e) $message
parse($uri)
Parses a URI and returns its individual components.
Definition: functions.php:181