Exemplo n.º 1
0
 /**
  *  Default constructor for calendar synchronization adapter.
  *
  * @param int Calendar id.
  * @param array Hash array with ical properties:
  *   url: Absolute URL to iCAL resource.
  */
 public function __construct($cal_id, $props)
 {
     $this->ical = libcalendaring::get_ical();
     $this->cal_id = $cal_id;
     $this->url = $props["url"];
     $this->user = isset($props["user"]) ? $props["user"] : null;
     $this->pass = isset($props["pass"]) ? $props["pass"] : null;
     $this->sync = isset($props["sync"]) ? $props["sync"] : 0;
     $this->etag = isset($props["tag"]) ? $props["tag"] : null;
 }
 /**
  * Get the next recurring instance of this event
  *
  * @return mixed Array with event properties or False if recurrence ended
  */
 public function next_instance()
 {
     if ($next_start = $this->next()) {
         $next = $this->event;
         $next['start'] = $next_start;
         if ($this->duration) {
             $next['end'] = clone $next_start;
             $next['end']->add($this->duration);
         }
         $next['recurrence_date'] = clone $next_start;
         $next['_instance'] = libcalendaring::recurrence_instance_identifier($next);
         unset($next['_formatobj']);
         return $next;
     }
     return false;
 }
 /**
  * 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'));
             }
         }
     }
 }
Exemplo n.º 4
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);
         }
     }
 }
Exemplo n.º 5
0
 /**
  * Render localized text describing the recurrence rule of an event
  */
 private function _recurrence_text($rrule)
 {
     // derive missing FREQ and INTERVAL from RDATE list
     if (empty($rrule['FREQ']) && !empty($rrule['RDATE'])) {
         $first = $rrule['RDATE'][0];
         $second = $rrule['RDATE'][1];
         $third = $rrule['RDATE'][2];
         if (is_a($first, 'DateTime') && is_a($second, 'DateTime')) {
             $diff = $first->diff($second);
             foreach (array('y' => 'YEARLY', 'm' => 'MONTHLY', 'd' => 'DAILY') as $k => $freq) {
                 if ($diff->{$k} != 0) {
                     $rrule['FREQ'] = $freq;
                     $rrule['INTERVAL'] = $diff->{$k};
                     // verify interval with next item
                     if (is_a($third, 'DateTime')) {
                         $diff2 = $second->diff($third);
                         if ($diff2->{$k} != $diff->{$k}) {
                             unset($rrule['INTERVAL']);
                         }
                     }
                     break;
                 }
             }
         }
         if (!$rrule['INTERVAL']) {
             $rrule['FREQ'] = 'RDATE';
         }
         $rrule['UNTIL'] = end($rrule['RDATE']);
     }
     // TODO: finish this
     $freq = sprintf('%s %d ', $this->gettext('every'), $rrule['INTERVAL']);
     $details = '';
     switch ($rrule['FREQ']) {
         case 'DAILY':
             $freq .= $this->gettext('days');
             break;
         case 'WEEKLY':
             $freq .= $this->gettext('weeks');
             break;
         case 'MONTHLY':
             $freq .= $this->gettext('months');
             break;
         case 'YEARLY':
             $freq .= $this->gettext('years');
             break;
     }
     if ($rrule['INTERVAL'] <= 1) {
         $freq = $this->gettext(strtolower($rrule['FREQ']));
     }
     if ($rrule['COUNT']) {
         $until = $this->gettext(array('name' => 'forntimes', 'vars' => array('nr' => $rrule['COUNT'])));
     } else {
         if ($rrule['UNTIL']) {
             $until = $this->gettext('recurrencend') . ' ' . format_date($rrule['UNTIL'], libcalendaring::to_php_date_format($this->rc->config->get('calendar_date_format', $this->defaults['calendar_date_format'])));
         } else {
             $until = $this->gettext('forever');
         }
     }
     return rtrim($freq . $details . ', ' . $until);
 }
 /**
  * Load iCalendar functions
  */
 public function get_ical()
 {
     if (!$this->ical) {
         $this->ical = libcalendaring::get_ical();
     }
     return $this->ical;
 }
