/** * This method is used in cases where an event got updated, and we * potentially need to send emails to attendees to let them know of updates * in the events. * * We will detect which attendees got added, which got removed and create * specific messages for these situations. * * @param VCalendar $calendar * @param array $eventInfo * @param array $oldEventInfo * @return array */ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, array $oldEventInfo) { // Merging attendee lists. $attendees = array(); foreach ($oldEventInfo['attendees'] as $attendee) { $attendees[$attendee['href']] = array('href' => $attendee['href'], 'oldInstances' => $attendee['instances'], 'newInstances' => array(), 'name' => $attendee['name'], 'forceSend' => null); } foreach ($eventInfo['attendees'] as $attendee) { if (isset($attendees[$attendee['href']])) { $attendees[$attendee['href']]['name'] = $attendee['name']; $attendees[$attendee['href']]['newInstances'] = $attendee['instances']; $attendees[$attendee['href']]['forceSend'] = $attendee['forceSend']; } else { $attendees[$attendee['href']] = array('href' => $attendee['href'], 'oldInstances' => array(), 'newInstances' => $attendee['instances'], 'name' => $attendee['name'], 'forceSend' => $attendee['forceSend']); } } $messages = array(); foreach ($attendees as $attendee) { // An organizer can also be an attendee. We should not generate any // messages for those. if ($attendee['href'] === $eventInfo['organizer']) { continue; } $message = new Message(); $message->uid = $eventInfo['uid']; $message->component = 'VEVENT'; $message->sequence = $eventInfo['sequence']; $message->sender = $eventInfo['organizer']; $message->senderName = $eventInfo['organizerName']; $message->recipient = $attendee['href']; $message->recipientName = $attendee['name']; if (!$attendee['newInstances']) { // If there are no instances the attendee is a part of, it // means the attendee was removed and we need to send him a // CANCEL. $message->method = 'CANCEL'; // Creating the new iCalendar body. $icalMsg = new VCalendar(); $icalMsg->METHOD = $message->method; $event = $icalMsg->add('VEVENT', array('UID' => $message->uid, 'SEQUENCE' => $message->sequence)); if (isset($calendar->VEVENT->SUMMARY)) { $event->add('SUMMARY', $calendar->VEVENT->SUMMARY->getValue()); } $event->add(clone $calendar->VEVENT->DTSTART); $org = $event->add('ORGANIZER', $eventInfo['organizer']); if ($eventInfo['organizerName']) { $org['CN'] = $eventInfo['organizerName']; } $event->add('ATTENDEE', $attendee['href'], array('CN' => $attendee['name'])); $message->significantChange = true; } else { // The attendee gets the updated event body $message->method = 'REQUEST'; // Creating the new iCalendar body. $icalMsg = new VCalendar(); $icalMsg->METHOD = $message->method; foreach ($calendar->select('VTIMEZONE') as $timezone) { $icalMsg->add(clone $timezone); } // We need to find out that this change is significant. If it's // not, systems may opt to not send messages. // // We do this based on the 'significantChangeHash' which is // some value that changes if there's a certain set of // properties changed in the event, or simply if there's a // difference in instances that the attendee is invited to. $message->significantChange = $attendee['forceSend'] === 'REQUEST' || array_keys($attendee['oldInstances']) != array_keys($attendee['newInstances']) || $oldEventInfo['significantChangeHash'] !== $eventInfo['significantChangeHash']; foreach ($attendee['newInstances'] as $instanceId => $instanceInfo) { $currentEvent = clone $eventInfo['instances'][$instanceId]; if ($instanceId === 'master') { // We need to find a list of events that the attendee // is not a part of to add to the list of exceptions. $exceptions = array(); foreach ($eventInfo['instances'] as $instanceId => $vevent) { if (!isset($attendee['newInstances'][$instanceId])) { $exceptions[] = $instanceId; } } // If there were exceptions, we need to add it to an // existing EXDATE property, if it exists. if ($exceptions) { if (isset($currentEvent->EXDATE)) { $currentEvent->EXDATE->setParts(array_merge($currentEvent->EXDATE->getParts(), $exceptions)); } else { $currentEvent->EXDATE = $exceptions; } } // Cleaning up any scheduling information that // shouldn't be sent along. unset($currentEvent->ORGANIZER['SCHEDULE-FORCE-SEND']); unset($currentEvent->ORGANIZER['SCHEDULE-STATUS']); foreach ($currentEvent->ATTENDEE as $attendee) { unset($attendee['SCHEDULE-FORCE-SEND']); unset($attendee['SCHEDULE-STATUS']); // We're adding PARTSTAT=NEEDS-ACTION to ensure that // iOS shows an "Inbox Item" if (!isset($attendee['PARTSTAT'])) { $attendee['PARTSTAT'] = 'NEEDS-ACTION'; } } } $icalMsg->add($currentEvent); } } $message->message = $icalMsg; $messages[] = $message; } return $messages; }
/** * create calendar object from VCalendar * @param VCalendar $vcalendar * @return $this */ public function fromVObject(VCalendar $vcalendar) { foreach ($this->icsMapper as $classvar => $icsproperty) { $setter = 'set' . ucfirst($classvar); $value = $vcalendar->select($icsproperty); if (!empty($value)) { $this->{$setter}(reset($value)); } } $tzIds = $vcalendar->select('X-WR-TIMEZONE'); $tzId = reset($tzIds); $tz = SabreUtility::getTimezoneFromVObject($vcalendar, $tzId); if ($tz) { $this->setTimezone($tz); } return $this; }
/** * This method is used in cases where an event got updated, and we * potentially need to send emails to attendees to let them know of updates * in the events. * * We will detect which attendees got added, which got removed and create * specific messages for these situations. * * @param VCalendar $calendar * @param array $eventInfo * @param array $oldEventInfo * @return array */ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, array $oldEventInfo) { // Merging attendee lists. $attendees = array(); foreach ($oldEventInfo['attendees'] as $attendee) { $attendees[$attendee['href']] = array('href' => $attendee['href'], 'oldInstances' => $attendee['instances'], 'newInstances' => array(), 'name' => $attendee['name']); } foreach ($eventInfo['attendees'] as $attendee) { if (isset($attendees[$attendee['href']])) { $attendees[$attendee['href']]['name'] = $attendee['name']; $attendees[$attendee['href']]['newInstances'] = $attendee['instances']; } else { $attendees[$attendee['href']] = array('href' => $attendee['href'], 'oldInstances' => array(), 'newInstances' => $attendee['instances'], 'name' => $attendee['name']); } } $messages = array(); foreach ($attendees as $attendee) { // An organizer can also be an attendee. We should not generate any // messages for those. if ($attendee['href'] === $eventInfo['organizer']) { continue; } $message = new Message(); $message->uid = $eventInfo['uid']; $message->component = 'VEVENT'; $message->sequence = $eventInfo['sequence']; $message->sender = $eventInfo['organizer']; $message->senderName = $eventInfo['organizerName']; $message->recipient = $attendee['href']; $message->recipientName = $attendee['name']; if (!$attendee['newInstances']) { // If there are no instances the attendee is a part of, it // means the attendee was removed and we need to send him a // CANCEL. $message->method = 'CANCEL'; // Creating the new iCalendar body. $icalMsg = new VCalendar(); $icalMsg->METHOD = $message->method; $event = $icalMsg->add('VEVENT', array('SEQUENCE' => $message->sequence, 'UID' => $message->uid)); $event->add('ATTENDEE', $attendee['href'], array('CN' => $attendee['name'])); $org = $event->add('ORGANIZER', $eventInfo['organizer']); if ($eventInfo['organizerName']) { $org['CN'] = $eventInfo['organizerName']; } } else { // The attendee gets the updated event body $message->method = 'REQUEST'; // Creating the new iCalendar body. $icalMsg = new VCalendar(); $icalMsg->METHOD = $message->method; foreach ($calendar->select('VTIMEZONE') as $timezone) { $icalMsg->add(clone $timezone); } foreach ($attendee['newInstances'] as $instanceId => $instanceInfo) { $currentEvent = clone $eventInfo['instances'][$instanceId]; if ($instanceId === 'master') { // We need to find a list of events that the attendee // is not a part of to add to the list of exceptions. $exceptions = array(); foreach ($eventInfo['instances'] as $instanceId => $vevent) { if (!isset($attendee['newInstances'][$instanceId])) { $exceptions[] = $instanceId; } } // If there were exceptions, we need to add it to an // existing EXDATE property, if it exists. if ($exceptions) { if (isset($currentEvent->EXDATE)) { $currentEvent->EXDATE->setParts(array_merge($currentEvent->EXDATE->getParts(), $exceptions)); } else { $currentEvent->EXDATE = $exceptions; } } } $icalMsg->add($currentEvent); } } $message->message = $icalMsg; $messages[] = $message; } return $messages; }
/** * extract timezone-data for a certain timezone from Component\VCalendar object * @param Component\VCalendar $vcalendar * @param string $timezoneId * @return Timezone|null */ public static function getTimezoneFromVObject(Component\VCalendar $vcalendar, $timezoneId) { foreach ($vcalendar->select('VTIMEZONE') as $vtimezone) { if ($vtimezone->TZID === $timezoneId) { return new Timezone($vtimezone); } } return null; }