/** * Initialize recurrence engine * * @param array The recurrence properties * @param object DateTime The recurrence start date */ public function init($recurrence, $start = null) { $this->recurrence = $recurrence; $this->engine = new Horde_Date_Recurrence($start); $this->engine->fromRRule20(libcalendaring::to_rrule($recurrence)); $this->set_start($start); if (is_array($recurrence['EXDATE'])) { foreach ($recurrence['EXDATE'] as $exdate) { if (is_a($exdate, 'DateTime')) { $this->engine->addException($exdate->format('Y'), $exdate->format('n'), $exdate->format('j')); } } } if (is_array($recurrence['RDATE'])) { foreach ($recurrence['RDATE'] as $rdate) { if (is_a($rdate, 'DateTime')) { $this->engine->addRDate($rdate->format('Y'), $rdate->format('n'), $rdate->format('j')); } } } }
/** * libcalendaring::to_rrule() */ function test_to_rrule() { $rrule = array('FREQ' => 'MONTHLY', 'BYDAY' => '2WE', 'INTERVAL' => 2, 'UNTIL' => new DateTime('2025-05-01 18:00:00 CEST')); $s = libcalendaring::to_rrule($rrule); $this->assertRegExp('/FREQ=' . $rrule['FREQ'] . '/', $s, "Recurrence Frequence"); $this->assertRegExp('/INTERVAL=' . $rrule['INTERVAL'] . '/', $s, "Recurrence Interval"); $this->assertRegExp('/BYDAY=' . $rrule['BYDAY'] . '/', $s, "Recurrence BYDAY"); $this->assertRegExp('/UNTIL=20250501T160000Z/', $s, "Recurrence End date (in UTC)"); }
/** * Build a valid iCal format block from the given event * * @param array Hash array with event/task properties from libkolab * @param object VCalendar object to append event to or false for directly sending data to stdout * @param callable Callback function to fetch attachment contents, false if no attachment export * @param object RECURRENCE-ID property when serializing a recurrence exception */ private function _to_ical($event, $vcal, $get_attachment, $recurrence_id = null) { $type = $event['_type'] ?: 'event'; $vcal_creator = new VObject\Component\VCalendar(); $ve = $vcal_creator->createComponent($this->type_component_map[$type]); $ve->add('UID', $event['uid']); // set DTSTAMP according to RFC 5545, 3.8.7.2. $dtstamp = !empty($event['changed']) && !empty($this->method) ? $event['changed'] : new DateTime(); $ve->add('DTSTAMP', $dtstamp); if ($event['allday']) { $ve->DTSTAMP['VALUE'] = 'DATE'; } if (!empty($event['created'])) { $ve->add('CREATED', $event['created']); } if (!empty($event['changed'])) { $ve->add('LAST-MODIFIED', $event['changed']); } if (!empty($event['start'])) { $ve->add('DTSTART', $event['start']); } if ($event['allday']) { $ve->DTSTART['VALUE'] = 'DATE'; } if (!empty($event['end'])) { $ve->add('DTEND', $event['end']); } if ($event['allday']) { $ve->DTEND['VALUE'] = 'DATE'; } if (!empty($event['due'])) { $ve->add('DUE', $event['due']); } // we're exporting a recurrence instance only if (!$recurrence_id && $event['recurrence_date'] && $event['recurrence_date'] instanceof DateTime) { $recurrence_id = $vcal_creator->createProperty('RECURRENCE-ID'); $recurrence_id->setDateTime($event['recurrence_date']); if ($event['allday']) { $recurrence_id['VALUE'] = 'DATE'; } if ($event['thisandfuture']) { $recurrence_id->add('RANGE', 'THISANDFUTURE'); } } if ($recurrence_id) { $ve->add($recurrence_id); } $ve->add('SUMMARY', $event['title']); if ($event['location']) { $ve->add('LOCATION', $event['location']); } if ($event['description']) { $ve->add('DESCRIPTION', strtr($event['description'], array("\r\n" => "\n", "\r" => "\n"))); } // normalize line endings if (isset($event['sequence'])) { $ve->add('SEQUENCE', $event['sequence']); } if ($event['recurrence'] && !$recurrence_id) { $exdates = $rdates = null; if (isset($event['recurrence']['EXDATE'])) { $exdates = $event['recurrence']['EXDATE']; unset($event['recurrence']['EXDATE']); // don't serialize EXDATEs into RRULE value } if (isset($event['recurrence']['RDATE'])) { $rdates = $event['recurrence']['RDATE']; unset($event['recurrence']['RDATE']); // don't serialize RDATEs into RRULE value } if ($event['recurrence']['FREQ']) { $ve->add('RRULE', libcalendaring::to_rrule($event['recurrence'], (bool) $event['allday'])); } // add EXDATEs each one per line (for Thunderbird Lightning) if (is_array($exdates)) { foreach ($exdates as $ex) { $ve->add('EXDATE', $ex); } } // add RDATEs if (is_array($rdates) && !empty($rdates)) { $ve->RDATE = $rdates; } } if ($event['categories']) { $ve->add('CATEGORIES', (array) $event['categories']); } if (!empty($event['free_busy'])) { $ve->add('TRANSP', $event['free_busy'] == 'free' ? 'TRANSPARENT' : 'OPAQUE'); // for Outlook clients we provide the X-MICROSOFT-CDO-BUSYSTATUS property if (stripos($this->agent, 'outlook') !== false) { $ve->add('X-MICROSOFT-CDO-BUSYSTATUS', $event['free_busy'] == 'outofoffice' ? 'OOF' : strtoupper($event['free_busy'])); } } if ($event['priority']) { $ve->add('PRIORITY', $event['priority']); } if ($event['cancelled']) { $ve->add('STATUS', 'CANCELLED'); } else { if ($event['free_busy'] == 'tentative') { $ve->add('STATUS', 'TENTATIVE'); } else { if ($event['complete'] == 100) { $ve->add('STATUS', 'COMPLETED'); } else { if (!empty($event['status'])) { $ve->add('STATUS', $event['status']); } } } } if (!empty($event['sensitivity'])) { $ve->add('CLASS', strtoupper($event['sensitivity'])); } if (!empty($event['complete'])) { $ve->add('PERCENT-COMPLETE', intval($event['complete'])); } // Apple iCal and BusyCal required the COMPLETED date to be set in order to consider a task complete if ($event['status'] == 'COMPLETED' || $event['complete'] == 100) { $ve->add('COMPLETED', $event['changed'] ?: new DateTime('now - 1 hour'), true); } if ($event['valarms']) { foreach ($event['valarms'] as $alarm) { $va = $vcal_creator->createComponent('VALARM'); $va->ACTION = $alarm['action']; if ($alarm['trigger'] instanceof DateTime) { $va->add('TRIGGER', $alarm['trigger']); } else { $va->add('TRIGGER', $alarm['trigger']); if (strtoupper($alarm['related']) == 'END') { $va->TRIGGER['RELATED'] = 'END'; } } if ($alarm['action'] == 'EMAIL') { foreach ((array) $alarm['attendees'] as $attendee) { $va->add('ATTENDEE', 'mailto:' . $attendee); } } if ($alarm['description']) { $va->add('DESCRIPTION', $alarm['description'] ?: $event['title']); } if ($alarm['summary']) { $va->add('SUMMARY', $alarm['summary']); } if ($alarm['duration']) { $va->add('DURATION', $alarm['duration']); $va->add('REPEAT', intval($alarm['repeat'])); } if ($alarm['uri']) { $va->add('ATTACH', $alarm['uri'], array('VALUE' => 'URI')); } $ve->add($va); } } else { if ($event['alarms']) { $va = $vcal_creator->createComponent('VALARM'); list($trigger, $va->action) = explode(':', $event['alarms']); $val = libcalendaring::parse_alarm_value($trigger); if ($val[3]) { $va->add('TRIGGER', $val[3]); } else { if ($val[0] instanceof DateTime) { $va->add('TRIGGER', $val[0]); } } $ve->add($va); } } foreach ((array) $event['attendees'] as $attendee) { if ($attendee['role'] == 'ORGANIZER') { if (empty($event['organizer'])) { $event['organizer'] = $attendee; } } else { if (!empty($attendee['email'])) { if (isset($attendee['rsvp'])) { $attendee['rsvp'] = $attendee['rsvp'] ? 'TRUE' : null; } $ve->add('ATTENDEE', 'mailto:' . $attendee['email'], array_filter(self::map_keys($attendee, $this->attendee_keymap))); } } } if ($event['organizer']) { $ve->add('ORGANIZER', 'mailto:' . $event['organizer']['email'], self::map_keys($event['organizer'], array('name' => 'CN'))); } foreach ((array) $event['url'] as $url) { if (!empty($url)) { $ve->add('URL', $url); } } if (!empty($event['parent_id'])) { $ve->add('RELATED-TO', $event['parent_id'], array('RELTYPE' => 'PARENT')); } if ($event['comment']) { $ve->add('COMMENT', $event['comment']); } $memory_limit = parse_bytes(ini_get('memory_limit')); // export attachments if (!empty($event['attachments'])) { foreach ((array) $event['attachments'] as $attach) { // check available memory and skip attachment export if we can't buffer it // @todo: use rcube_utils::mem_check() if (is_callable($get_attachment) && $memory_limit > 0 && ($memory_used = function_exists('memory_get_usage') ? memory_get_usage() : 16 * 1024 * 1024) && $attach['size'] && $memory_used + $attach['size'] * 3 > $memory_limit) { continue; } // embed attachments using the given callback function if (is_callable($get_attachment) && ($data = call_user_func($get_attachment, $attach['id'], $event))) { // embed attachments for iCal $ve->add('ATTACH', base64_encode($data), array_filter(array('VALUE' => 'BINARY', 'ENCODING' => 'BASE64', 'FMTTYPE' => $attach['mimetype'], 'X-LABEL' => $attach['name']))); unset($data); // attempt to free memory } else { if (!empty($this->attach_uri)) { $ve->add('ATTACH', strtr($this->attach_uri, array('{{id}}' => urlencode($attach['id']), '{{name}}' => urlencode($attach['name']), '{{mimetype}}' => urlencode($attach['mimetype']))), array('FMTTYPE' => $attach['mimetype'], 'VALUE' => 'URI')); } } } } foreach ((array) $event['links'] as $uri) { $ve->add('ATTACH', $uri); } // add custom properties foreach ((array) $event['x-custom'] as $prop) { $ve->add($prop[0], $prop[1]); } // append to vcalendar container if ($vcal) { $vcal->add($ve); } else { // serialize and send to stdout echo $ve->serialize(); } // append recurrence exceptions if (is_array($event['recurrence']) && $event['recurrence']['EXCEPTIONS']) { foreach ($event['recurrence']['EXCEPTIONS'] as $ex) { $exdate = $ex['recurrence_date'] ?: $ex['start']; $recurrence_id = $vcal_crator->createProperty('RECURRENCE-ID'); $recurrence_id->setDateTime($exdate); if ($event['allday']) { $recurrence_id['VALUE'] = 'DATE'; } if ($ex['thisandfuture']) { $recurrence_id->add('RANGE', 'THISANDFUTURE'); } $this->_to_ical($ex, $vcal, $get_attachment, $recurrence_id); } } }
/** * Convert save data to be used in SQL statements */ private function _save_preprocess($event, $action = 'new') { if (!$event['start'] || !is_a($event['start'], 'DateTime')) { return $event; } if (!$event['end']) { $event['end'] = $event['start']; } $event['duration'] = $event['end']->format('U') - $event['start']->format('U'); if ($event['allday']) { $event['start']->setTimezone(new DateTimeZone('UTC')); $event['start']->_dateonly = true; $event['start']->setDate($event['start']->format('Y'), $event['start']->format('m'), $event['start']->format('d')); $event['start']->setTime(0, 0, 0); $event['end']->setTimezone(new DateTimeZone('UTC')); $event['end']->_dateonly = true; $event['end']->setDate($event['end']->format('Y'), $event['end']->format('m'), $event['end']->format('d')); $event['end']->setTime(0, 0, 0); if ($event['recurrence_date']) { $event['recurrence_date']->setTimezone(new DateTimeZone('UTC')); $event['recurrence_date']->_dateonly = true; $event['recurrence_date']->setDate($event['recurrence_date']->format('Y'), $event['recurrence_date']->format('m'), $event['recurrence_date']->format('d')); $event['recurrence_date']->setTime(0, 0, 0); } if (is_array($event['recurrence']) && is_array($event['recurrence']['RDATE'])) { foreach ($event['recurrence']['RDATE'] as $idx => $rdate) { $event['recurrence']['RDATE'][$idx]->setTimezone(new DateTimeZone('UTC')); $event['recurrence']['RDATE'][$idx]->_dateonly = true; $event['recurrence']['RDATE'][$idx]->setDate($rdate->format('Y'), $rdate->format('m'), $rdate->format('d')); $event['recurrence']['RDATE'][$idx]->setTime(0, 0, 0); } } if (is_array($event['recurrence']) && is_array($event['recurrence']['EXDATE'])) { foreach ($event['recurrence']['EXDATE'] as $idx => $exdate) { $event['recurrence']['EXDATE'][$idx]->setTimezone(new DateTimeZone('UTC')); $event['recurrence']['EXDATE'][$idx]->_dateonly = true; $event['recurrence']['EXDATE'][$idx]->setDate($exdate->format('Y'), $exdate->format('m'), $exdate->format('d')); $event['recurrence']['EXDATE'][$idx]->setTime(0, 0, 0); } } } if ($event['categories'] && is_array($event['categories'])) { $event['categories'] = implode(',', $event['categories']); } // compose vcalendar-style recurrencue rule from structured data $rrule = $event['recurrence'] ? libcalendaring::to_rrule($event['recurrence']) : ''; $event['_recurrence'] = rtrim($rrule, ';'); $event['free_busy'] = intval($this->free_busy_map[strtolower($event['free_busy'])]); $event['sensitivity'] = intval($this->sensitivity_map[strtolower($event['sensitivity'])]); if (isset($event['allday'])) { $event['all_day'] = $event['allday'] ? 1 : 0; } // compute absolute time to notify the user $event['notifyat'] = $this->_get_notification($event, $action); // process event attendees $_attendees = ''; foreach ((array) $event['attendees'] as $attendee) { if (is_array($attendee)) { if (!$attendee['name'] && !$attendee['email']) { continue; } $_attendees .= 'NAME="' . addcslashes($attendee['name'], '"') . '"' . ';STATUS=' . $attendee['status'] . ';ROLE=' . $attendee['role'] . ';EMAIL=' . $attendee['email'] . "\n"; } } $event['attendees'] = rtrim($_attendees); return $event; }
/** * Convert save data to be used in SQL statements */ private function _save_preprocess($event) { // shift dates to server's timezone (except for all-day events) if (!$event['allday']) { $event['start'] = clone $event['start']; $event['start']->setTimezone($this->server_timezone); $event['end'] = clone $event['end']; $event['end']->setTimezone($this->server_timezone); } // compose vcalendar-style recurrencue rule from structured data $rrule = $event['recurrence'] ? libcalendaring::to_rrule($event['recurrence']) : ''; $event['_recurrence'] = rtrim($rrule, ';'); $event['free_busy'] = intval($this->free_busy_map[strtolower($event['free_busy'])]); $event['sensitivity'] = intval($this->sensitivity_map[strtolower($event['sensitivity'])]); if ($event['free_busy'] == 'tentative') { $event['status'] = 'TENTATIVE'; } if (isset($event['allday'])) { $event['all_day'] = $event['allday'] ? 1 : 0; } // compute absolute time to notify the user $event['notifyat'] = $this->_get_notification($event); if (is_array($event['valarms'])) { $event['alarms'] = $this->serialize_alarms($event['valarms']); } // process event attendees if (!empty($event['attendees'])) { $event['attendees'] = json_encode((array) $event['attendees']); } else { $event['attendees'] = ''; } return $event; }
/** * Build a valid iCal format block from the given event * * @param array Hash array with event/task properties from libkolab * @param object VCalendar object to append event to or false for directly sending data to stdout * @param callable Callback function to fetch attachment contents, false if no attachment export * @param object RECURRENCE-ID property when serializing a recurrence exception */ private function _to_ical($event, $vcal, $get_attachment, $recurrence_id = null) { $type = $event['_type'] ?: 'event'; $ve = VObject\Component::create($this->type_component_map[$type]); $ve->add('UID', $event['uid']); // set DTSTAMP according to RFC 5545, 3.8.7.2. $dtstamp = !empty($event['changed']) && !empty($this->method) ? $event['changed'] : new DateTime(); $ve->add(self::datetime_prop('DTSTAMP', $dtstamp, true)); // all-day events end the next day if ($event['allday'] && !empty($event['end'])) { $event['end'] = clone $event['end']; $event['end']->add(new \DateInterval('P1D')); $event['end']->_dateonly = true; } if (!empty($event['created'])) { $ve->add(self::datetime_prop('CREATED', $event['created'], true)); } if (!empty($event['changed'])) { $ve->add(self::datetime_prop('LAST-MODIFIED', $event['changed'], true)); } if (!empty($event['start'])) { $ve->add(self::datetime_prop('DTSTART', $event['start'], false, (bool) $event['allday'])); } if (!empty($event['end'])) { $ve->add(self::datetime_prop('DTEND', $event['end'], false, (bool) $event['allday'])); } if (!empty($event['due'])) { $ve->add(self::datetime_prop('DUE', $event['due'], false)); } if ($recurrence_id) { $ve->add($recurrence_id); } $ve->add('SUMMARY', $event['title']); if ($event['location']) { $ve->add($this->is_apple() ? new vobject_location_property('LOCATION', $event['location']) : new VObject\Property('LOCATION', $event['location'])); } if ($event['description']) { $ve->add('DESCRIPTION', strtr($event['description'], array("\r\n" => "\n", "\r" => "\n"))); } // normalize line endings if ($event['sequence']) { $ve->add('SEQUENCE', $event['sequence']); } if ($event['recurrence'] && !$recurrence_id) { if ($exdates = $event['recurrence']['EXDATE']) { unset($event['recurrence']['EXDATE']); // don't serialize EXDATEs into RRULE value } if ($rdates = $event['recurrence']['RDATE']) { unset($event['recurrence']['RDATE']); // don't serialize RDATEs into RRULE value } if ($event['recurrence']['FREQ']) { $ve->add('RRULE', libcalendaring::to_rrule($event['recurrence'])); } // add EXDATEs each one per line (for Thunderbird Lightning) // Begin mod by Rosali (handle EXDATE the same as RDATE) if (!empty($exdates)) { $exdates = array_values($exdates); $sample = self::datetime_prop('EXDATE', $exdates[0]); $edprop = new VObject\Property\MultiDateTime('EXDATE', null); $edprop->setDateTimes($exdates, $sample->getDateType()); $ve->add($edprop); } // End mod by Rosali // add RDATEs if (!empty($rdates)) { $sample = self::datetime_prop('RDATE', $rdates[0]); $rdprop = new VObject\Property\MultiDateTime('RDATE', null); $rdprop->setDateTimes($rdates, $sample->getDateType()); $ve->add($rdprop); } } if ($event['categories']) { $cat = VObject\Property::create('CATEGORIES'); $cat->setParts((array) $event['categories']); $ve->add($cat); } if (!empty($event['free_busy'])) { $ve->add('TRANSP', $event['free_busy'] == 'free' ? 'TRANSPARENT' : 'OPAQUE'); // for Outlook clients we provide the X-MICROSOFT-CDO-BUSYSTATUS property if (stripos($this->agent, 'outlook') !== false) { $ve->add('X-MICROSOFT-CDO-BUSYSTATUS', $event['free_busy'] == 'outofoffice' ? 'OOF' : strtoupper($event['free_busy'])); } } // Begin mod by Rosali (adjust float value to percent) if ($event['complete'] && $event['complete'] <= 1) { $event['complete'] = round($event['complete'] * 100, 0); } // End mod by Rosali if ($event['priority']) { $ve->add('PRIORITY', $event['priority']); } if ($event['cancelled']) { $ve->add('STATUS', 'CANCELLED'); } else { if ($event['free_busy'] == 'tentative') { $ve->add('STATUS', 'TENTATIVE'); } else { if ($event['complete'] == 100) { $ve->add('STATUS', 'COMPLETED'); } else { if ($event['status']) { $ve->add('STATUS', $event['status']); } } } } // End mod by Rosali if (!empty($event['sensitivity'])) { $ve->add('CLASS', strtoupper($event['sensitivity'])); } if (!empty($event['complete'])) { $ve->add('PERCENT-COMPLETE', intval($event['complete'])); // Apple iCal required the COMPLETED date to be set in order to consider a task complete if ($event['complete'] == 100) { $ve->add(self::datetime_prop('COMPLETED', $event['changed'] ?: new DateTime('now - 1 hour'), true)); } } if ($event['alarms']) { $va = VObject\Component::create('VALARM'); list($trigger, $va->action) = explode(':', $event['alarms']); $val = libcalendaring::parse_alaram_value($trigger); $period = $val[1] && preg_match('/[HMS]$/', $val[1]) ? 'PT' : 'P'; if ($val[1]) { $va->add('TRIGGER', preg_replace('/^([-+])P?T?(.+)/', "\\1{$period}\\2", $trigger)); } else { $va->add('TRIGGER', gmdate('Ymd\\THis\\Z', $val[0]), array('VALUE' => 'DATE-TIME')); } $ve->add($va); } foreach ((array) $event['attendees'] as $attendee) { if (is_array($attendee)) { // Mod by Rosali (check type array) if ($attendee['role'] == 'ORGANIZER') { if (empty($event['organizer'])) { $event['organizer'] = $attendee; } } else { if (!empty($attendee['email'])) { $attendee['rsvp'] = $attendee['rsvp'] ? 'TRUE' : null; $ve->add('ATTENDEE', 'mailto:' . $attendee['email'], array_filter(self::map_keys($attendee, $this->attendee_keymap))); } } } } if ($event['organizer']) { $ve->add('ORGANIZER', 'mailto:' . $event['organizer']['email'], self::map_keys($event['organizer'], array('name' => 'CN'))); } foreach ((array) $event['url'] as $url) { if (!empty($url)) { $ve->add('URL', $url); } } if (!empty($event['parent_id'])) { $ve->add('RELATED-TO', $event['parent_id'], array('RELTYPE' => 'PARENT')); } // export attachments if (!empty($event['attachments'])) { foreach ((array) $event['attachments'] as $attach) { // check available memory and skip attachment export if we can't buffer it if (is_callable($get_attachment) && $memory_limit > 0 && ($memory_used = function_exists('memory_get_usage') ? memory_get_usage() : 16 * 1024 * 1024) && $attach['size'] && $memory_used + $attach['size'] * 3 > $memory_limit) { continue; } // embed attachments using the given callback function if (is_callable($get_attachment) && ($data = call_user_func($get_attachment, $attach['id'], $event))) { // embed attachments for iCal $ve->add('ATTACH', base64_encode($data), array_filter(array('VALUE' => 'BINARY', 'ENCODING' => 'BASE64', 'FMTTYPE' => $attach['mimetype'], 'X-LABEL' => $attach['name']))); unset($data); // attempt to free memory } else { if (!empty($this->attach_uri)) { $ve->add('ATTACH', strtr($this->attach_uri, array('{{id}}' => urlencode($attach['id']), '{{name}}' => urlencode($attach['name']), '{{mimetype}}' => urlencode($attach['mimetype']))), array('FMTTYPE' => $attach['mimetype'], 'VALUE' => 'URI')); } } } } foreach ((array) $event['links'] as $uri) { $ve->add('ATTACH', $uri); } // add custom properties foreach ((array) $event['x-custom'] as $prop) { $ve->add($prop[0], $prop[1]); } // append to vcalendar container if ($vcal) { $vcal->add($ve); } else { // serialize and send to stdout echo $ve->serialize(); } // append recurrence exceptions if (isset($event['recurrence']['EXCEPTIONS']) && is_array($event['recurrence']['EXCEPTIONS'])) { foreach ($event['recurrence']['EXCEPTIONS'] as $ex) { $exdate = clone $event['start']; $exdate->setDate($ex['start']->format('Y'), $ex['start']->format('n'), $ex['start']->format('j')); $recurrence_id = self::datetime_prop('RECURRENCE-ID', $exdate, true); // if ($ex['thisandfuture']) // not supported by any client :-( // $recurrence_id->add('RANGE', 'THISANDFUTURE'); $this->_to_ical($ex, $vcal, $get_attachment, $recurrence_id); } } }
/** * Convert save data to be used in SQL statements */ private function _save_preprocess($event) { // shift dates to server's timezone (except for all-day events) if (!$event['allday']) { $event['start'] = clone $event['start']; $event['start']->setTimezone($this->server_timezone); $event['end'] = clone $event['end']; $event['end']->setTimezone($this->server_timezone); } // compose vcalendar-style recurrencue rule from structured data $rrule = $event['recurrence'] ? libcalendaring::to_rrule($event['recurrence']) : ''; $event['_recurrence'] = rtrim($rrule, ';'); $event['free_busy'] = intval($this->free_busy_map[strtolower($event['free_busy'])]); $event['sensitivity'] = intval($this->sensitivity_map[strtolower($event['sensitivity'])]); if ($event['free_busy'] == 'tentative') { $event['status'] = 'TENTATIVE'; } if (isset($event['allday'])) { $event['all_day'] = $event['allday'] ? 1 : 0; } // compute absolute time to notify the user $event['notifyat'] = $this->_get_notification($event); if (is_array($event['valarms'])) { $event['alarms'] = $this->serialize_alarms($event['valarms']); } // process event attendees $_attendees = ''; foreach ((array) $event['attendees'] as $attendee) { if (!$attendee['name'] && !$attendee['email']) { continue; } $_attendees .= 'NAME="' . addcslashes($attendee['name'], '"') . '"' . ';STATUS=' . $attendee['status'] . ';ROLE=' . $attendee['role'] . ';EMAIL=' . $attendee['email'] . "\n"; } $event['attendees'] = rtrim($_attendees); return $event; }