Exemplo n.º 7
0
 /**
  * Feedback after showing/sending an alarm notification
  *
  * @see calendar_driver::dismiss_alarm()
  */
 public function dismiss_alarm($event_id, $snooze = 0)
 {
     $notify_at = null;
     //default
     $stz = date_default_timezone_get();
     date_default_timezone_set($this->cal->timezone->getName());
     $event = $this->get_master(array('id' => $event_id));
     $dismissed_alarm = $event['notifyat'];
     $dismissed_alarm = strtotime($dismissed_alarm) <= time() ? $dismissed_alarm : null;
     if ($snooze > 0) {
         $notify_at = date(self::DB_DATE_FORMAT, time() + $snooze);
     } else {
         if ($event['recurrence'] && $event['id'] == $event_id) {
             if ($event['recurrence']) {
                 $base_alarm = libcalendaring::get_next_alarm($event);
                 $before = $event['start']->format('U') - $base_alarm['time'];
                 $this->_get_recurrences($event, time() + $before, false, 'alarms');
                 if ($this->last_clone) {
                     $dismissed = $event['notifyat'];
                     if (substr($event['alarms'], 0, 1) == '@') {
                         $notify_at = null;
                     } else {
                         $notify_at = $this->last_clone['notifyat'];
                     }
                 }
             }
         }
     }
     $now = gmdate(self::DB_DATE_FORMAT);
     if ($dismissed_alarm) {
         $query = $this->rc->db->query("UPDATE " . $this->_get_table($this->db_events) . "\n        SET changed=?, alarms=?, notifyat=?, dismissed=?\n        WHERE event_id=?\n        AND calendar_id IN (" . $this->calendar_ids . ")", $now, $event['alarms'], $notify_at, $dismissed_alarm, $event_id);
     } else {
         $query = $this->rc->db->query("UPDATE " . $this->_get_table($this->db_events) . "\n        SET changed=?, alarms=?, notifyat=?\n        WHERE event_id=?\n        AND calendar_id IN (" . $this->calendar_ids . ")", $now, $event['alarms'], $notify_at, $event_id);
     }
     date_default_timezone_set($stz);
     return $this->rc->db->affected_rows($query);
 }
Exemplo n.º 8
0
 /**
  * 
  */
 public function mail_messages_list($p)
 {
     if (in_array('attachment', (array) $p['cols']) && !empty($p['messages'])) {
         foreach ($p['messages'] as $header) {
             $part = new StdClass();
             $part->mimetype = $header->ctype;
             if (libcalendaring::part_is_vcalendar($part)) {
                 $header->list_flags['attachmentClass'] = 'ical';
             } else {
                 if (in_array($header->ctype, array('multipart/alternative', 'multipart/mixed'))) {
                     // TODO: fetch bodystructure and search for ical parts. Maybe too expensive?
                     if (!empty($header->structure) && is_array($header->structure->parts)) {
                         foreach ($header->structure->parts as $part) {
                             if (libcalendaring::part_is_vcalendar($part) && !empty($part->ctype_parameters['method'])) {
                                 $header->list_flags['attachmentClass'] = 'ical';
                                 break;
                             }
                         }
                     }
                 }
             }
         }
     }
 }
 /**
  * Compute absolute time to notify the user
  */
 private function _get_notification($task)
 {
     if ($task['alarms'] && $task['complete'] < 1 || strpos($task['alarms'], '@') !== false) {
         $alarm = libcalendaring::get_next_alarm($task, 'task');
         if ($alarm['time'] && $alarm['action'] == 'DISPLAY') {
             return date('Y-m-d H:i:s', $alarm['time']);
         }
     }
     return null;
 }
 /**
  * Helper method to decode a serialized list of alarms
  */
 private function unserialize_alarms($alarms)
 {
     // decode json serialized alarms
     if ($alarms && $alarms[0] == '[') {
         $valarms = json_decode($alarms, true);
         foreach ($valarms as $i => $alarm) {
             if ($alarm['trigger'][0] == '@') {
                 try {
                     $valarms[$i]['trigger'] = new DateTime(substr($alarm['trigger'], 1));
                 } catch (Exception $e) {
                     unset($valarms[$i]);
                 }
             }
         }
     } else {
         if (strlen($alarms)) {
             list($trigger, $action) = explode(':', $alarms, 2);
             if ($trigger = libcalendaring::parse_alarm_value($trigger)) {
                 $valarms = array(array('action' => $action, 'trigger' => $trigger[3] ?: $trigger[0]));
             }
         }
     }
     return $valarms;
 }
