* Calendar/CalDAV: do not allow to resurrect a deleted meeting by accepting it again via CalDAV or meeting-request from mail app

This commit is contained in:
ralf 2024-03-13 17:25:22 +02:00
parent a8222ed710
commit 0846fa78f3
3 changed files with 107 additions and 40 deletions

View File

@ -1067,6 +1067,25 @@ class calendar_groupdav extends Api\CalDAV\Handler
}
}
// if path not found, check the UID and return "403 Forbidden" if event is deleted or user has not rights to event with same UID
if (!isset($oldEvent) && ($events = $handler->icaltoegw($vCalendar)) &&
($oldEvents = $this->bo->read(['cal_uid' => $events[0]['uid'], 'cal_reference=0'], null, false, 'server')) !== null)
{
foreach($oldEvents as $oldEvent)
{
if (empty($oldEvent['deleted']))
{
break;
}
}
if (!empty($oldEvent['deleted']))
{
$this->caldav->log("Event with UID='{$events[0]['uid']}' has already been deleted!");
return '403 Forbidden';
}
// case user has no edit-rights for $oldEvent is handled below
}
if (is_array($oldEvent))
{
$eventId = $oldEvent['id'];
@ -1583,6 +1602,21 @@ class calendar_groupdav extends Api\CalDAV\Handler
$return_no_access = true; // to allow to check if current use is a participant and reject the event for him
$event = $this->_common_get_put_delete('DELETE',$options,$id,$return_no_access);
/* user has delete-rights, check if we have an external organizer and more participants
if ($event && $return_no_access && count($event['participants']) > 2)
{
foreach($event['participants'] as $uid => $status)
{
$quantity = $role = null;
calendar_so::split_status($status, $quantity, $role);
if (!is_numeric($uid) && $role == 'CHAIR') break;
}
if (!(!is_numeric($uid) && $role == 'CHAIR'))
{
$return_no_access = false; // only set status rejected, but do NOT delete the event
}
}*/
// no event found --> 404 Not Found
if (!is_array($event))
{
@ -1643,15 +1677,31 @@ class calendar_groupdav extends Api\CalDAV\Handler
* the same UID and/or caldav_name as not deleted events and would block access to valid entries
*
* @param string|id $id
* @return array|boolean array with entry, false if no read rights, null if $id does not exist
* @return array|boolean array with entry, false if no read rights or deleted, null if $id does not exist
*/
function read($id)
{
if (strpos($column=self::$path_attr,'_') === false) $column = 'cal_'.$column;
$event = $this->bo->read(array($column => $id, 'cal_deleted IS NULL', 'cal_reference=0'), null, true,
$event = $this->bo->read(array($column => $id, 'cal_reference=0'), null, true,
$date_format = Api\CalDAV::isJSON() ? 'object' : 'server');
if ($event) $event = array_shift($event); // read with array as 1. param, returns an array of events!
// read with array as 1. param, returns an array of events!
// as we no longer return only NOT-deleted events, there might be more
if ($event)
{
foreach ($event as $event)
{
if (empty($event['cal_deleted'])) break;
}
// the above prefers a NOT deleted event over deleted noes, thought all might be deleted
if (!empty($event['cal_deleted']))
{
$retval = false;
if ($this->debug > 0) error_log(__METHOD__."($id) event has been deleted returning ".array2string($retval));
return $retval;
}
}
if (!($retval = $this->bo->check_perms(calendar_bo::ACL_FREEBUSY,$event, 0, 'server')) &&
// above can be true, if current user is not in master but just a recurrence

View File

@ -2130,8 +2130,7 @@ class calendar_uiforms extends calendar_ui
$event['end'] += $diff;
}
if (($existing_event = $this->bo->read($event['uid'], $event['recurrence'], false, 'ts', null, true)) && // true = read the exception
!$existing_event['deleted'])
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
$from_extern_organizer = false;
@ -2179,7 +2178,7 @@ class calendar_uiforms extends calendar_ui
// warn user about party-crashers (non-participants sending a reply)
if (!isset($existing_status))
{
if (!empty($event['sender_warning'])) $event['sender_warning'] .= "\n";
if (!empty($event['sender_warning'])) $event['sender_warning'] .= "\n\n";
$event['sender_warning'] .= lang('Replying "%1" is NOT a participant of the event! Only continue if you want to add as new participant.', $participant);
}
calendar_so::split_status($existing_status, $quantity, $role);
@ -2255,7 +2254,7 @@ class calendar_uiforms extends calendar_ui
$event['recure'] = $this->bo->recure2string($event);
$event['all_participants'] = implode(",\n",$this->bo->participants($event, true));
// EGroupware event has been deleted, dont let user resurect it by accepting again
// EGroupware event has been deleted, don't let user resurrect it by accepting again
if ($existing_event && $existing_event['deleted'] && strtolower($ical_method) !== 'cancel')
{
// check if this is an EGroupware event or has an external organizer
@ -2265,12 +2264,8 @@ class calendar_uiforms extends calendar_ui
calendar_so::split_status($status, $quantity, $role);
if (!is_numeric($uid) && $role == 'CHAIR') break;
}
if (!(!is_numeric($uid) && $role == 'CHAIR'))
{
$event['error'] = lang('Event has been deleted by organizer!');
$readonlys['button[accept]'] = $readonlys['button[tentativ]'] =
$readonlys['button[reject]'] = $readonlys['button[cancel]'] = true;
}
$event['sender_warning'] = lang('Event has been deleted by organizer!').
(!empty($event['sender_warning']) ? "\n\n".$event['sender_warning'] : '');
}
// ignore events in the past (for recurring events check enddate!)
elseif ($this->bo->date2ts($event['start']) < $this->bo->now_su &&

View File

@ -10,7 +10,7 @@
</columns>
<rows>
<row disabled="!@sender_warning">
<grid width="100%" class="meetingRequest" span="all">
<grid width="100%" class="meetingRequest meetingWarning" span="all">
<columns>
<column/>
</columns>
@ -19,35 +19,42 @@
<et2-description value="Attention"></et2-description>
</row>
<row class="row">
<et2-description id="sender_warning" class="meetingRequestError"></et2-description>
<et2-description id="sender_warning"></et2-description>
</row>
</rows>
</grid>
</row>
<row disabled="!@ics_method=request">
<et2-vbox>
<et2-description value="This mail contains a meeting request" class="meetingRequestMessage"></et2-description>
<et2-hbox>
<et2-button label="Apply" id="button[apply]" class="leftPad5" hideOnReadonly="true"></et2-button>
<et2-button label="Accept" id="button[accept]" class="leftPad5" image="calendar/accepted"></et2-button>
<et2-button label="Tentative" id="button[tentativ]" class="leftPad5" image="calendar/tentative"></et2-button>
<et2-button label="Reject" id="button[reject]" class="leftPad5" image="calendar/rejected"></et2-button>
<et2-button statustext="Edit event in calendar" label="Edit" id="button[edit]" image="edit" hideOnReadonly="true" onclick="window.open(egw::link('/index.php','menuaction=calendar.calendar_uiforms.edit&amp;cal_id=$cont[id]'),'_blank','dependent=yes,width=750,height=410,scrollbars=yes,status=yes'); return false;" class="leftPad5" noSubmit="true"></et2-button>
<et2-hbox class="buttonRow">
<et2-button label="Apply" id="button[apply]" hideOnReadonly="true"></et2-button>
<et2-button label="Accept" id="button[accept]" image="calendar/accepted"></et2-button>
<et2-button label="Tentative" id="button[tentativ]" image="calendar/tentative"></et2-button>
<et2-button label="Reject" id="button[reject]" image="calendar/rejected"></et2-button>
<et2-button statustext="Edit event in calendar" label="Edit" id="button[edit]" image="edit" hideOnReadonly="true"
onclick="window.open(egw::link('/index.php','menuaction=calendar.calendar_uiforms.edit&amp;cal_id=$cont[id]'),'_blank','dependent=yes,width=750,height=410,scrollbars=yes,status=yes'); return false;" noSubmit="true"></et2-button>
</et2-hbox>
<et2-description id="error" class="meetingRequestError" align="right"></et2-description>
</et2-vbox>
</row>
<row disabled="!@ics_method=reply">
<et2-vbox>
<et2-description value="This mail contains a reply to a meeting request" class="meetingRequestMessage"></et2-description>
<et2-button label="Apply" id="button[apply]" class="leftPad5"></et2-button>
<et2-description id="error" class="meetingRequestError" align="right"></et2-description>
<et2-hbox class="buttonRow">
<et2-button label="Apply" id="button[apply]"></et2-button>
</et2-hbox>
</et2-vbox>
</row>
<row disabled="!@ics_method=cancel">
<et2-vbox>
<et2-description value="This mail cancels a meeting" class="meetingRequestMessage"></et2-description>
<et2-hbox>
<et2-button label="Apply" statustext="Removes the event from my calendar" id="button[cancel]" class="leftPad5"></et2-button>
<et2-button label="Delete" statustext="Delete this meeting for all participants" id="button[delete]" class="leftPad5" onclick="et2_dialog.confirm(widget,'Delete this meeting for all participants','Delete')"></et2-button>
<et2-button statustext="Edit event in calendar" label="Edit" id="button[edit]" image="edit" onclick="window.open(egw::link('/index.php','menuaction=calendar.calendar_uiforms.edit&amp;cal_id=$cont[id]'),'_blank','dependent=yes,width=750,height=410,scrollbars=yes,status=yes'); return false;" class="leftPad5"></et2-button>
<et2-hbox class="buttonRow">
<et2-button label="Apply" statustext="Removes the event from my calendar" id="button[cancel]"></et2-button>
<et2-button label="Delete" statustext="Delete this meeting for all participants" id="button[delete]" onclick="et2_dialog.confirm(widget,'Delete this meeting for all participants','Delete')"></et2-button>
<et2-button statustext="Edit event in calendar" label="Edit" id="button[edit]" image="edit"
onclick="window.open(egw::link('/index.php','menuaction=calendar.calendar_uiforms.edit&amp;cal_id=$cont[id]'),'_blank','dependent=yes,width=750,height=410,scrollbars=yes,status=yes'); return false;"></et2-button>
</et2-hbox>
<et2-description id="error" class="meetingRequestError" align="right"></et2-description>
</et2-vbox>
</row>
</rows>
</grid>
@ -61,6 +68,9 @@
<row class="th">
<et2-description id="ics_method_label" span="all"></et2-description>
</row>
<row class="row">
<et2-description id="error" class="meetingRequestError" span="all"></et2-description>
</row>
<row class="row">
<et2-description value="Title"></et2-description>
<et2-description id="title" noLang="1"></et2-description>
@ -75,9 +85,9 @@
</row>
<row class="row">
<et2-description value="Date"></et2-description>
<et2-hbox>
<et2-hbox class="dates">
<et2-date-time id="start" readonly="true"></et2-date-time>
<et2-date-time label="-" id="end" readonly="true" class="leftPad5"></et2-date-time>
<et2-date-time label="-" id="end" readonly="true"></et2-date-time>
</et2-hbox>
</row>
<row class="row" disabled="!@recure">
@ -107,7 +117,7 @@
overflow-y: auto;
}
.meetingRequestMessage {
font-size: 120%;
font-size: 150%;
}
table.meetingRequest {
@ -123,6 +133,7 @@
}
.meetingRequest td {
padding: 3px;
padding-left: 0;
}
.meetingRequest a {
display: inline-block;
@ -130,11 +141,22 @@
overflow-wrap: break-word;
vertical-align: text-top;
}
et2-hbox.dates {
padding-left: 5px;
}
.meetingWarning tr.th {
background-color: #dc2625;
}
.meetingWarning tr.row td {
font-size: 120% !important;
}
.meetingRequestError {
color: red;
font-style: italic;
font-size: 120%;
color: red;
}
.buttonRow {
padding-top: 10px;
padding-bottom: 10px;
}
</styles>
</template>