From 0f64b4c74039c4dfcd22d2881fb3de56555b504c Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 10 Jan 2020 11:13:18 +0100 Subject: [PATCH] * CalDAV/Calendar: fix for first recurence being an exception cause it to be deleted by mac calendar due to wrong start of series not taking execption into account also fix missing timezone in EXDATE did not match recurrence --- calendar/inc/class.calendar_groupdav.inc.php | 10 ++++++---- calendar/inc/class.calendar_ical.inc.php | 13 ++++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) 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); } /**