Exemplo n.º 11
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);
         }
     }
 }
 /**
  * Get a list of pending alarms to be displayed to the user
  *
  * @param  integer Current time (unix timestamp)
  * @param  mixed   List of list IDs to show alarms for (either as array or comma-separated string)
  * @return array   A list of alarms, each encoded as hash array with task properties
  * @see tasklist_driver::pending_alarms()
  */
 public function pending_alarms($time, $lists = null)
 {
     $interval = 300;
     $time -= $time % 60;
     $slot = $time;
     $slot -= $slot % $interval;
     $last = $time - max(60, $this->rc->config->get('refresh_interval', 0));
     $last -= $last % $interval;
     // only check for alerts once in 5 minutes
     if ($last == $slot) {
         return array();
     }
     if ($lists && is_string($lists)) {
         $lists = explode(',', $lists);
     }
     $time = $slot + $interval;
     $candidates = array();
     $query = array(array('tags', '=', 'x-has-alarms'), array('tags', '!=', 'x-complete'));
     foreach ($this->lists as $lid => $list) {
         // skip lists with alarms disabled
         if (!$list['showalarms'] || $lists && !in_array($lid, $lists)) {
             continue;
         }
         $folder = $this->get_folder($lid);
         foreach ($folder->select($query) as $record) {
             if (!($record['valarms'] || $record['alarms']) || $record['status'] == 'COMPLETED' || $record['complete'] == 100) {
                 // don't trust query :-)
                 continue;
             }
             $task = $this->_to_rcube_task($record, $lid, false);
             // add to list if alarm is set
             $alarm = libcalendaring::get_next_alarm($task, 'task');
             if ($alarm && $alarm['time'] && $alarm['time'] <= $time && in_array($alarm['action'], $this->alarm_types)) {
                 $id = $alarm['id'];
                 // use alarm-id as primary identifier
                 $candidates[$id] = array('id' => $id, 'title' => $task['title'], 'date' => $task['date'], 'time' => $task['time'], 'notifyat' => $alarm['time'], 'action' => $alarm['action']);
             }
         }
     }
     // get alarm information stored in local database
     if (!empty($candidates)) {
         $alarm_ids = array_map(array($this->rc->db, 'quote'), array_keys($candidates));
         $result = $this->rc->db->query("SELECT *" . " FROM " . $this->rc->db->table_name('kolab_alarms', true) . " WHERE `alarm_id` IN (" . join(',', $alarm_ids) . ")" . " AND `user_id` = ?", $this->rc->user->ID);
         while ($result && ($rec = $this->rc->db->fetch_assoc($result))) {
             $dbdata[$rec['alarm_id']] = $rec;
         }
     }
     $alarms = array();
     foreach ($candidates as $id => $task) {
         // skip dismissed
         if ($dbdata[$id]['dismissed']) {
             continue;
         }
         // snooze function may have shifted alarm time
         $notifyat = $dbdata[$id]['notifyat'] ? strtotime($dbdata[$id]['notifyat']) : $task['notifyat'];
         if ($notifyat <= $time) {
             $alarms[] = $task;
         }
     }
     return $alarms;
 }
 /**
  * Create instances of a recurring event
  *
  * @param array  Hash array with event properties
  * @param object DateTime Start date of the recurrence window
  * @param object DateTime End date of the recurrence window
  * @return array List of recurring event instances
  */
 public function get_recurring_events($event, $start, $end = null)
 {
     $events = array();
     if ($event['recurrence']) {
         // include library class
         require_once dirname(__FILE__) . '/../lib/calendar_recurrence.php';
         $rcmail = rcmail::get_instance();
         $recurrence = new calendar_recurrence($rcmail->plugins->get_plugin('calendar'), $event);
         $recurrence_id_format = libcalendaring::recurrence_id_format($event);
         // determine a reasonable end date if none given
         if (!$end) {
             switch ($event['recurrence']['FREQ']) {
                 case 'YEARLY':
                     $intvl = 'P100Y';
                     break;
                 case 'MONTHLY':
                     $intvl = 'P20Y';
                     break;
                 default:
                     $intvl = 'P10Y';
                     break;
             }
             $end = clone $event['start'];
             $end->add(new DateInterval($intvl));
         }
         $i = 0;
         while ($next_event = $recurrence->next_instance()) {
             // add to output if in range
             if ($next_event['start'] <= $end && $next_event['end'] >= $start) {
                 $next_event['_instance'] = $next_event['start']->format($recurrence_id_format);
                 $next_event['id'] = $next_event['uid'] . '-' . $exception['_instance'];
                 $next_event['recurrence_id'] = $event['uid'];
                 $events[] = $next_event;
             } else {
                 if ($next_event['start'] > $end) {
                     // stop loop if out of range
                     break;
                 }
             }
             // avoid endless recursion loops
             if (++$i > 1000) {
                 break;
             }
         }
     }
     return $events;
 }
