/**
  * 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'));
             }
         }
     }
 }
Example #2
0
 /**
  * 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)");
 }
Example #3
0
 /**
  * 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;
 }
Example #6
0
 /**
  * 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);
         }
     }
 }
Example #7
0
 /**
  * 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;
 }