From 1509f30971dbce450bd00ee572a9557008dccce4 Mon Sep 17 00:00:00 2001 From: ralf Date: Tue, 9 Jul 2024 21:39:40 +0200 Subject: [PATCH] show changes in participants, new and old start-date for exceptions only sending the concerned recurrence, instead of the whole series, so user understands what has changed --- calendar/inc/class.calendar_boupdate.inc.php | 67 +++++++++++++++++++- calendar/inc/class.calendar_uiforms.inc.php | 52 +++++++++------ calendar/templates/default/meeting.xet | 11 ++-- 3 files changed, 102 insertions(+), 28 deletions(-) diff --git a/calendar/inc/class.calendar_boupdate.inc.php b/calendar/inc/class.calendar_boupdate.inc.php index 5e3cfe73e1..7fe27dce76 100644 --- a/calendar/inc/class.calendar_boupdate.inc.php +++ b/calendar/inc/class.calendar_boupdate.inc.php @@ -570,10 +570,10 @@ class calendar_boupdate extends calendar_bo * * @param array $new_event the updated event * @param array $old_event the event before the update + * @param int $notify_max_recurrences notify only about this number of single recurrences at maximum */ - function check4update($new_event,$old_event) + function check4update($new_event, $old_event, int $notify_max_recurrences=3) { - //error_log(__METHOD__."($new_event[title])"); $modified = $added = $deleted = array(); //echo "

calendar_boupdate::check4update() new participants = ".print_r($new_event['participants'],true).", old participants =".print_r($old_event['participants'],true)."

\n"; @@ -582,6 +582,63 @@ class calendar_boupdate extends calendar_bo { if($new_event[$field] !== $old_event[$field]) { + $n = 0; + switch($field) + { + case 'recur_exception': + foreach ($added_exceptions=array_diff($new_event[$field]??[],$old_event[$field]??[]) as $key => $recurrence) + { + if ($this->read($new_event['uid'], $recurrence)) + { + unset($added_exceptions[$key]); + continue; // explicit exception --> no need to notify, already done for the exception itself + } + // send a cancel for each RECURRENCE-ID + $this->send_update(MSG_DELETED, $new_event['participants'], [ + 'recurrence' => $recurrence, + 'start' => $recurrence, + 'end' => $recurrence + $new_event['end'] - $new_event['start'], + ]+$new_event); + // limit number of notifications to the first N recurrences + if (++$n >= $notify_max_recurrences) break; + } + $n = 0; + foreach($removed_exceptions=array_diff($old_event[$field]??[],$new_event[$field]??[]) as $key => $recurrence) + { + if ($this->read($new_event['uid'], $recurrence)) + { + unset($removed_exceptions[$key]); + continue; // explicit exception --> no need to notify, already done for the exception itself + } + // send an add for each RECURRENCE-ID + $this->send_update(MSG_ADDED, $new_event['participants'], null, [ + 'recurrence' => $recurrence, + 'start' => $recurrence, + 'end' => $recurrence + $new_event['end'] - $new_event['start'], + ]+$new_event); + // limit number of notifications to the first N recurrences + if (++$n >= $notify_max_recurrences) break; + } + continue 2; + + case 'recur_rdates': + foreach(array_diff($new_event[$field]??[],$old_event[$field]??[]) as $recurrence) + { + // send an add for each RECURRENCE-ID + $this->send_update(MSG_ADDED, $new_event['participants'], null, [ + 'recurrence' => $recurrence, + 'start' => $recurrence, + 'end' => $recurrence + $new_event['end'] - $new_event['start'], + ]+$new_event); + // limit number of notifications to the first N recurrences + if (++$n >= $notify_max_recurrences) break; + } + continue 2; + + case 'recur_enddate': + if ($new_event['recur_type'] == MCAL_RECUR_RDATE) continue 2; // already handled above + break; + } $modified = $new_event['participants']; break; } @@ -1288,7 +1345,7 @@ class calendar_boupdate extends calendar_bo 'videoconference' => $details['videoconference'], ), $event['id']); } - if ($m_type === MSG_ALARM) + elseif ($m_type === MSG_ALARM) { $notification->set_popupdata('calendar', array('egw_pr_notify' => 1, @@ -1299,6 +1356,10 @@ class calendar_boupdate extends calendar_bo ) + ($alarm ? ['alarm-offset' => (int)$alarm['offset']] : []), $event['id']); } + else + { + $notification->set_popupdata('calendar', null, $event['id']); + } $notification->set_popupmessage($subject . "\n\n" . $notify_body . "\n\n" . $details['description'] . "\n\n" . $details_body . "\n\n"); $notification->set_popuplinks(array($details['link_arr'] + array('app' => 'calendar'))); diff --git a/calendar/inc/class.calendar_uiforms.inc.php b/calendar/inc/class.calendar_uiforms.inc.php index c664c85727..69c04ec054 100644 --- a/calendar/inc/class.calendar_uiforms.inc.php +++ b/calendar/inc/class.calendar_uiforms.inc.php @@ -2225,14 +2225,6 @@ class calendar_uiforms extends calendar_ui // convert event from servertime returned by calendar_ical to user-time $this->bo->server2usertime($event); - // Check if this is an exception - if($event['recur_type'] && count($event['recur_exception']) && !$event['recurrence']) - { - $diff = $event['recur_exception'][0] - $event['start']; - $event['start'] += $diff; - $event['end'] += $diff; - } - if (($existing_event = $this->bo->read($event['uid'], $event['recurrence'], false, 'ts', null, true))) // true = read the exception { // check if mail is from extern organizer @@ -2251,6 +2243,20 @@ class calendar_uiforms extends calendar_ui { $master = $this->bo->read($event['uid']); } + $all_participants = ($event['participants'] ?? []) + ($existing_event['participants'] ?? []); + $event['participantChanges'] = array_map(function($uid, $status) use ($existing_event, $event) { + return [ + 'changed' => !isset($event['participants'][$uid]) ? 'meetingRequestParticipantDeleted' : + (!isset($existing_event['participants'][$uid]) ? 'meetingRequestChanged' : + ($status !== $existing_event['participants'][$uid] ? 'meetingRequestChangedStatus' : '')), + 'label' => $this->bo->participant_name($uid), + 'status' => lang($this->bo->verbose_status[calendar_so::split_status($status, $quantity, $role)]), + 'role' => $role === 'REQ-PARTICIPANT' ? '' : lang($this->bo->roles[$role] ?? + (substr($role,0,6) === 'X-CAT-' && ($cat_id = (int)substr($role,6)) > 0 ? + $GLOBALS['egw']->categories->id2name($cat_id) : str_replace('X-','',$role))), + ]; + }, array_keys($all_participants), $all_participants); + switch(strtolower($ical_method)) { case 'reply': @@ -2338,22 +2344,14 @@ class calendar_uiforms extends calendar_ui $readonlys['button[reject]'] = $readonlys['button[cancel]'] = true; } } - $all_participants = ($event['participants'] ?? []) + ($existing_event['participants'] ?? []); - $event['participantChanges'] = array_map(function($uid, $status) use ($existing_event, $event) { - return [ - 'changed' => !isset($event['participants'][$uid]) ? 'meetingRequestParticipantDeleted' : - (!isset($existing_event['participants'][$uid]) ? 'meetingRequestChanged' : - ($status !== $existing_event['participants'][$uid] ? 'meetingRequestChangedStatus' : '')), - 'label' => $this->bo->participant_name($uid), - 'status' => lang($this->bo->verbose_status[calendar_so::split_status($status, $quantity, $role)]), - 'role' => $role === 'REQ-PARTICIPANT' ? '' : lang($this->bo->roles[$role] ?? - (substr($role,0,6) === 'X-CAT-' && ($cat_id = (int)substr($role,6)) > 0 ? - $GLOBALS['egw']->categories->id2name($cat_id) : str_replace('X-','',$role))), - ]; - }, array_keys($all_participants), $all_participants); break; case 'cancel': // first participant is the (external) organizer (our iCal parser adds owner first!) + $event['changed'] = [ + 'start' => 'meetingRequestChangedValue', + 'end' => 'meetingRequestChangedValue', + 'participants' => 'meetingRequestParticipantDeleted', + ]; $parts = $event['participants'] ?? []; unset($parts[$existing_event['owner']]); $event['ical_sender_uid'] = key($parts); @@ -2600,6 +2598,18 @@ class calendar_uiforms extends calendar_ui unset($changes['recure']); } + // if start changed, make old start-time available to template + if (isset($changes['start']) && $_event['start'] != $changes['start']) + { + $_event['old_start'] = $changes['start']; + } + // if we have an explicit exception, always report that as a change from the regular recurrence + elseif (!empty($_event['recurrence']) && empty($_event['recur_type']) && $_event['recurrence'] != $_event['start']) + { + $_event['old_start'] = $changes['start'] = $_event['recurrence']; + $changes['end'] = $_event['recurrence'] + $_event['end']-$_event['start']; + } + return $changes; } diff --git a/calendar/templates/default/meeting.xet b/calendar/templates/default/meeting.xet index 6ff8511598..270b616868 100644 --- a/calendar/templates/default/meeting.xet +++ b/calendar/templates/default/meeting.xet @@ -78,7 +78,7 @@ - + @@ -90,6 +90,7 @@ + @@ -111,7 +112,7 @@ - + @@ -169,6 +170,7 @@ } et2-hbox.dates { padding-left: 5px; + width: fit-content; } .meetingWarning tr.th { background-color: #dc2625; @@ -176,7 +178,7 @@ .meetingWarning tr.row td { font-size: 120% !important; } - .meetingRequestError, .meetingRequestChanged, .meetingRequestChangedStatus et2-description[id$=status\]], .meetingRequestParticipantDeleted { + .meetingRequestError, .meetingRequestChanged, .meetingRequestChangedStatus et2-description[id$=status\]] { font-style: italic; color: red; } @@ -184,7 +186,8 @@ padding: 0 !important; padding-left: 0 !important; } - .meetingRequestParticipantDeleted { + .meetingRequestParticipantDeleted, .meetingRequestChangedValue { + font-style: italic; text-decoration: line-through; }