Exemplo n.º 14
0
 /**
  * Set CSS class according to the event's attendde partstat
  */
 public static function add_partstat_class($event, $partstats, $user = null)
 {
     // set classes according to PARTSTAT
     if (is_array($event['attendees'])) {
         $user_emails = libcalendaring::get_instance()->get_user_emails($user);
         $partstat = 'UNKNOWN';
         foreach ($event['attendees'] as $attendee) {
             if (in_array($attendee['email'], $user_emails)) {
                 $partstat = $attendee['status'];
                 break;
             }
         }
         if (in_array($partstat, $partstats)) {
             $event['className'] = trim($event['className'] . ' fc-invitation-' . strtolower($partstat));
         }
     }
     return $event;
 }
Exemplo n.º 15
0
 /**
  * Get a list of pending alarms to be displayed to the user
  *
  * @param  integer Current time (unix timestamp)
  * @param  mixed   List of list IDs to show alarms for (either as array or comma-separated string)
  * @return array   A list of alarms, each encoded as hash array with task properties
  * @see tasklist_driver::pending_alarms()
  */
 public function pending_alarms($time, $lists = null)
 {
     $interval = 300;
     $time -= $time % 60;
     $slot = $time;
     $slot -= $slot % $interval;
     $last = $time - max(60, $this->rc->config->get('refresh_interval', 0));
     $last -= $last % $interval;
     // only check for alerts once in 5 minutes
     if ($last == $slot) {
         return array();
     }
     if ($lists && is_string($lists)) {
         $lists = explode(',', $lists);
     }
     $time = $slot + $interval;
     $tasks = array();
     $query = array(array('tags', '=', 'x-has-alarms'), array('tags', '!=', 'x-complete'));
     foreach ($this->lists as $lid => $list) {
         // skip lists with alarms disabled
         if (!$list['showalarms'] || $lists && !in_array($lid, $lists)) {
             continue;
         }
         $folder = $this->folders[$lid];
         foreach ((array) $folder->select($query) as $record) {
             if (!$record['alarms']) {
                 // don't trust query :-)
                 continue;
             }
             $task = $this->_to_rcube_task($record);
             // add to list if alarm is set
             $alarm = libcalendaring::get_next_alarm($task, 'task');
             if ($alarm && $alarm['time'] && $alarm['time'] <= $time && $alarm['action'] == 'DISPLAY') {
                 $id = $task['id'];
                 $tasks[$id] = $task;
                 $tasks[$id]['notifyat'] = $alarm['time'];
             }
         }
     }
     // get alarm information stored in local database
     if (!empty($tasks)) {
         $task_ids = array_map(array($this->rc->db, 'quote'), array_keys($tasks));
         $result = $this->rc->db->query(sprintf("SELECT * FROM kolab_alarms\n                 WHERE event_id IN (%s) AND user_id=?", join(',', $task_ids), $this->rc->db->now()), $this->rc->user->ID);
         while ($result && ($rec = $this->rc->db->fetch_assoc($result))) {
             $dbdata[$rec['event_id']] = $rec;
         }
     }
     $alarms = array();
     foreach ($tasks as $id => $task) {
         // skip dismissed
         if ($dbdata[$id]['dismissed']) {
             continue;
         }
         // snooze function may have shifted alarm time
         $notifyat = $dbdata[$id]['notifyat'] ? strtotime($dbdata[$id]['notifyat']) : $task['notifyat'];
         if ($notifyat <= $time) {
             $alarms[] = $task;
         }
     }
     return $alarms;
 }
Exemplo n.º 16
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)");
 }
