diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php index 6e7a6c899f..aeb87ad367 100644 --- a/calendar/inc/class.calendar_groupdav.inc.php +++ b/calendar/inc/class.calendar_groupdav.inc.php @@ -688,7 +688,9 @@ class calendar_groupdav extends Api\CalDAV\Handler if (isset($expand['start'])) $expand['start'] = $this->vCalendar->_parseDateTime($expand['start']); if (isset($expand['end'])) $expand['end'] = $this->vCalendar->_parseDateTime($expand['end']); } - $events =& self::get_series($event['uid'], $this->bo, $expand, $user); + // pass in original event as master, as it has correct start-date even if first recurrence is an exception + $events =& self::get_series($event['uid'], $this->bo, $expand, $user, $event); + // as alarm is now only on next recurrence, set alarm from original event on master if ($event['alarm']) $events[0]['alarm'] = $event['alarm']; } @@ -708,9 +710,10 @@ class calendar_groupdav extends Api\CalDAV\Handler * @param calendar_bo $bo =null calendar_bo object to reuse for search call * @param boolean|array $expand =false true or array with values for 'start', 'end' to expand recurrences * @param int $user =null account_id of calendar to display, to remove master, if current user does not participate in + * @param array $master =null use provided event as master to fix wrong start-date if first recurrence is an exception * @return array */ - private static function &get_series($uid,calendar_bo $bo=null, $expand=false, $user=null) + private static function &get_series($uid,calendar_bo $bo=null, $expand=false, $user=null, $master=null) { if (is_null($bo)) $bo = new calendar_bopdate(); @@ -729,13 +732,12 @@ class calendar_groupdav extends Api\CalDAV\Handler } // find master, which is not always first event, eg. when first event is an exception - $master = null; $exceptions = array(); foreach($events as $k => &$recurrence) { if ($recurrence['recur_type']) { - $master = $recurrence; + if (!isset($master)) $master = $recurrence; $exceptions =& $master['recur_exception']; unset($events[$k]); break; diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index 67e7c3d582..640c726167 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -672,9 +672,10 @@ class calendar_ical extends calendar_boupdate { foreach ($event['recur_exception'] as $key => $timestamp) { - // current Horde_Icalendar 2.1.4 exports EXDATE always in UTC, so we should not set a timezone here + // current Horde_Icalendar 2.1.4 exports EXDATE always postfixed with a Z :( + // so if we set a timezone here, we have to remove the Z, see the hack at the end of this method // Apple calendar on OS X 10.11.4 uses a timezone, so does Horde eg. for Recurrence-ID - $event['recur_exception'][$key] = self::getDateTime($timestamp,$tzid);//,$parameters['EXDATE']); + $event['recur_exception'][$key] = self::getDateTime($timestamp, $tzid, $parameters['EXDATE']); } } else @@ -1073,7 +1074,13 @@ class calendar_ical extends calendar_boupdate error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ . "()\n".array2string($retval)."\n",3,$this->logfile); } - return $retval; + + // hack to fix iCalendar exporting EXDATE always postfixed with a Z + // EXDATE can have multiple values and therefore be folded into multiple lines + return preg_replace_callback("/\nEXDATE;TZID=[^:]+:[0-9TZ \n,]+/", function($matches) + { + return preg_replace('/([0-9 ])Z/', '$1', $matches[0]); + }, $retval); } /**