ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Broker.php
Go to the documentation of this file.
1 <?php
2 
4 
9 
38 class 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;
625  $messages[] = $message;
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 }
static parse($date, $referenceTz=null)
Parses either a Date or DateTime, or Duration value.
The ITip class is a utility class that helps with processing so-called iTip messages.
Definition: Broker.php:38
processMessageRequest(Message $itipMessage, VCalendar $existingObject=null)
Processes incoming REQUEST messages.
Definition: Broker.php:279
select($name)
Returns an array with elements that match the specified name.
Definition: Component.php:231
processMessage(Message $itipMessage, VCalendar $existingObject=null)
This method is used to process an incoming itip message.
Definition: Broker.php:112
This class represents an iTip message.
Definition: Message.php:17
parseEventForAttendee(VCalendar $calendar, array $eventInfo, array $oldEventInfo, $attendee)
Parse an event update for an attendee.
Definition: Broker.php:645
The VCalendar component.
Definition: VCalendar.php:23
$summary
Definition: cron.php:24
parseEventInfo(VCalendar $calendar=null)
Returns attendee information and information about instances of an event.
Definition: Broker.php:831
$messages
Definition: en.php:5
catch(Exception $e) $message
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.
processMessageReply(Message $itipMessage, VCalendar $existingObject=null)
Processes incoming REPLY messages.
Definition: Broker.php:341
add()
Adds a new property or component, and returns the new item.
Definition: Component.php:109
processMessageCancel(Message $itipMessage, VCalendar $existingObject=null)
Processes incoming CANCEL messages.
Definition: Broker.php:315
static read($data, $options=0, $charset='UTF-8')
Parses a vCard or iCalendar object, and returns the top component.
Definition: Reader.php:42
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
$exceptions
Definition: Utf8Test.php:67
$key
Definition: croninfo.php:18