Exemplo n.º 17
0
 /**
  * @depends test_import
  */
 function test_apple_alarms()
 {
     $ical = new libvcalendar();
     $events = $ical->import_from_file(__DIR__ . '/resources/apple-alarms.ics', 'UTF-8');
     $event = $events[0];
     // alarms
     $this->assertEquals('-45M:AUDIO', $event['alarms'], "Relative alarm string");
     $alarm = libcalendaring::parse_alarm_value($event['alarms']);
     $this->assertEquals('45', $alarm[0], "Alarm value");
     $this->assertEquals('-M', $alarm[1], "Alarm unit");
     $this->assertEquals(1, count($event['valarms']), "Ignore invalid alarm blocks");
     $this->assertEquals('AUDIO', $event['valarms'][0]['action'], "Full alarm item (action)");
     $this->assertEquals('-PT45M', $event['valarms'][0]['trigger'], "Full alarm item (trigger)");
     $this->assertEquals('Basso', $event['valarms'][0]['uri'], "Full alarm item (attachment)");
 }
Exemplo n.º 18
0
 /**
  * Prepare the given task record before sending it to the client
  */
 private function encode_task(&$rec)
 {
     $rec['mask'] = $this->filter_mask($rec);
     $rec['flagged'] = intval($rec['flagged']);
     $rec['complete'] = floatval($rec['complete']);
     $rec['changed'] = is_object($rec['changed']) ? $rec['changed']->format('U') : null;
     if ($rec['date']) {
         try {
             $date = new DateTime($rec['date'] . ' ' . $rec['time'], $this->timezone);
             $rec['datetime'] = intval($date->format('U'));
             $rec['date'] = $date->format($this->rc->config->get('date_format', 'Y-m-d'));
             $rec['_hasdate'] = 1;
         } catch (Exception $e) {
             $rec['date'] = $rec['datetime'] = null;
         }
     } else {
         $rec['date'] = $rec['datetime'] = null;
         $rec['_hasdate'] = 0;
     }
     if ($rec['startdate']) {
         try {
             $date = new DateTime($rec['startdate'] . ' ' . $rec['starttime'], $this->timezone);
             $rec['startdatetime'] = intval($date->format('U'));
             $rec['startdate'] = $date->format($this->rc->config->get('date_format', 'Y-m-d'));
         } catch (Exception $e) {
             $rec['startdate'] = $rec['startdatetime'] = null;
         }
     }
     if ($rec['alarms']) {
         $rec['alarms_text'] = libcalendaring::alarms_text($rec['alarms']);
     }
     foreach ((array) $rec['attachments'] as $k => $attachment) {
         $rec['attachments'][$k]['classname'] = rcube_utils::file2class($attachment['mimetype'], $attachment['name']);
     }
     if (in_array($rec['id'], $this->collapsed_tasks)) {
         $rec['collapsed'] = true;
     }
     $this->task_titles[$rec['id']] = $rec['title'];
 }
 /**
  * Helper function to build a Mail_mime object to send an iTip message
  *
  * @param array   Event object to send
  * @param string  iTip method (REQUEST|REPLY|CANCEL)
  * @param boolean Request RSVP
  * @return object Mail_mime object with message data
  */
 public function compose_itip_message($event, $method, $rsvp = true)
 {
     $from = rcube_utils::idn_to_ascii($this->sender['email']);
     $from_utf = rcube_utils::idn_to_utf8($from);
     $sender = format_email_recipient($from, $this->sender['name']);
     // truncate list attendees down to the recipient of the iTip Reply.
     // constraints for a METHOD:REPLY according to RFC 5546
     if ($method == 'REPLY') {
         $replying_attendee = null;
         $reply_attendees = array();
         foreach ($event['attendees'] as $attendee) {
             if ($attendee['role'] == 'ORGANIZER') {
                 $reply_attendees[] = $attendee;
             } else {
                 if (strcasecmp($attendee['email'], $from) == 0 || strcasecmp($attendee['email'], $from_utf) == 0) {
                     $replying_attendee = $attendee;
                     if ($attendee['status'] != 'DELEGATED') {
                         unset($replying_attendee['rsvp']);
                         // unset the RSVP attribute
                     }
                 } else {
                     if (!empty($attendee['delegated-to']) && (strcasecmp($attendee['delegated-to'], $from) == 0 || strcasecmp($attendee['delegated-to'], $from_utf) == 0) || !empty($attendee['delegated-from']) && (strcasecmp($attendee['delegated-from'], $from) == 0 || strcasecmp($attendee['delegated-from'], $from_utf) == 0)) {
                         $reply_attendees[] = $attendee;
                     }
                 }
             }
         }
         if ($replying_attendee) {
             array_unshift($reply_attendees, $replying_attendee);
             $event['attendees'] = $reply_attendees;
         }
         if ($event['recurrence']) {
             unset($event['recurrence']['EXCEPTIONS']);
         }
     } else {
         if ($method == 'REQUEST') {
             foreach ($event['attendees'] as $i => $attendee) {
                 if (($rsvp || !isset($attendee['rsvp'])) && ($attendee['status'] != 'DELEGATED' && $attendee['role'] != 'NON-PARTICIPANT')) {
                     $event['attendees'][$i]['rsvp'] = (bool) $rsvp;
                 }
             }
         } else {
             if ($method == 'CANCEL') {
                 if ($event['recurrence']) {
                     unset($event['recurrence']['EXCEPTIONS']);
                 }
             }
         }
     }
     // compose multipart message using PEAR:Mail_Mime
     $message = new Mail_mime("\r\n");
     $message->setParam('text_encoding', 'quoted-printable');
     $message->setParam('head_encoding', 'quoted-printable');
     $message->setParam('head_charset', RCUBE_CHARSET);
     $message->setParam('text_charset', RCUBE_CHARSET . ";\r\n format=flowed");
     $message->setContentType('multipart/alternative');
     // compose common headers array
     $headers = array('From' => $sender, 'Date' => $this->rc->user_date(), 'Message-ID' => $this->rc->gen_message_id(), 'X-Sender' => $from);
     if ($agent = $this->rc->config->get('useragent')) {
         $headers['User-Agent'] = $agent;
     }
     $message->headers($headers);
     // attach ics file for this event
     $ical = libcalendaring::get_ical();
     $ics = $ical->export(array($event), $method, false, $method == 'REQUEST' && $this->plugin->driver ? array($this->plugin->driver, 'get_attachment_body') : false);
     $filename = $event['_type'] == 'task' ? 'todo.ics' : 'event.ics';
     $message->addAttachment($ics, 'text/calendar', $filename, false, '8bit', '', RCUBE_CHARSET . "; method=" . $method);
     return $message;
 }
