* CalDAV/Calendar: fixed doublication of alarms when participant setting the alarm has no edit rights on the event

also fixed alarms set on a recurrence where lost, if you had edit rights
This commit is contained in:
Ralf Becker 2020-08-21 13:59:15 +02:00
parent b16b103639
commit 4205118f8d
2 changed files with 55 additions and 15 deletions

View File

@ -963,6 +963,7 @@ class calendar_groupdav extends Api\CalDAV\Handler
if (($events = $handler->icaltoegw($vCalendar))) if (($events = $handler->icaltoegw($vCalendar)))
{ {
$modified = 0; $modified = 0;
$master = null;
foreach($events as $n => $event) foreach($events as $n => $event)
{ {
// for recurrances of event series, we need to read correct recurrence (or if series master is no first event) // for recurrances of event series, we need to read correct recurrence (or if series master is no first event)
@ -1012,9 +1013,17 @@ class calendar_groupdav extends Api\CalDAV\Handler
if ((array)$event['alarm'] !== (array)$oldEvent['alarm']) if ((array)$event['alarm'] !== (array)$oldEvent['alarm'])
{ {
$event['id'] = $oldEvent['id']; $event['id'] = $oldEvent['id'];
if (isset($master) && $event['id'] == $master['id']) // only for pseudo exceptions
{
$modified += $handler->sync_alarms($event, (array)$oldEvent['alarm'], $user, $master);
}
else
{
$modified += $handler->sync_alarms($event, (array)$oldEvent['alarm'], $user); $modified += $handler->sync_alarms($event, (array)$oldEvent['alarm'], $user);
} }
} }
if (!isset($master) && !$event['recurrence']) $master = $event;
}
if (!$modified) // NO modififictions, or none we understood --> log it and return Ok: "204 No Content" if (!$modified) // NO modififictions, or none we understood --> log it and return Ok: "204 No Content"
{ {
$this->caldav->log(__METHOD__."(,,$user) NO changes for current user events=".array2string($events).', old-event='.array2string($oldEvent)); $this->caldav->log(__METHOD__."(,,$user) NO changes for current user events=".array2string($events).', old-event='.array2string($oldEvent));

View File

@ -203,7 +203,7 @@ class calendar_ical extends calendar_boupdate
* @param int|string $current_user =0 uid of current user to only export that one as participant for method=REPLY * @param int|string $current_user =0 uid of current user to only export that one as participant for method=REPLY
* @return string|boolean string with iCal or false on error (e.g. no permission to read the event) * @return string|boolean string with iCal or false on error (e.g. no permission to read the event)
*/ */
function &exportVCal($events, $version='1.0', $method='PUBLISH', $recur_date=0, $principalURL='', $charset='UTF-8', $current_user=0) function exportVCal($events, $version='1.0', $method='PUBLISH', $recur_date=0, $principalURL='', $charset='UTF-8', $current_user=0)
{ {
if ($this->log) if ($this->log)
{ {
@ -1191,7 +1191,7 @@ class calendar_ical extends calendar_boupdate
date_default_timezone_set($tzid); date_default_timezone_set($tzid);
$msg = null; $msg = $master = null;
foreach ($events as $event) foreach ($events as $event)
{ {
if (!is_array($event)) continue; // the iterator may return false if (!is_array($event)) continue; // the iterator may return false
@ -1515,7 +1515,7 @@ class calendar_ical extends calendar_boupdate
} }
// update alarms depending on the given event type // update alarms depending on the given event type
if (count($event['alarm']) > 0 || isset($this->supportedFields['alarm'])) if (count($event['alarm']) > 0 || isset($this->supportedFields['alarm']) && isset($event['alarm']))
{ {
switch ($event_info['type']) switch ($event_info['type'])
{ {
@ -1523,14 +1523,14 @@ class calendar_ical extends calendar_boupdate
case 'SERIES-MASTER': case 'SERIES-MASTER':
case 'SERIES-EXCEPTION': case 'SERIES-EXCEPTION':
case 'SERIES-EXCEPTION-PROPAGATE': case 'SERIES-EXCEPTION-PROPAGATE':
if (isset($event['alarm']))
{
$this->sync_alarms($event, (array)$event_info['stored_event']['alarm'], $this->user); $this->sync_alarms($event, (array)$event_info['stored_event']['alarm'], $this->user);
} if ($event_info['type'] === 'SERIES-MASTER') $master = $event;
break; break;
case 'SERIES-PSEUDO-EXCEPTION': case 'SERIES-PSEUDO-EXCEPTION':
// nothing to do here // only sync alarms for pseudo exceptions, if we have a master
if (!isset($master) && isset($event_info['master_event'])) $master = $event_info['master_event'];
if ($master) $this->sync_alarms($event, (array)$event_info['stored_event']['alarm'], $this->user, $master);
break; break;
} }
} }
@ -1854,14 +1854,18 @@ class calendar_ical extends calendar_boupdate
/** /**
* Sync alarms of current user: add alarms added on client and remove the ones removed * Sync alarms of current user: add alarms added on client and remove the ones removed
* *
* Currently alarms of pseudo exceptions will always be added to series master (and searched for there too)!
* Alarms are search by uid first, but if no uid match found, we also consider same offset as a match.
*
* @param array& $event * @param array& $event
* @param array $old_alarms * @param array $old_alarms
* @param int $user account_id of user to create alarm for * @param int $user account_id of user to create alarm for
* @param array $master =null master for pseudo exceptions
* @return int number of modified alarms * @return int number of modified alarms
*/ */
public function sync_alarms(array &$event, array $old_alarms, $user) public function sync_alarms(array &$event, array $old_alarms, $user, array &$master=null)
{ {
if ($this->debug) error_log(__METHOD__."(".array2string($event).', old_alarms='.array2string($old_alarms).", $user,)"); if ($this->debug) error_log(__METHOD__."(".array2string($event).', old_alarms='.array2string($old_alarms).", $user, master=".")");
$modified = 0; $modified = 0;
foreach($event['alarm'] as &$alarm) foreach($event['alarm'] as &$alarm)
{ {
@ -1875,13 +1879,25 @@ class calendar_ical extends calendar_boupdate
unset($old_alarm[$id]); unset($old_alarm[$id]);
continue; continue;
} }
// alarm found --> stop // alarm with matching uid found --> stop
if (empty($alarm['uid']) && $alarm['offset'] == $old_alarm['offset'] || $alarm['uid'] && $alarm['uid'] == $old_alarm['uid']) if (!empty($alarm['uid']) && $alarm['uid'] == $old_alarm['uid'])
{ {
$found = true; $found = true;
unset($old_alarms[$id]); unset($old_alarms[$id]);
break; break;
} }
// alarm only matching offset, remember first matching one, in case no uid match
if ($alarm['offset'] == $old_alarm['offset'] && $found === false)
{
$found = $id;
}
}
// no uid, but offset match found --> use it like an uid match
if (!is_bool($found))
{
$found = true;
$old_alarm = $old_alarms[$id];
unset($old_alarms[$id]);
} }
if ($this->debug) error_log(__METHOD__."($event[title] (#$event[id]), ..., $user) processing ".($found?'existing':'new')." alarm ".array2string($alarm)); if ($this->debug) error_log(__METHOD__."($event[title] (#$event[id]), ..., $user) processing ".($found?'existing':'new')." alarm ".array2string($alarm));
if (!empty($alarm['attrs']['X-LIC-ERROR'])) if (!empty($alarm['attrs']['X-LIC-ERROR']))
@ -1889,14 +1905,29 @@ class calendar_ical extends calendar_boupdate
if ($this->debug) error_log(__METHOD__."($event[title] (#$event[id]), ..., $user) ignored X-LIC-ERROR=".array2string($alarm['X-LIC-ERROR'])); if ($this->debug) error_log(__METHOD__."($event[title] (#$event[id]), ..., $user) ignored X-LIC-ERROR=".array2string($alarm['X-LIC-ERROR']));
unset($alarm['attrs']['X-LIC-ERROR']); unset($alarm['attrs']['X-LIC-ERROR']);
} }
// alarm not found --> add it // search not found alarm in master
if ($master && !$found)
{
foreach($master['alarm'] ?? [] as $m_alarm)
{
if ($alarm['offset'] == $m_alarm['offset'] &&
($m_alarm['all'] || $m_alarm['owner'] == $user))
{
if ($this->debug) error_log(__METHOD__."() alarm of pseudo exception already in master --> ignored alarm=".json_encode($alarm).", master=".json_encode($m_alarm));
continue 2;
}
}
}
// alarm not found --> add it (to master as $event['id'] is the one from the master)
if (!$found) if (!$found)
{ {
$alarm['owner'] = $user; $alarm['owner'] = $user;
if (!isset($alarm['time'])) $alarm['time'] = $event['start'] - $alarm['offset']; if (!isset($alarm['time'])) $alarm['time'] = $event['start'] - $alarm['offset'];
if ($alarm['time'] < time()) calendar_so::shift_alarm($event, $alarm); if ($alarm['time'] < time()) calendar_so::shift_alarm($event, $alarm);
if ($this->debug) error_log(__METHOD__."() adding new alarm from client ".array2string($alarm)); if ($this->debug) error_log(__METHOD__."() adding new alarm from client ".array2string($alarm));
if ($event['id']) $alarm['id'] = $this->save_alarm($event['id'], $alarm); if ($event['id'] || $master) $alarm['id'] = $this->save_alarm($event['id'] ?? $master['id'], $alarm);
// adding alarm to master to be found for further pseudo exceptions (it will be added to DB below)
if ($master) $master['alarm'][] = $alarm;
++$modified; ++$modified;
} }
// existing alarm --> update it // existing alarm --> update it