From 1c6ca4eb7a881b92494bfd7c7f37cfc972ce4c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Wed, 24 Feb 2010 14:53:15 +0000 Subject: [PATCH] Fix various recurring event issues --- calendar/inc/class.calendar_boupdate.inc.php | 7 +- calendar/inc/class.calendar_ical.inc.php | 149 +++++++++++-------- calendar/inc/class.calendar_sif.inc.php | 3 +- calendar/inc/class.calendar_so.inc.php | 54 +++++-- calendar/inc/class.calendar_uiforms.inc.php | 26 +--- 5 files changed, 130 insertions(+), 109 deletions(-) diff --git a/calendar/inc/class.calendar_boupdate.inc.php b/calendar/inc/class.calendar_boupdate.inc.php index 2909986aeb..a4e0235648 100644 --- a/calendar/inc/class.calendar_boupdate.inc.php +++ b/calendar/inc/class.calendar_boupdate.inc.php @@ -129,9 +129,6 @@ class calendar_boupdate extends calendar_bo if (!$new_event) { $old_event = $this->read((int)$event['id'],null,$ignore_acl); - // if no participants are set, set them from the old event, as we might need them to update recuring events - if (!isset($event['participants'])) $event['participants'] = $old_event['participants']; - //echo "old $event[id]="; _debug_array($old_event); } // check for conflicts only happens !$ignore_conflicts AND if start + end date are given @@ -144,7 +141,7 @@ class calendar_boupdate extends calendar_bo } // get all NOT rejected participants and evtl. their quantity $quantity = $users = array(); - foreach($event['participants'] as $uid => $status) + foreach((array)$event['participants'] as $uid => $status) { calendar_so::split_status($status,$q,$r); if ($status[0] == 'R') continue; // ignore rejected participants @@ -1091,7 +1088,7 @@ class calendar_boupdate extends calendar_bo } $cat_id_list = array(); - foreach ($catname_list as $cat_name) + foreach ((array)$catname_list as $cat_name) { $cat_name = trim($cat_name); $cat_id = $this->categories->name2id($cat_name, 'X-'); diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index 0ead233939..c7e4c0ad90 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -236,6 +236,7 @@ class calendar_ical extends calendar_boupdate $vcal->setAttribute('VERSION', $version); $vcal->setAttribute('METHOD', $method); $serverTZ = false; + $events_exported = false; if (!is_array($events)) $events = array($events); @@ -405,7 +406,8 @@ class calendar_ical extends calendar_boupdate $exceptions = array(); // dont use "virtual" exceptions created by participant status for GroupDAV or file export - if (!in_array($this->productManufacturer,array('file','groupdav'))) + if (!in_array($this->productManufacturer,array('file','groupdav')) && + isset($this->supportedFields['participants'])) { $exceptions = $this->so->get_recurrence_exceptions($event); if ($this->log) @@ -983,9 +985,10 @@ class calendar_ical extends calendar_boupdate } } $vcal->addComponent($vevent); + $events_exported = true; } - $retval = $vcal->exportvCalendar(); + $retval = $events_exported ? $vcal->exportvCalendar() : false; if ($this->log) { error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ . @@ -1097,7 +1100,7 @@ class calendar_ical extends calendar_boupdate if (!isset($event['participants'][$uid])) { // Add it back in - $event['participants'][$uid] = $event['participant_types']['r'][substr($uid,1)] = $status; + $event['participants'][$uid] = $status; } } break; @@ -1110,7 +1113,14 @@ class calendar_ical extends calendar_boupdate else { // no merge - if (!isset($this->supportedFields['participants']) || !count($event['participants'])) + if(!isset($this->supportedFields['category'])) + { + $event['category'] = $event_info['stored_event']['category']; + } + if (!isset($this->supportedFields['participants']) + || !$event['participants'] + || !is_array($event['participants']) + || !count($event['participants'])) { if ($this->log) { @@ -1121,16 +1131,14 @@ class calendar_ical extends calendar_boupdate // If this is an updated meeting, and the client doesn't support // participants OR the event no longer contains participants, add them back unset($event['participants']); - unset($event['participant_types']); } else { - // if the client does not return a status, we restore the original one foreach ($event['participants'] as $uid => $status) { - // Is it a resource and no longer present in the event? if ($status[0] == 'X') { + // the client did not give us a proper status => restore original if (isset($event_info['stored_event']['participants'][$uid])) { if ($this->log) @@ -1146,20 +1154,19 @@ class calendar_ical extends calendar_boupdate } } } - } - - foreach ($event_info['stored_event']['participants'] as $uid => $status) - { - // Is it a resource and no longer present in the event? - if ($uid[0] == 'r' && !isset($event['participants'][$uid])) + foreach ($event_info['stored_event']['participants'] as $uid => $status) { - if ($this->log) + // Is it a resource and no longer present in the event? + if ($uid[0] == 'r' && !isset($event['participants'][$uid])) { - error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. - "() Restore resource $uid to status $status\n",3,$this->logfile); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. + "() Restore resource $uid to status $status\n",3,$this->logfile); + } + // Add it back in + $event['participants'][$uid] = $status; } - // Add it back in - $event['participants'][$uid] = $event['participant_types']['r'][substr($uid,1)] = $status; } } @@ -1184,7 +1191,9 @@ class calendar_ical extends calendar_boupdate $event['owner'] = $this->user; } - if (!is_array($event['participants']) || !count($event['participants'])) + if (!$event['participants'] + || !is_array($event['participants']) + || !count($event['participants'])) { $status = $event['owner'] == $this->user ? 'A' : 'U'; $status = calendar_so::combine_status($status, 1, 'CHAIR'); @@ -1194,7 +1203,7 @@ class calendar_ical extends calendar_boupdate { foreach ($event['participants'] as $uid => $status) { - // Is it a resource and no longer present in the event? + // if the client did not give us a proper status => set default if ($status[0] == 'X') { if ($uid == $event['owner']) @@ -1412,48 +1421,50 @@ class calendar_ical extends calendar_boupdate $event_info['stored_event'] = $this->read($updated_id, 0, false, 'server'); } - // update status depending on the given event type - switch ($event_info['type']) + if (isset($event['participants'])) { - case 'SINGLE': - case 'SERIES-MASTER': - case 'SERIES-EXCEPTION': - case 'SERIES-EXCEPTION-PROPAGATE': - if (is_array($event_info['stored_event'])) // status update requires a stored event - { - if ($event_info['acl_edit']) + // update status depending on the given event type + switch ($event_info['type']) + { + case 'SINGLE': + case 'SERIES-MASTER': + case 'SERIES-EXCEPTION': + case 'SERIES-EXCEPTION-PROPAGATE': + if (is_array($event_info['stored_event'])) // status update requires a stored event { - // update all participants if we have the right to do that - $this->update_status($event, $event_info['stored_event']); + if ($event_info['acl_edit']) + { + // update all participants if we have the right to do that + $this->update_status($event, $event_info['stored_event']); + } + elseif (isset($event['participants'][$this->user]) || isset($event_info['stored_event']['participants'][$this->user])) + { + // update the users status only + $this->set_status($event_info['stored_event']['id'], $this->user, + ($event['participants'][$this->user] ? $event['participants'][$this->user] : 'R'), 0, true); + } } - elseif (isset($event['participants'][$this->user]) || isset($event_info['stored_event']['participants'][$this->user])) - { - // update the users status only - $this->set_status($event_info['stored_event']['id'], $this->user, - ($event['participants'][$this->user] ? $event['participants'][$this->user] : 'R'), 0, true); - } - } - break; + break; - case 'SERIES-PSEUDO-EXCEPTION': - if (is_array($event_info['master_event'])) // status update requires a stored master event - { - $recurrence = $this->date2usertime($event['reference']); - if ($event_info['acl_edit']) + case 'SERIES-PSEUDO-EXCEPTION': + if (is_array($event_info['master_event'])) // status update requires a stored master event { - // update all participants if we have the right to do that - $this->update_status($event, $event_info['stored_event'], $recurrence); + $recurrence = $this->date2usertime($event['reference']); + if ($event_info['acl_edit']) + { + // update all participants if we have the right to do that + $this->update_status($event, $event_info['stored_event'], $recurrence); + } + elseif (isset($event['participants'][$this->user]) || isset($event_info['master_event']['participants'][$this->user])) + { + // update the users status only + $this->set_status($event_info['master_event']['id'], $this->user, + ($event['participants'][$this->user] ? $event['participants'][$this->user] : 'R'), $recurrence, true); + } } - elseif (isset($event['participants'][$this->user]) || isset($event_info['master_event']['participants'][$this->user])) - { - // update the users status only - $this->set_status($event_info['master_event']['id'], $this->user, - ($event['participants'][$this->user] ? $event['participants'][$this->user] : 'R'), $recurrence, true); - } - } - break; + break; + } } - // choose which id to return to the client switch ($event_info['type']) { @@ -1618,6 +1629,11 @@ class calendar_ical extends calendar_boupdate } } + if (in_array($this->productManufacturer,array('file','groupdav'))) + { + $this->useServerTZ = true; + } + if ($this->log) { error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. @@ -1761,6 +1777,7 @@ class calendar_ical extends calendar_boupdate case 'd750i': case 'p910i': case 'g705i': + case 'w890i': $this->supportedFields = $defaultFields['basic']; break; default: @@ -2295,7 +2312,7 @@ class calendar_ical extends calendar_boupdate case 'UID': if (strlen($attributes['value']) >= $minimum_uid_length) { - $event['uid'] = $vcardData['uid'] = $attributes['value']; + $vcardData['uid'] = $attributes['value']; } break; case 'TRANSP': @@ -2406,7 +2423,7 @@ class calendar_ical extends calendar_boupdate { //Horde::logMessage("vevent2egw: set status to " . $status, // __FILE__, __LINE__, PEAR_LOG_DEBUG); - $event['participants'][$this->user] = + $vcardData['participants'][$this->user] = calendar_so::combine_status($status); } $status = 'U'; // keep the group @@ -2442,12 +2459,12 @@ class calendar_ical extends calendar_boupdate switch($attributes['name']) { case 'ATTENDEE': - if (!isset($event['participants'][$uid]) || - $event['participants'][$uid][0] != 'A') + if (!isset($vcardData['participants'][$uid]) || + $vcardData['participants'][$uid][0] != 'A') { // for multiple entries the ACCEPT wins // add quantity and role - $event['participants'][$uid] = + $vcardData['participants'][$uid] = calendar_so::combine_status($status, $attributes['params']['X-EGROUPWARE-QUANTITY'], $attributes['params']['ROLE']); @@ -2455,11 +2472,11 @@ class calendar_ical extends calendar_boupdate break; case 'ORGANIZER': - if (isset($event['participants'][$uid])) + if (isset($vcardData['participants'][$uid])) { - $status = $event['participants'][$uid]; + $status = $vcardData['participants'][$uid]; calendar_so::split_status($status, $quantity, $role); - $event['participants'][$uid] = + $vcardData['participants'][$uid] = calendar_so::combine_status($status, $quantity, 'CHAIR'); } if (is_numeric($uid) && ($uid == $this->calendarOwner || !$this->calendarOwner)) @@ -2471,10 +2488,10 @@ class calendar_ical extends calendar_boupdate { // we must insert a CHAIR participant to keep the ORGANIZER $event['owner'] = $this->user; - if (!isset($event['participants'][$uid])) + if (!isset($vcardData['participants'][$uid])) { // save the ORGANIZER as event CHAIR - $event['participants'][$uid] = + $vcardData['participants'][$uid] = calendar_so::combine_status('U', 1, 'CHAIR'); } } @@ -2674,6 +2691,8 @@ class calendar_ical extends calendar_boupdate */ function update_status($new_event, $old_event , $recur_date=0) { + if (!isset($new_event['participants'])) return; + // check the old list against the new list foreach ($old_event['participants'] as $userid => $status) { diff --git a/calendar/inc/class.calendar_sif.inc.php b/calendar/inc/class.calendar_sif.inc.php index 855101c4a8..ba1ea0737f 100644 --- a/calendar/inc/class.calendar_sif.inc.php +++ b/calendar/inc/class.calendar_sif.inc.php @@ -420,7 +420,7 @@ class calendar_sif extends calendar_boupdate // overwrite with server data for merge foreach ($event_info['stored_event'] as $key => $value) { - if (in_array($key, array('participants', 'participant_types'))) + if ($key == 'participants') { unset($event[$key]); continue; @@ -433,7 +433,6 @@ class calendar_sif extends calendar_boupdate // not merge // SIF clients do not support participants => add them back unset($event['participants']); - unset($event['participant_types']); // avoid that iCal changes the organizer, which is not allowed $event['owner'] = $event_info['stored_event']['owner']; diff --git a/calendar/inc/class.calendar_so.inc.php b/calendar/inc/class.calendar_so.inc.php index cab232f19a..a0318743fa 100644 --- a/calendar/inc/class.calendar_so.inc.php +++ b/calendar/inc/class.calendar_so.inc.php @@ -659,16 +659,36 @@ ORDER BY cal_user_type, cal_usre_id $this->db->update($this->cal_table, array('cal_uid' => $event['cal_uid']), array('cal_id' => $event['cal_id']),__LINE__,__FILE__,'calendar'); } - // write information about recuring event, if recur_type is present in the array - if (isset($event['recur_type'])) + // write information about recurring event + if ($event['recur_type'] != MCAL_RECUR_NONE) { // fetch information about the currently saved (old) event $old_min = (int) $this->db->select($this->dates_table,'MIN(cal_start)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchColumn(); $old_duration = (int) $this->db->select($this->dates_table,'MIN(cal_end)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchColumn() - $old_min; $old_repeats = $this->db->select($this->repeats_table,'*',array('cal_id' => $cal_id),__LINE__,__FILE__,false,'','calendar')->fetch(); $old_exceptions = $old_repeats['recur_exception'] ? explode(',',$old_repeats['recur_exception']) : array(); + if (!empty($old_exceptions)) + { + sort($old_exceptions); + if ($old_min > $old_exceptions[0]) $old_min = $old_exceptions[0]; + } $event['recur_exception'] = is_array($event['recur_exception']) ? $event['recur_exception'] : array(); + if (!empty($event['recur_exception'])) + { + sort($event['recur_exception']); + } + + $where = array('cal_id' => $cal_id, + 'cal_recur_date' => 0); + $old_participants = array(); + foreach ($this->db->select($this->user_table,'cal_user_type,cal_user_id,cal_status,cal_quantity', $where, + __LINE__,__FILE__,false,'','calendar') as $row) + { + $uid = self::combine_user($row['cal_user_type'], $row['cal_user_id']); + $status = self::combine_status($row['cal_status'], $row['cal_quantity']); + $old_participants[$uid] = $status; + } // re-check: did so much recurrence data change that we have to rebuild it from scratch? if (!$set_recurrences) @@ -699,16 +719,18 @@ ORDER BY cal_user_type, cal_usre_id // deleted exceptions: re-insert recurrences into the user and dates table if(count($deleted_exceptions = array_diff($old_exceptions,$event['recur_exception']))) { + if (isset($event['cal_participants'])) + { + $participants = $event['cal_participants']; + } + else + { + // use old default + $participants = $old_participants; + } foreach($deleted_exceptions as $id => $deleted_exception) { // rebuild participants for the re-inserted recurrence - $participants = array(); - $participants_only = $this->get_participants($cal_id); // participants without states - foreach($participants_only as $id => $participant_only) - { - $states = $this->get_recurrences($cal_id, $participant_only['uid']); - $participants[$participant_only['uid']] = $states[0]; // insert main status as default - } $this->recurrence($cal_id, $deleted_exception, $deleted_exception + $old_duration, $participants); } } @@ -959,11 +981,12 @@ ORDER BY cal_user_type, cal_usre_id /** * updates the participants of an event, taken into account the evtl. recurrences of the event(!) * this method just adds new participants or removes not longer set participants - * this method does never overwrite existing entries (except for delete) + * this method does never overwrite existing entries (except the 0-recurrence and for delete) * * @param int $cal_id * @param array $participants uid => status pairs - * @param int|boolean $change_since=0, false=new entry, 0=all, > 0 time from which on the repetitions should be changed + * @param int|boolean $change_since=0, false=new event, + * 0=all, > 0 time from which on the repetitions should be changed * @param boolean $add_only=false * false = add AND delete participants if needed (full list of participants required in $participants) * true = only add participants if needed, no participant will be deleted (participants to check/add required in $participants) @@ -1049,13 +1072,13 @@ ORDER BY cal_user_type, cal_usre_id // update participants foreach($participants as $uid => $status) { - $id = null; + $type = $id = $quantity = $role = null; self::split_user($uid,$type,$id); self::split_status($status,$quantity,$role); $set = array( 'cal_status' => $status, 'cal_quantity' => $quantity, - 'cal_role' => $role, + //'cal_role' => $role, ); foreach($recurrences as $recur_date) { @@ -1149,12 +1172,11 @@ ORDER BY cal_user_type, cal_usre_id 'cal_start' => $start, ),__LINE__,__FILE__,'calendar'); - foreach($participants as $uid => $status) + foreach ($participants as $uid => $status) { if ($status == 'G') continue; // dont save group-invitations - $type = ''; - $id = null; + $type = $id = null; self::split_user($uid,$type,$id); $this->db->insert($this->user_table,array( 'cal_status' => $status !== true ? $status[0] : 'U', diff --git a/calendar/inc/class.calendar_uiforms.inc.php b/calendar/inc/class.calendar_uiforms.inc.php index 9a498f0bfb..e52b623338 100644 --- a/calendar/inc/class.calendar_uiforms.inc.php +++ b/calendar/inc/class.calendar_uiforms.inc.php @@ -235,7 +235,7 @@ class calendar_uiforms extends calendar_ui $event['end']['hour'] = 23; $event['end']['minute'] = $event['end']['second'] = 59; unset($event['end']['raw']); $event['end'] = $this->bo->date2ts($event['end']); } - // some checks for recurances, if you give a date, make it a weekly repeating event and visa versa + // some checks for recurrences, if you give a date, make it a weekly repeating event and visa versa if ($event['recur_type'] == MCAL_RECUR_NONE && $event['recur_data']) $event['recur_type'] = MCAL_RECUR_WEEKLY; if ($event['recur_type'] == MCAL_RECUR_WEEKLY && !$event['recur_data']) { @@ -643,24 +643,6 @@ class calendar_uiforms extends calendar_ui } } } - /* - // Update the stati - foreach ($participants as $uid => $status) - { - if (!isset($old_event['participants'][$uid]) - || !$old_event['participants'][$uid] - || $old_event['participants'][$uid][0] != $status) - { - $this->bo->set_status($old_event['id'], $uid, $status, $content['edit_single']); - } - unset($old_event['participants'][$uid]); - } - foreach ($old_event['participants'] as $uid => $status) - { - // delete the removed participants - $this->bo->set_status($old_event['id'], $uid, 'G', $content['edit_single']); - } - */ if ($event['recur_type'] != MCAL_RECUR_NONE) { // remove deleted exception @@ -963,13 +945,15 @@ class calendar_uiforms extends calendar_ui $preserv['actual_date'] = $event['start']; // remember the date clicked if ($event['recur_type'] != MCAL_RECUR_NONE) { - $participants = array('participants' => $event['participants'], 'participant_types' => $event['participant_types']); // preserv participants of this event - $event = array_merge($this->bo->read($cal_id,0,true), $participants); // recuring event --> read the series + concatenate with participants of the selected recurrence // check if we should create an exception if ($_GET['exception']) { $msg = $this->_create_exception($event,$preserv); } + else + { + $event = $this->bo->read($cal_id,0,true); + } } } // set new start and end if given by $_GET