Exemplo n.º 20
0
 /**
  * Process the alarms values submitted by the client
  */
 public static function from_client_alarms($valarms)
 {
     return array_map(function ($alarm) {
         if ($alarm['trigger'][0] == '@') {
             try {
                 $alarm['trigger'] = new DateTime($alarm['trigger']);
                 $alarm['trigger']->setTimezone(new DateTimeZone('UTC'));
             } catch (Exception $e) {
                 /* handle this ? */
             }
         } else {
             if ($trigger = libcalendaring::parse_alarm_value($alarm['trigger'])) {
                 $alarm['trigger'] = $trigger[3];
             }
         }
         return $alarm;
     }, (array) $valarms);
 }
 /**
  * Convert from Kolab_Format to internal representation
  */
 private function _to_driver_event($record, $noinst = false)
 {
     $record['calendar'] = $this->id;
     $record['links'] = $this->get_links($record['uid']);
     if ($this->get_namespace() == 'other') {
         $record['className'] = 'fc-event-ns-other';
         $record = kolab_driver::add_partstat_class($record, array('NEEDS-ACTION', 'DECLINED'), $this->get_owner());
     }
     // add instance identifier to first occurrence (master event)
     $recurrence_id_format = libcalendaring::recurrence_id_format($record);
     if (!$noinst && $record['recurrence'] && !$record['recurrence_id'] && !$record['_instance']) {
         $record['_instance'] = $record['start']->format($recurrence_id_format);
     } else {
         if (is_a($record['recurrence_date'], 'DateTime')) {
             $record['_instance'] = $record['recurrence_date']->format($recurrence_id_format);
         }
     }
     // clean up exception data
     if ($record['recurrence'] && is_array($record['recurrence']['EXCEPTIONS'])) {
         array_walk($record['recurrence']['EXCEPTIONS'], function (&$exception) {
             unset($exception['_mailbox'], $exception['_msguid'], $exception['_formatobj'], $exception['_attachments']);
         });
     }
     return $record;
 }