/**
  * 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;
 }
Example #2
0
 /**
  * Add or update the given event as an exception to $master
  */
 public static function add_exception(&$master, $event, $old = null)
 {
     if ($old) {
         $event['_instance'] = $old['_instance'];
         if (!$event['recurrence_date']) {
             $event['recurrence_date'] = $old['recurrence_date'] ?: $old['start'];
         }
     } else {
         if (!$event['recurrence_date']) {
             $event['recurrence_date'] = $event['start'];
         }
     }
     if (!$event['_instance'] && is_a($event['recurrence_date'], 'DateTime')) {
         $event['_instance'] = libcalendaring::recurrence_instance_identifier($event);
     }
     if (!is_array($master['exceptions']) && is_array($master['recurrence']['EXCEPTIONS'])) {
         $master['exceptions'] =& $master['recurrence']['EXCEPTIONS'];
     }
     $existing = false;
     foreach ((array) $master['exceptions'] as $i => $exception) {
         if ($exception['_instance'] == $event['_instance']) {
             $master['exceptions'][$i] = $event;
             $existing = true;
         }
     }
     if (!$existing) {
         $master['exceptions'][] = $event;
     }
     return true;
 }
 /**
  * Get event data
  *
  * @see calendar_driver::load_events()
  */
 public function load_events($start, $end, $query = null, $calendars = null, $virtual = 1, $modifiedsince = null)
 {
     if (empty($calendars)) {
         $calendars = array_keys($this->calendars);
     } else {
         if (!is_array($calendars)) {
             $calendars = explode(',', strval($calendars));
         }
     }
     // only allow to select from calendars of this use
     $calendar_ids = array_map(array($this->rc->db, 'quote'), array_intersect($calendars, array_keys($this->calendars)));
     // compose (slow) SQL query for searching
     // FIXME: improve searching using a dedicated col and normalized values
     if ($query) {
         foreach (array('title', 'location', 'description', 'categories', 'attendees') as $col) {
             $sql_query[] = $this->rc->db->ilike($col, '%' . $query . '%');
         }
         $sql_add = 'AND (' . join(' OR ', $sql_query) . ')';
     }
     if (!$virtual) {
         $sql_add .= ' AND e.recurrence_id = 0';
     }
     if ($modifiedsince) {
         $sql_add .= ' AND e.changed >= ' . $this->rc->db->quote(date('Y-m-d H:i:s', $modifiedsince));
     }
     $events = array();
     if (!empty($calendar_ids)) {
         $result = $this->rc->db->query(sprintf("SELECT e.*, (SELECT COUNT(attachment_id) FROM " . $this->db_attachments . "\n            WHERE event_id = e.event_id OR event_id = e.recurrence_id) AS _attachments\n         FROM " . $this->db_events . " e\n         WHERE e.calendar_id IN (%s)\n            AND e.start <= %s AND e.end >= %s\n            %s", join(',', $calendar_ids), $this->rc->db->fromunixtime($end), $this->rc->db->fromunixtime($start), $sql_add));
         while ($result && ($sql_arr = $this->rc->db->fetch_assoc($result))) {
             $event = $this->_read_postprocess($sql_arr);
             $add = true;
             if (!empty($event['recurrence']) && !$event['recurrence_id']) {
                 // load recurrence exceptions (i.e. for export)
                 if (!$virtual) {
                     $event['recurrence']['EXCEPTIONS'] = $this->_load_exceptions($event);
                 } else {
                     $instance = libcalendaring::recurrence_instance_identifier($event);
                     $exceptions = $this->_load_exceptions($event, $instance);
                     if ($exceptions && is_array($exceptions[$instance])) {
                         $event = $exceptions[$instance];
                         $add = false;
                     }
                 }
             }
             if ($add) {
                 $events[] = $event;
             }
         }
     }
     // add events from the address books birthday calendar
     if (in_array(self::BIRTHDAY_CALENDAR_ID, $calendars) && empty($query)) {
         $events = array_merge($events, $this->load_birthday_events($start, $end, $search, $modifiedsince));
     }
     return $events;
 }
 /**
  * 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
  * @param string ID of a specific recurring event instance
  * @return array List of recurring event instances
  */
 public function get_recurring_events($event, $start, $end = null, $event_id = null)
 {
     $object = $event['_formatobj'];
     if (!$object) {
         $rec = $this->storage->get_object($event['id']);
         $object = $rec['_formatobj'];
     }
     if (!is_object($object)) {
         return array();
     }
     // 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));
     }
     // copy the recurrence rule from the master event (to be used in the UI)
     $recurrence_rule = $event['recurrence'];
     unset($recurrence_rule['EXCEPTIONS'], $recurrence_rule['EXDATE']);
     // read recurrence exceptions first
     $events = array();
     $exdata = array();
     $futuredata = array();
     $recurrence_id_format = libcalendaring::recurrence_id_format($event);
     if (is_array($event['recurrence']['EXCEPTIONS'])) {
         foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
             if (!$exception['_instance']) {
                 $exception['_instance'] = libcalendaring::recurrence_instance_identifier($exception);
             }
             $rec_event = $this->_to_driver_event($exception);
             $rec_event['id'] = $event['uid'] . '-' . $exception['_instance'];
             $rec_event['isexception'] = 1;
             // found the specifically requested instance: register exception (single occurrence wins)
             if ($rec_event['id'] == $event_id && (!$this->events[$event_id] || $this->events[$event_id]['thisandfuture'])) {
                 $rec_event['recurrence'] = $recurrence_rule;
                 $rec_event['recurrence_id'] = $event['uid'];
                 $this->events[$rec_event['id']] = $rec_event;
             }
             // remember this exception's date
             $exdate = substr($exception['_instance'], 0, 8);
             if (!$exdata[$exdate] || $exdata[$exdate]['thisandfuture']) {
                 $exdata[$exdate] = $rec_event;
             }
             if ($rec_event['thisandfuture']) {
                 $futuredata[$exdate] = $rec_event;
             }
         }
     }
     // found the specifically requested instance, exiting...
     if ($event_id && !empty($this->events[$event_id])) {
         return array($this->events[$event_id]);
     }
     // use libkolab to compute recurring events
     if (class_exists('kolabcalendaring')) {
         $recurrence = new kolab_date_recurrence($object);
     } else {
         // fallback to local recurrence implementation
         require_once $this->cal->home . '/lib/calendar_recurrence.php';
         $recurrence = new calendar_recurrence($this->cal, $event);
     }
     $i = 0;
     while ($next_event = $recurrence->next_instance()) {
         $datestr = $next_event['start']->format('Ymd');
         $instance_id = $next_event['start']->format($recurrence_id_format);
         // use this event data for future recurring instances
         if ($futuredata[$datestr]) {
             $overlay_data = $futuredata[$datestr];
         }
         // add to output if in range
         $rec_id = $event['uid'] . '-' . $instance_id;
         if ($next_event['start'] <= $end && $next_event['end'] >= $start || $event_id && $rec_id == $event_id) {
             $rec_event = $this->_to_driver_event($next_event);
             $rec_event['_instance'] = $instance_id;
             $rec_event['_count'] = $i + 1;
             if ($overlay_data || $exdata[$datestr]) {
                 // copy data from exception
                 kolab_driver::merge_exception_data($rec_event, $exdata[$datestr] ?: $overlay_data);
             }
             $rec_event['id'] = $rec_id;
             $rec_event['recurrence_id'] = $event['uid'];
             $rec_event['recurrence'] = $recurrence_rule;
             unset($rec_event['_attendees']);
             $events[] = $rec_event;
             if ($rec_id == $event_id) {
                 $this->events[$rec_id] = $rec_event;
                 break;
             }
         } else {
             if ($next_event['start'] > $end) {
                 // stop loop if out of range
                 break;
             }
         }
         // avoid endless recursion loops
         if (++$i > 1000) {
             break;
         }
     }
     return $events;
 }