From 21ccdd5f288e11ef83577da78e83e39f5135bb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Wed, 17 Feb 2010 13:29:28 +0000 Subject: [PATCH] Fix various synchronization issues (SyncML & CalDAV) --- calendar/inc/class.calendar_bo.inc.php | 4 + calendar/inc/class.calendar_boupdate.inc.php | 74 ++++++---- calendar/inc/class.calendar_groupdav.inc.php | 66 ++++++--- calendar/inc/class.calendar_ical.inc.php | 138 +++++++++++++----- calendar/inc/class.calendar_sif.inc.php | 10 +- calendar/inc/class.calendar_so.inc.php | 102 ++++++++----- calendar/inc/class.calendar_ui.inc.php | 3 +- calendar/templates/default/images/forward.gif | Bin 0 -> 319 bytes calendar/templates/default/images/forward.png | Bin 0 -> 494 bytes 9 files changed, 264 insertions(+), 133 deletions(-) create mode 100644 calendar/templates/default/images/forward.gif create mode 100644 calendar/templates/default/images/forward.png diff --git a/calendar/inc/class.calendar_bo.inc.php b/calendar/inc/class.calendar_bo.inc.php index 08d45f45c6..9b759b0d6d 100644 --- a/calendar/inc/class.calendar_bo.inc.php +++ b/calendar/inc/class.calendar_bo.inc.php @@ -95,6 +95,7 @@ class calendar_bo 'R' => 'Rejected', 'T' => 'Tentative', 'U' => 'No Response', + 'D' => 'Delegated', 'G' => 'Group invitation', ); /** @@ -1349,6 +1350,9 @@ class calendar_bo case 'U': // no response = unknown $status = html::image('calendar','cnr-pending',$this->verbose_status[$status]); break; + case 'D': // delegated + $status = html::image('calendar','forward',$this->verbose_status[$status]); + break; case 'G': // group invitation // Todo: Image, seems not to be used $status = '('.$this->verbose_status[$status].')'; diff --git a/calendar/inc/class.calendar_boupdate.inc.php b/calendar/inc/class.calendar_boupdate.inc.php index e4fe7a1639..c6cdc212a1 100644 --- a/calendar/inc/class.calendar_boupdate.inc.php +++ b/calendar/inc/class.calendar_boupdate.inc.php @@ -20,6 +20,7 @@ define('MSG_TENTATIVE',4); define('MSG_ACCEPTED',5); define('MSG_ALARM',6); define('MSG_DISINVITE',7); +define('MSG_DELEGATED',8); /** * Class to access AND manipulate all calendar data (business object) @@ -493,7 +494,7 @@ class calendar_boupdate extends calendar_bo // the following switch falls through all cases, as each included the following too // - $msg_is_response = $msg_type == MSG_REJECTED || $msg_type == MSG_ACCEPTED || $msg_type == MSG_TENTATIVE; + $msg_is_response = $msg_type == MSG_REJECTED || $msg_type == MSG_ACCEPTED || $msg_type == MSG_TENTATIVE || $msg_type == MSG_DELEGATED; switch($ru = $part_prefs['calendar']['receive_updates']) { @@ -619,6 +620,12 @@ class calendar_boupdate extends calendar_bo $msgtype = '"calendar";'; $method = 'REPLY'; break; + case MSG_DELEGATED: + $action = lang('Delegated'); + $msg = 'Response'; + $msgtype = '"calendar";'; + $method = 'REPLY'; + break; case MSG_ALARM: $action = lang('Alarm'); $msg = 'Alarm'; @@ -825,8 +832,16 @@ class calendar_boupdate extends calendar_bo return false; } - // invalidate the read-cache if it contains the event we store now - if ($event['id'] && $event['id'] == self::$cached_event['id']) self::$cached_event = array(); + if ($event['id']) + { + // invalidate the read-cache if it contains the event we store now + if ($event['id'] == self::$cached_event['id']) self::$cached_event = array(); + $old_event = $this->read($event['id'], $event['recurrence'], false, 'server'); + } + else + { + $old_event = null; + } $save_event = $event; // we run all dates through date2ts, to adjust to server-time and the possible date-formats @@ -858,8 +873,6 @@ class calendar_boupdate extends calendar_bo } $set_recurrences = false; $set_recurrences_start = 0; - $old_event = $this->read($event['id'], $event['recurrence']); - if (($cal_id = $this->so->save($event,$set_recurrences,$set_recurrences_start,0,$event['etag'])) && $set_recurrences && $event['recur_type'] != MCAL_RECUR_NONE) { $save_event['id'] = $cal_id; @@ -1062,7 +1075,7 @@ class calendar_boupdate extends calendar_bo error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. "($cal_id, $uid, $status, $recur_date)"); } - $old_event = $this->read($cal_id, $recur_date); + $old_event = $this->read($cal_id, $recur_date, false, 'server'); if (($Ok = $this->so->set_status($cal_id,is_numeric($uid)?'u':$uid[0],is_numeric($uid)?$uid:substr($uid,1),$status,$recur_date ? $this->date2ts($recur_date,true) : 0,$role))) { if ($updateTS) $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,'modify',time()); @@ -1071,6 +1084,7 @@ class calendar_boupdate extends calendar_bo 'R' => MSG_REJECTED, 'T' => MSG_TENTATIVE, 'A' => MSG_ACCEPTED, + 'D' => MSG_DELEGATED, ); if (isset($status2msg[$status])) { @@ -1080,7 +1094,7 @@ class calendar_boupdate extends calendar_bo } // Update history - if (!is_array($event)) $event = $this->read($cal_id); + $event = $this->read($cal_id, $recur_date, false, 'server'); $tracking = new calendar_tracking($this); $tracking->track($event, $old_event); @@ -1098,8 +1112,6 @@ class calendar_boupdate extends calendar_bo */ function delete($cal_id,$recur_date=0,$ignore_acl=false) { - $event = $this->read($cal_id,$recur_date); - if (!($event = $this->read($cal_id,$recur_date)) || !$ignore_acl && !$this->check_perms(EGW_ACL_DELETE,$event)) { @@ -1111,10 +1123,6 @@ class calendar_boupdate extends calendar_bo { $this->so->delete($cal_id); $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,'delete',time()); - - // Update history - $tracking = new calendar_tracking($this); - $tracking->track($event, $event, null, true); // delete all links to the event egw_link::unlink(0,'calendar',$cal_id); @@ -1448,6 +1456,7 @@ class calendar_boupdate extends calendar_bo { $matchingEvents = array(); $query = array(); + $recur_date = 0; if ($this->log) { @@ -1459,17 +1468,12 @@ class calendar_boupdate extends calendar_bo { if (isset($event['recurrence'])) { - $recur_date = $event['recurrence']; + $recur_date = $this->date2usertime($event['recurrence']); } - else + elseif (isset($event['start'])) { - $recur_date = $event['start']; + $recur_date = $this->date2usertime($event['start']); } - $recur_date = $this->date2usertime($recur_date); - } - else - { - $recur_date = 0; } if ($event['id']) @@ -1501,13 +1505,14 @@ class calendar_boupdate extends calendar_bo return $matchingEvents; } } - if ($filter == 'exact' || $filter == 'check') return array(); + if ($filter == 'exact') return array(); } unset($event['id']); if ($filter == 'master') { $query[] = 'recur_type!='. MCAL_RECUR_NONE; + $query['cal_recurrence'] = 0; } // only query calendars of users, we have READ-grants from @@ -1611,7 +1616,7 @@ class calendar_boupdate extends calendar_bo error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. '(' . $event['uid'] . ')[EventUID]'); } - if (isset($event['recurrence'])) + if ($filter != 'master' && isset($event['recurrence'])) { $query['cal_recurrence'] = $event['recurrence']; } @@ -1637,17 +1642,17 @@ class calendar_boupdate extends calendar_bo foreach($foundEvents as $egwEvent) { - if (in_array($egwEvent['id'], $matchingEvents)) continue; - if ($this->log) { error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. '[FOUND]: ' . array2string($egwEvent)); } + if (in_array($egwEvent['id'], $matchingEvents)) continue; - if ($filter == 'chec' && !empty($event['uid'])) + if (in_array($filter, array('exact', 'master')) && !empty($event['uid'])) { $matchingEvents[] = $egwEvent['id']; // UID found + if ($filter = 'master') break; continue; } @@ -1849,6 +1854,7 @@ class calendar_boupdate extends calendar_bo } } $matchingEvents[] = $egwEvent['id']; // exact match + if ($filter = 'master') break; } // append pseudos as last entries $matchingEvents = array_merge($matchingEvents, $pseudos); @@ -1942,20 +1948,24 @@ class calendar_boupdate extends calendar_bo in_array($event['recurrence'], $master_event['recur_exception'])) { $type = 'SERIES-PSEUDO-EXCEPTION'; // could also be a real one - $recurrence_event = $event; + $recurrence_event = $master_event; + $recurrence_event['start'] = $event['recurrence']; + $recurrence_event['end'] -= $master_event['start'] - $event['recurrence']; break; } elseif (in_array($event['start'], $master_event['recur_exception'])) { $type='SERIES-PSEUDO-EXCEPTION'; // new pseudo exception? - $recurrence_event = $event; + $recurrence_event = $master_event; + $recurrence_event['start'] = $event['start']; + $recurrence_event['end'] -= $master_event['start'] - $event['start']; break; } else { // try to find a suitable pseudo exception date $egw_rrule = calendar_rrule::event2rrule($master_event, false); - $egw_rrule->rewind(); + $egw_rrule->current = clone $egw_rrule->time; while ($egw_rrule->valid()) { $occurrence = egw_time::to($egw_rrule->current(), 'server'); @@ -1967,7 +1977,9 @@ class calendar_boupdate extends calendar_bo if ($event['start'] == $occurrence) { $type = 'SERIES-PSEUDO-EXCEPTION'; // let's try a pseudo exception - $recurrence_event = $event; + $recurrence_event = $master_event; + $recurrence_event['start'] = $occurrence; + $recurrence_event['end'] -= $master_event['start'] - $occurrence; break 2; } if (isset($event['recurrence']) && $event['recurrence'] == $occurrence) @@ -2077,4 +2089,4 @@ class calendar_boupdate extends calendar_bo } } } -} +} \ No newline at end of file diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php index 32d170f64d..5883a74643 100644 --- a/calendar/inc/class.calendar_groupdav.inc.php +++ b/calendar/inc/class.calendar_groupdav.inc.php @@ -110,10 +110,12 @@ class calendar_groupdav extends groupdav_handler 'daywise' => false, 'date_format' => 'server', ); + /* if ($this->client_shared_uid_exceptions) { $cal_filters['query']['cal_reference'] = 0; } + */ // process REPORT filters or multiget href's if (($id || $options['root']['name'] != 'propfind') && !$this->_report_filters($options,$cal_filters,$id)) { @@ -137,8 +139,17 @@ class calendar_groupdav extends groupdav_handler if ($events) { // get all max user modified times at once - foreach($events as &$event) + foreach($events as $k => &$event) { + if ($this->client_shared_uid_exceptions && + $event['reference'] && + ($master = $this->bo->read($event['reference'], 0, false, 'server')) && + array_search($event['recurrence'], $master['recur_exception']) !== false) + { + // this exception will be handled with the series master + unset($events[$k]); + continue; + } $ids[] = $event['id']; } $max_user_modified = $this->bo->so->max_user_modified($ids); @@ -350,22 +361,28 @@ class calendar_groupdav extends groupdav_handler */ private static function &get_series($uid,calendar_bo $bo=null) { - if (is_null($bo)) $bo = new calendar_bo(); + if (is_null($bo)) $bo = new calendar_bopdate(); + + if (!($masterId = array_shift($bo->find_event(array('uid' => $uid), 'master'))) + || !($master = $bo->read($masterId, 0, false, 'server'))) + { + return array(); // should never happen + } + + $exceptions = $master['recur_exception']; $events =& $bo->search(array( 'query' => array('cal_uid' => $uid), 'daywise' => false, 'date_format' => 'server', )); - $master = null; + $events = array_merge(array($master), $events); foreach($events as $k => &$recurrence) { - if (!isset($master)) // first event is always the series master - { - $master =& $events[$k]; - //error_log('master: '.array2string($master)); - continue; // nothing to change - } + //error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. + // "($uid)[$k]:" . array2string($recurrence)); + if (!$k) continue; // nothing to change + if ($recurrence['id'] != $master['id']) // real exception { //error_log('real exception: '.array2string($recurrence)); @@ -373,9 +390,9 @@ class calendar_groupdav extends groupdav_handler // at least Lightning "understands" EXDATE as exception from what's included // in the whole resource / VCALENDAR component // not removing it causes Lightning to remove the exception itself - if (($k = array_search($recurrence['recurrence'],$master['recur_exception'])) !== false) + if (($e = array_search($recurrence['recurrence'],$exceptions)) !== false) { - unset($master['recur_exception'][$k]); + unset($exceptions[$e]); } continue; // nothing to change } @@ -386,13 +403,14 @@ class calendar_groupdav extends groupdav_handler unset($events[$k]); // no exception --> remove it continue; } - // this is a virtual excetion now (no extra event/cal_id in DB) + // this is a virtual exception now (no extra event/cal_id in DB) //error_log('virtual exception: '.array2string($recurrence)); $recurrence['recurrence'] = $recurrence['start']; $recurrence['reference'] = $master['id']; $recurrence['recur_type'] = MCAL_RECUR_NONE; // is set, as this is a copy of the master // not for included exceptions (Lightning): $master['recur_exception'][] = $recurrence['start']; } + $events[0]['recur_exception'] = $exceptions; return $events; } @@ -416,10 +434,12 @@ class calendar_groupdav extends groupdav_handler return $event; } $handler = $this->_get_handler(); - if (!is_numeric($id) && ($foundEntries = $handler->find_event($options['content'], 'check'))) + + if (!is_numeric($id) && ($foundEntries = $handler->find_event($options['content'], 'exact'))) { $id = array_shift($foundEntries); } + if (!($cal_id = $handler->importVCal($options['content'],is_numeric($id) ? $id : -1, self::etag2value($this->http_if_match)))) { @@ -448,14 +468,15 @@ class calendar_groupdav extends groupdav_handler static function fix_series(array &$events) { foreach($events as $n => $event) error_log(__METHOD__." $n before: ".array2string($event)); - $master =& $events[0]; + //$master =& $events[0]; $bo = new calendar_boupdate(); // get array with orginal recurrences indexed by recurrence-id - $org_recurrences = array(); - foreach(self::get_series($master['uid'],$bo) as $event) + $org_recurrences = $exceptions = array(); + foreach(self::get_series($events[0]['uid'],$bo) as $k => $event) { + if (!$k) $master = $event; if ($event['recurrence']) { $org_recurrences[$event['recurrence']] = $event; @@ -463,9 +484,15 @@ class calendar_groupdav extends groupdav_handler } // assign cal_id's to already existing recurrences and evtl. re-add recur_exception to master - foreach($events as &$recurrence) + foreach($events as $k => &$recurrence) { - if ($recurrence['id'] || !$recurrence['recurrence']) continue; // master + if (!$recurrence['recurrence']) + { + // master + $recurrence['id'] = $master['id']; + $master =& $events[$k]; + continue; + } // from now on we deal with exceptions $org_recurrence = $org_recurrences[$recurrence['recurrence']]; @@ -478,12 +505,13 @@ class calendar_groupdav extends groupdav_handler if ($recurrence['id'] != $master['id']) { error_log(__METHOD__.'() re-adding recur_exception '.$recurrence['recurrence'].' = '.date('Y-m-d H:i:s',$recurrence['recurrence'])); - $master['recur_exception'][] = $recurrence['recurrence']; + $exceptions[] = $recurrence['recurrence']; } // remove recurrence to be able to detect deleted exceptions unset($org_recurrences[$recurrence['recurrence']]); } } + $master['recur_exception'] = array_merge($exceptions, $master['recur_exception']); // delete not longer existing recurrences foreach($org_recurrences as $org_recurrence) diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index 7a62111d79..167731fe1e 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -41,6 +41,7 @@ class calendar_ical extends calendar_boupdate 'A' => 'ACCEPTED', 'R' => 'DECLINED', 'T' => 'TENTATIVE', + 'D' => 'DELEGATED' ); /** * @var array conversation of the participant status ical => egw @@ -51,6 +52,7 @@ class calendar_ical extends calendar_boupdate 'ACCEPTED' => 'A', 'DECLINED' => 'R', 'TENTATIVE' => 'T', + 'DELEGATED' => 'D', ); /** @@ -227,7 +229,7 @@ class calendar_ical extends calendar_boupdate if ($this->log) { error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. - "() User does not have the permission to read event $_id.\n", + '() User does not have the permission to read event ' . $event['id']. "\n", 3,$this->logfile); } return -1; // Permission denied @@ -238,8 +240,8 @@ class calendar_ical extends calendar_boupdate if ($this->log) { error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. - "() Event $_id not found.\n", - 3,$this->logfile); + "() Event $event not found.\n", + 3, $this->logfile); } } continue; @@ -342,15 +344,21 @@ class calendar_ical extends calendar_boupdate $horde_vtimezone->parsevCalendar($vtimezone,'VTIMEZONE'); // DTSTART must be in local time! $standard = $horde_vtimezone->findComponent('STANDARD'); - $dtstart = $standard->getAttribute('DTSTART'); - $dtstart = new egw_time($dtstart, egw_time::$server_timezone); - $dtstart->setTimezone(self::$tz_cache[$tzid]); - $standard->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false); + if (is_a($standard, 'Horde_iCalendar')) + { + $dtstart = $standard->getAttribute('DTSTART'); + $dtstart = new egw_time($dtstart, egw_time::$server_timezone); + $dtstart->setTimezone(self::$tz_cache[$tzid]); + $standard->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false); + } $daylight = $horde_vtimezone->findComponent('DAYLIGHT'); - $dtstart = $daylight->getAttribute('DTSTART'); - $dtstart = new egw_time($dtstart, egw_time::$server_timezone); - $dtstart->setTimezone(self::$tz_cache[$tzid]); - $daylight->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false); + if (is_a($daylight, 'Horde_iCalendar')) + { + $dtstart = $daylight->getAttribute('DTSTART'); + $dtstart = new egw_time($dtstart, egw_time::$server_timezone); + $dtstart->setTimezone(self::$tz_cache[$tzid]); + $daylight->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false); + } $vcal->addComponent($horde_vtimezone); $vtimezones_added[] = $tzid; } @@ -396,6 +404,7 @@ class calendar_ical extends calendar_boupdate sort($exceptions); } $event['recur_exception'] = $exceptions; + /* // Adjust the event start -- must not be an exception $length = $event['end'] - $event['start']; $rriter = calendar_rrule::event2rrule($event, false, $tzid); @@ -415,13 +424,21 @@ class calendar_ical extends calendar_boupdate { // the series dissolved completely into exceptions continue; - } + }*/ } foreach ($egwSupportedFields as $icalFieldName => $egwFieldName) { - if (!isset($this->supportedFields[$egwFieldName])) continue; - + if (!isset($this->supportedFields[$egwFieldName])) + { + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. + '(' . $event['id'] . ") [$icalFieldName] not supported\n", + 3,$this->logfile); + } + continue; + } $values[$icalFieldName] = array(); switch ($icalFieldName) { @@ -928,11 +945,30 @@ class calendar_ical extends calendar_boupdate { if (!is_array($this->supportedFields)) $this->setSupportedFields(); - if (!($events = $this->icaltoegw($_vcalData,$cal_id,$etag,$recur_date))) + if (!($events = $this->icaltoegw($_vcalData))) { return false; } + if ($cal_id > 0) + { + if (count($events) == 1) + { + $events[0]['id'] = $cal_id; + if (!is_null($etag)) $events[0]['etag'] = (int) $etag; + if ($recur_date) $events[0]['recurrence'] = $recur_date; + } + elseif (($foundEvent = $this->find_event(array('id' => $cal_id), 'exact')) && + ($eventId = array_shift($foundEvent)) && + ($egwEvent = $this->read($eventId))) + { + foreach ($events as $k => $event) + { + if (!isset($event['uid'])) $events[$k]['uid'] = $egwEvent['uid']; + } + } + } + // check if we are importing an event series with exceptions in CalDAV // only first event / series master get's cal_id from URL // other events are exceptions and need to be checked if they are new @@ -947,13 +983,18 @@ class calendar_ical extends calendar_boupdate foreach ($events as $event) { if ($this->so->isWholeDay($event)) $event['whole_day'] = true; - + if (is_array($event['category'])) + { + $event['category'] = $this->find_or_add_categories($event['category'], + isset($event['id']) ? $event['id'] : -1); + } if ($this->log) { error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . array2string($event)."\n",3,$this->logfile); } + /* if ($event['recur_type'] != MCAL_RECUR_NONE) { // Adjust the event start -- no exceptions before and at the start @@ -987,7 +1028,7 @@ class calendar_ical extends calendar_boupdate } $event['recur_exception'] = $exceptions; } - + */ $updated_id = false; $event_info = $this->get_event_info($event); @@ -1066,11 +1107,11 @@ class calendar_ical extends calendar_boupdate error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. "() Restore status for $uid\n",3,$this->logfile); } - $event['participants']['uid'] = $event_info['stored_event']['participants'][$uid]; + $event['participants'][$uid] = $event_info['stored_event']['participants'][$uid]; } else { - $event['participants']['uid'] = calendar_so::combine_status('U'); + $event['participants'][$uid] = calendar_so::combine_status('U'); } } } @@ -1271,7 +1312,7 @@ class calendar_ical extends calendar_boupdate if (is_array($days)) { $recur_exceptions = array(); - + /* if (!isset($days[$event_info['stored_event']['start']]) && $event_info['stored_event']['start'] < $event['start']) { @@ -1318,7 +1359,7 @@ class calendar_ical extends calendar_boupdate $event['start'] = $startdate; $event['end'] = $startdate + $length; - } + } */ foreach ($event['recur_exception'] as $recur_exception) { @@ -1363,7 +1404,7 @@ class calendar_ical extends calendar_boupdate $event_info['master_event']['recur_exception'] = array_unique(array_merge($event_info['master_event']['recur_exception'], array($event['recurrence']))); - + /* // Adjust the event start -- must not be an exception $length = $event_info['master_event']['end'] - $event_info['master_event']['start']; $rriter = calendar_rrule::event2rrule($event_info['master_event'], false); @@ -1376,6 +1417,21 @@ class calendar_ical extends calendar_boupdate // remove leading exceptions if ($day < $newstart) { + if (($foundEvents = $this->find_event( + array('uid' => $event_info['master_event']['uid'], + 'recurrence' => $day), 'exact')) && + ($eventId = array_shift($foundEvents)) && + ($exception = read($eventId, 0, 'server'))) + { + // Unlink this exception + unset($exception['uid']); + $this->update($exception, true); + } + if ($event['recurrence'] == $day) + { + // Unlink this exception + unset($event['uid']); + } unset($event_info['master_event']['recur_exception'][$key]); } } @@ -1384,14 +1440,14 @@ class calendar_ical extends calendar_boupdate { $event_info['master_event']['start'] = $newstart; $event_info['master_event']['end'] = $newstart + $length; - $event_to_store = $event_info['master_event']; // prevent the master_event from being changed by the update method - $this->server2usertime($event_to_store); - $this->update($event_to_store, true); - unset($event_to_store); - } + }*/ $event['reference'] = $event_info['master_event']['id']; $event['category'] = $event_info['master_event']['category']; $event['owner'] = $event_info['master_event']['owner']; + $event_to_store = $event_info['master_event']; // prevent the master_event from being changed by the update method + $this->server2usertime($event_to_store); + $this->update($event_to_store, true); + unset($event_to_store); } $event_to_store = $event; // prevent $event from being changed by update method @@ -1864,7 +1920,7 @@ class calendar_ical extends calendar_boupdate } } - function icaltoegw($_vcalData, $cal_id=-1, $etag=null, $recur_date=0) + function icaltoegw($_vcalData) { if ($this->log) { @@ -1906,7 +1962,7 @@ class calendar_ical extends calendar_boupdate { if (is_a($component, 'Horde_iCalendar_vevent')) { - if (($event = $this->vevent2egw($component, $version, $this->supportedFields, $cal_id))) + if (($event = $this->vevent2egw($component, $version, $this->supportedFields))) { //common adjustments if ($this->productManufacturer == '' && $this->productName == '' @@ -1943,11 +1999,6 @@ class calendar_ical extends calendar_boupdate date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']); - // if cal_id, etag or recur_date is given, use/set it for 1. event - if ($cal_id > 0) $events[0]['id'] = $cal_id; - if (!is_null($etag)) $events[0]['etag'] = (int) $etag; - if ($recur_date) $events[0]['recurrence'] = $recur_date; - return $events; } @@ -1957,11 +2008,10 @@ class calendar_ical extends calendar_boupdate * @param array $component VEVENT * @param string $version vCal version (1.0/2.0) * @param array $supportedFields supported fields of the device - * @param int $cal_id id of existing event in the content (only used to merge categories) * * @return array|boolean event on success, false on failure */ - function vevent2egw(&$component, $version, $supportedFields, $cal_id=-1) + function vevent2egw(&$component, $version, $supportedFields) { if (!is_a($component, 'Horde_iCalendar_vevent')) return false; @@ -2353,7 +2403,7 @@ class calendar_ical extends calendar_boupdate case 'CATEGORIES': if ($attributes['value']) { - $vcardData['category'] = $this->find_or_add_categories(explode(',',$attributes['value']), $cal_id); + $vcardData['category'] = explode(',', $attributes['value']); } else { @@ -2369,6 +2419,7 @@ class calendar_ical extends calendar_boupdate if (isset($attributes['params']['STATUS'])) { $status = $this->status_ical2egw[strtoupper($attributes['params']['STATUS'])]; + if (empty($status)) $status = 'X'; } else { @@ -2599,8 +2650,6 @@ class calendar_ical extends calendar_boupdate if ($this->calendarOwner) $event['owner'] = $this->calendarOwner; - if ($cal_id > 0) $event['id'] = $cal_id; - if ($this->log) { error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . @@ -2613,7 +2662,15 @@ class calendar_ical extends calendar_boupdate function search($_vcalData, $contentID=null, $relax=false) { - if (($events = $this->icaltoegw($_vcalData,!is_null($contentID) ? $contentID : -1))) + if (is_null($contentID)) + { + $eventId = -1; + } + else + { + $eventId = $contentID; + } + if (($events = $this->icaltoegw($_vcalData))) { // this function only supports searching a single event if (count($events) == 1) @@ -2621,6 +2678,7 @@ class calendar_ical extends calendar_boupdate $filter = $relax ? 'relax' : 'check'; $event = array_shift($events); if ($this->so->isWholeDay($event)) $event['whole_day'] = true; + $event['category'] = $this->find_or_add_categories($event['category'], $eventId); if ($contentID) $event['id'] = $contentID; return $this->find_event($event, $filter); } diff --git a/calendar/inc/class.calendar_sif.inc.php b/calendar/inc/class.calendar_sif.inc.php index d5c70c8942..6551ebd2f9 100644 --- a/calendar/inc/class.calendar_sif.inc.php +++ b/calendar/inc/class.calendar_sif.inc.php @@ -430,6 +430,7 @@ class calendar_sif extends calendar_boupdate return false; } + /* if ($event['recur_type'] != MCAL_RECUR_NONE) { // Adjust the event start -- no exceptions before and at the start @@ -462,7 +463,7 @@ class calendar_sif extends calendar_boupdate } } $event['recur_exception'] = $exceptions; - } + } */ if ($recur_date) $event['recurrence'] = $recur_date; $event_info = $this->get_event_info($event); @@ -684,7 +685,7 @@ class calendar_sif extends calendar_boupdate $event_info['master_event']['recur_exception'] = array_unique(array_merge($event_info['master_event']['recur_exception'], array($event['recurrence']))); - + /* // Adjust the event start -- must not be an exception $length = $event_info['master_event']['end'] - $event_info['master_event']['start']; $rriter = calendar_rrule::event2rrule($event_info['master_event'], false); @@ -709,7 +710,7 @@ class calendar_sif extends calendar_boupdate $this->server2usertime($event_to_store); $this->update($event_to_store, true); unset($event_to_store); - } + } */ $event['reference'] = $event_info['master_event']['id']; $event['category'] = $event_info['master_event']['category']; $event['owner'] = $event_info['master_event']['owner']; @@ -914,6 +915,7 @@ class calendar_sif extends calendar_boupdate array2string($exceptions)."\n",3,$this->logfile); } $event['recur_exception'] = $exceptions; + /* // Adjust the event start -- must not be an exception $length = $event['end'] - $event['start']; $rriter = calendar_rrule::event2rrule($event, false, $tzid); @@ -927,7 +929,7 @@ class calendar_sif extends calendar_boupdate // remove leading exceptions if ($day <= $event['start']) unset($exceptions[$key]); } - $event['recur_exception'] = $exceptions; + $event['recur_exception'] = $exceptions; */ } if ($this->uidExtension) diff --git a/calendar/inc/class.calendar_so.inc.php b/calendar/inc/class.calendar_so.inc.php index 1a5094a132..b198599cd0 100644 --- a/calendar/inc/class.calendar_so.inc.php +++ b/calendar/inc/class.calendar_so.inc.php @@ -44,6 +44,7 @@ define('REJECTED',0); define('NO_RESPONSE',1); define('TENTATIVE',2); define('ACCEPTED',3); +define('DELEGATED',4); define('HOUR_s',60*60); define('DAY_s',24*HOUR_s); @@ -188,6 +189,18 @@ class calendar_so $this->db->update($this->cal_table, array('cal_uid' => $event['uid']), array('cal_id' => $event['id']),__LINE__,__FILE__,'calendar'); } + if ((int) $recur_date == 0 && + $event['recur_type'] != MCAL_RECUR_NONE && + !empty($event['recur_exception'])) + { + sort($event['recur_exception']); + if ($event['recur_exception'][0] < $event['start']) + { + // leading exceptions => move start and end + $event['end'] -= $event['start'] - $event['recur_exception'][0]; + $event['start'] = $event['recur_exception'][0]; + } + } } // check if we have a real recurance, if not set $recur_date=0 @@ -377,6 +390,8 @@ class calendar_so $where[] = "cal_status='T'"; break; case 'rejected': $where[] = "cal_status='R'"; break; + case 'delegated': + $where[] = "cal_status='D'"; break; case 'all': case 'owner': break; @@ -649,6 +664,8 @@ ORDER BY cal_user_type, cal_usre_id $minimum_uid_length = 8; } + $old_min = $old_duration = 0; + //echo '

'.__METHOD__.'('.array2string($event).",$change_since) event="; _debug_array($event); //error_log(__METHOD__.'('.array2string($event).",$set_recurrences,$change_since,$etag)"); @@ -827,7 +844,7 @@ ORDER BY cal_user_type, cal_usre_id // update start- and endtime if present in the event-array, evtl. we need to move all recurrences if (isset($event['cal_start']) && isset($event['cal_end'])) { - $this->move($cal_id,$event['cal_start'],$event['cal_end'],!$cal_id ? false : $change_since); + $this->move($cal_id,$event['cal_start'],$event['cal_end'],!$cal_id ? false : $change_since, $old_min, $old_min + $old_duration); } // update participants if present in the event-array if (isset($event['cal_participants'])) @@ -1157,7 +1174,8 @@ ORDER BY cal_user_type, cal_usre_id REJECTED => 'R', NO_RESPONSE => 'U', TENTATIVE => 'T', - ACCEPTED => 'A' + ACCEPTED => 'A', + DELEGATED => 'D' ); if (!(int)$cal_id || !(int)$user_id && $user_type != 'e') { @@ -1581,7 +1599,7 @@ ORDER BY cal_user_type, cal_usre_id * @param int $start=0 if != 0: startdate of the search/list (servertime) * @param int $end=0 if != 0: enddate of the search/list (servertime) * @param string $filter='all' string filter-name: all (not rejected), - * accepted, unknown, tentative, rejected, + * accepted, unknown, tentative, rejected, delegated * rrule return array of remote exceptions in servertime * tz_rrule/tz_only, return (only by) timezone transition affected entries * map return array of dates with no pseudo exception @@ -1607,15 +1625,46 @@ ORDER BY cal_user_type, cal_usre_id $remote = in_array($filter, array('tz_rrule', 'rrule')); $egw_rrule = calendar_rrule::event2rrule($event, false); - $egw_rrule->rewind(); + $egw_rrule->current = clone $egw_rrule->time; if ($expand_all) { unset($event['recur_excpetion']); $remote_rrule = calendar_rrule::event2rrule($event, false, $tz_id); - $remote_rrule->rewind(); + $remote_rrule->current = clone $remote_rrule->time; } while ($egw_rrule->valid()) { + while ($egw_rrule->exceptions && + in_array($egw_rrule->current->format('Ymd'),$egw_rrule->exceptions)) + { + if (in_array($filter, array('map','tz_map','rrule','tz_rrule'))) + { + // real exception + $locts = (int)egw_time::to($egw_rrule->current(),'server'); + if ($expand_all) + { + $remts = (int)egw_time::to($remote_rrule->current(),'server'); + if ($remote) + { + $days[$locts]= $remts; + } + else + { + $days[$remts]= $locts; + } + } + else + { + $days[$locts]= $locts; + } + } + if ($expand_all) + { + $remote_rrule->next_no_exception(); + } + $egw_rrule->next_no_exception(); + if (!$egw_rrule->valid()) return $days; + } $day = $egw_rrule->current(); $locts = (int)egw_time::to($day,'server'); $tz_exception = ($filter == 'tz_rrule'); @@ -1659,7 +1708,6 @@ ORDER BY cal_user_type, cal_usre_id // '() status exception: ' . $day->format('Ymd\THis')); if ($expand_all) { - $remts = (int)egw_time::to($remote_day,'server'); if ($filter == 'tz_only') { unset($days[$remts]); @@ -1699,40 +1747,11 @@ ORDER BY cal_user_type, cal_usre_id } } } - do + if ($expand_all) { - $egw_rrule->next_no_exception(); - $day = $egw_rrule->current(); - if ($expand_all) - { - $remote_rrule->next_no_exception(); - $remts = (int)egw_time::to($remote_rrule->current(),'server'); - } - $exception = $egw_rrule->exceptions && - in_array($day->format('Ymd'),$egw_rrule->exceptions); - if (in_array($filter, array('map','tz_map','rrule','tz_rrule')) - && $exception) - { - // real exception - $locts = (int)egw_time::to($day,'ts'); - if ($expand_all) - { - if ($remote) - { - $days[$locts]= $remts; - } - else - { - $days[$remts]= $locts; - } - } - else - { - $days[$locts]= $locts; - } - } + $remote_rrule->next_no_exception(); } - while ($exception); + $egw_rrule->next_no_exception(); } return $days; } @@ -1831,6 +1850,13 @@ ORDER BY cal_user_type, cal_usre_id continue; } break; + case 'delegated': + if ($status != 'D') + { + unset($participants[$uid]); + continue; + } + break; case 'default': if ($status == 'R') { diff --git a/calendar/inc/class.calendar_ui.inc.php b/calendar/inc/class.calendar_ui.inc.php index 2092dd515e..e9346b659d 100644 --- a/calendar/inc/class.calendar_ui.inc.php +++ b/calendar/inc/class.calendar_ui.inc.php @@ -146,7 +146,7 @@ class calendar_ui $this->accountsel = $GLOBALS['egw']->uiaccountsel; $this->categories = new categories($this->user,'calendar'); - + $this->common_prefs = &$GLOBALS['egw_info']['user']['preferences']['common']; $this->cal_prefs = &$GLOBALS['egw_info']['user']['preferences']['calendar']; $this->bo->check_set_default_prefs(); @@ -708,6 +708,7 @@ class calendar_ui 'accepted' => array(lang('Accepted'), lang('Show only accepted events')), 'unknown' => array(lang('Invitations'), lang('Show only invitations, not yet accepted or rejected')), 'tentative' => array(lang('Tentative'), lang('Show only tentative accepted events')), + 'delegated' => array(lang('Delegated'), lang('Show only delegated events')), 'rejected' => array(lang('Rejected'),lang('Show only rejected events')), 'owner' => array(lang('Owner too'),lang('Show also events just owned by selected user')), 'all' => array(lang('All incl. rejected'),lang('Show all status incl. rejected events')), diff --git a/calendar/templates/default/images/forward.gif b/calendar/templates/default/images/forward.gif new file mode 100644 index 0000000000000000000000000000000000000000..7a5df0b7572acce25f3050e3e3c17a6d2601a9e8 GIT binary patch literal 319 zcmZ?wbhEHb6E|9J%5>F;abPSbsh!FJPVdN z6|HkFTIXK8#;bI#TiJTQ^3}c->-}ps2h?o~YuXXpvOTV4Yi#Sb#EzZm6AxufI+Qo% zP|nmN|49dm{{)?jQWHy3QxwWGOEMJPJ$(Zh6o0ZXaxvI5=r8~Q$fFEw?g!=iq?Ap%+k(md=bD>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igJ= z3mz`%)+>Sl00C}EL_t(2&yA74OB-<%z(3#bosCk|4)GT>L=Y*62qFo%Sm{y>mWG0p zNVhKj1ByZxF@w7}b?)NeqP7UGZE+}~MG%6av>caK)1G(9d%1lD zFgCX?Pm>A$xd9+ol0tzPrcyl}tsI{e0^2^HHUXk~9mlEQc(oh1h=_`Jw(-}wr1ZP| zMQi=p1gQD}Eri4$YeYaRP%SY$KP2=k93K_;9%*yBu(RNs0Ipk^IXpTIX)ILJusoI; z84s#1#b5S&jhZj)EcpGIeB