allow status update via CalDAV/GroupDAV PUT and DELETE for attendees (with no other rights on the event), moved that code from bocalupdate (wasnt working because of wrong param code) to boical, this should work for SyncML and CalDAV/GroupDAV - thought SyncML does not handle the delete to reject an event so far

This commit is contained in:
Ralf Becker 2008-05-17 13:00:34 +00:00
parent a659e4eb6b
commit 89eba8038d
3 changed files with 73 additions and 48 deletions

View File

@ -81,7 +81,7 @@ class bocalupdate extends bocal
*/ */
function update(&$event,$ignore_conflicts=false,$touch_modified=true,$ignore_acl=false) function update(&$event,$ignore_conflicts=false,$touch_modified=true,$ignore_acl=false)
{ {
//error_log(__METHOD__."(".str_replace(array("\n",' '),'',print_r($event,true)).",$ignore_conflicts,$touch_modified,$ignore_acl)"); //error_log(__METHOD__."(".array2string($event).",$ignore_conflicts,$touch_modified,$ignore_acl)");
if ($this->debug > 1 || $this->debug == 'update') if ($this->debug > 1 || $this->debug == 'update')
{ {
$this->debug_message('bocalupdate::update(%1,ignore_conflict=%2,touch_modified=%3,ignore_acl=%4)', $this->debug_message('bocalupdate::update(%1,ignore_conflict=%2,touch_modified=%3,ignore_acl=%4)',
@ -117,21 +117,6 @@ class bocalupdate extends bocal
!$event['id'] && !$this->check_perms(EGW_ACL_EDIT,0,$event['owner'])) && !$event['id'] && !$this->check_perms(EGW_ACL_EDIT,0,$event['owner'])) &&
!$this->check_perms(EGW_ACL_ADD,0,$event['owner'])) !$this->check_perms(EGW_ACL_ADD,0,$event['owner']))
{ {
// Just update the status, if the user is in the event already
// is user is in both original and updated event
$egw_event = $this->read($event['id']);
if ( isset($egw_event['participants'][$this->user])
&& isset($event['participants'][$this->user]))
{
// Update their status in the event and say we're done.
// Admittedly, this is false, it's dropping any changes on the floor,
// But this will work better than dropping -everything- silently on
// the floor
$this->set_status($event['id'],'u',$this->user,$event['participants'][$this->user],0);
unset($egw_event);
return $event['id'];
}
return false; return false;
} }

View File

@ -234,7 +234,8 @@
break; break;
case 'ORGANIZER': // according to iCalendar standard, ORGANIZER not used for events in the own calendar case 'ORGANIZER': // according to iCalendar standard, ORGANIZER not used for events in the own calendar
if (!isset($event['participants'][$event['owner']]) || count($event['participants']) > 1) if ($event['owner'] != $this->user)
//if (!isset($event['participants'][$event['owner']]) || count($event['participants']) > 1)
{ {
$mailtoOrganizer = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email'); $mailtoOrganizer = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email');
$attributes['ORGANIZER'] = $mailtoOrganizer ? 'MAILTO:'.$mailtoOrganizer : ''; $attributes['ORGANIZER'] = $mailtoOrganizer ? 'MAILTO:'.$mailtoOrganizer : '';
@ -475,7 +476,8 @@
$_vcalData = preg_replace("/[\r\n]+ /",'',$_vcalData); $_vcalData = preg_replace("/[\r\n]+ /",'',$_vcalData);
$vcal = &new Horde_iCalendar; $vcal = &new Horde_iCalendar;
if(!$vcal->parsevCalendar($_vcalData)) { if(!$vcal->parsevCalendar($_vcalData))
{
return FALSE; return FALSE;
} }
@ -850,6 +852,10 @@
{ {
$event['id'] = $cal_id; $event['id'] = $cal_id;
} }
elseif($event['id'] && $cal_id <= 0)
{
$cal_id = $event['id']; // event[id] set via uid
}
while(($fieldName = array_shift($supportedFields))) while(($fieldName = array_shift($supportedFields)))
{ {
switch($fieldName) switch($fieldName)
@ -892,10 +898,9 @@
// If this is an updated meeting, and the client doesn't support // If this is an updated meeting, and the client doesn't support
// participants, add them back // participants, add them back
if( $cal_id >0 && !isset($this->supportedFields['participants'])) if( $cal_id > 0 && !isset($this->supportedFields['participants']))
{ {
$egw_event = $this->read($cal_id); if (($egw_event = $this->read($cal_id)))
if ($egw_event)
{ {
$event['participants'] = $egw_event['participants']; $event['participants'] = $egw_event['participants'];
$event['participant_types'] = $egw_event['participant_types']; $event['participant_types'] = $egw_event['participant_types'];
@ -906,22 +911,26 @@
if( $cal_id > 0 ) if( $cal_id > 0 )
{ {
// for each existing participant: // for each existing participant:
$egw_event = $this->read($cal_id); if (($egw_event = $this->read($cal_id)))
if ( $egw_event )
{ {
foreach( $egw_event['participants'] as $uid => $status ) foreach( $egw_event['participants'] as $uid => $status )
{ {
// Is it a resource? // Is it a resource and not longer present in the event?
if ( preg_match("/^r(.*)/", $uid, $matches) ) if ( $uid[0] == 'r' && !isset($event['participants'][$uid]) )
{ {
// Add it back in // Add it back in
$event['participants'][$uid] = 'A'; $event['participants'][$uid] = $event['participant_types']['r'][substr($uid,1)] = $status;
$event['participant_types']['r'][$matches[1]] = 'A';
} }
} }
} }
} }
// check if iCal changes the organizer, which is not allowed
if ($cal_id > 0 && ($egw_event = $this->read($cal_id)) && $event['owner'] != $egw_event['owner'])
{
$event['owner'] = $egw_event['owner']; // set it back to the original owner
}
#error_log('ALARMS'); #error_log('ALARMS');
#error_log(print_r($event, true)); #error_log(print_r($event, true));
@ -932,6 +941,16 @@
} }
if (!($Ok = $this->update($event, TRUE))) if (!($Ok = $this->update($event, TRUE)))
{ {
// check if current user is an attendee and tried to change his status
if ($Ok === false && $cal_id && ($egw_event = $this->read($cal_id)) && isset($egw_event['participants'][$this->user]) &&
$egw_event['participants'][$this->user] !== $event['participants'][$this->user])
{
$this->set_status($egw_event,$this->user,
$status = $event['participants'][$this->user] ? $event['participants'][$this->user] : 'R');
$Ok = $cal_id;
continue;
}
break; // stop with the first error break; // stop with the first error
} }
else else

View File

@ -38,9 +38,16 @@ class calendar_groupdav extends groupdav_handler
//'RECURRENCE-ID' //'RECURRENCE-ID'
); );
function __construct($debug=null) /**
* Constructor
*
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
*/
function __construct($app,$debug=null,$base_uri=null)
{ {
parent::__construct('calendar',$debug); parent::__construct($app,$debug,$base_uri);
$this->bo =& new bocalupdate(); $this->bo =& new bocalupdate();
} }
@ -57,7 +64,7 @@ class calendar_groupdav extends groupdav_handler
*/ */
function propfind($path,$options,&$files,$user,$id='') function propfind($path,$options,&$files,$user,$id='')
{ {
if ($this->debug > 2) error_log(__METHOD__."($path,".str_replace(array("\n",' '),'',print_r($options,true)).",,$user,$id)"); if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user,$id)");
// ToDo: add parameter to only return id & etag // ToDo: add parameter to only return id & etag
$cal_filters = array( $cal_filters = array(
@ -68,7 +75,7 @@ class calendar_groupdav extends groupdav_handler
'daywise' => false, 'daywise' => false,
'date_format' => 'server', 'date_format' => 'server',
); );
error_log(__METHOD__."($path,,,$user,$id) cal_filters=".str_replace(array("\n",' '),'',print_r($cal_filters,true))); if ($this->debug > 1) error_log(__METHOD__."($path,,,$user,$id) cal_filters=".array2string($cal_filters));
// process REPORT filters or multiget href's // process REPORT filters or multiget href's
if (($id || $options['root']['name'] != 'propfind') && !$this->_report_filters($options,$cal_filters,$id)) if (($id || $options['root']['name'] != 'propfind') && !$this->_report_filters($options,$cal_filters,$id))
@ -131,13 +138,13 @@ class calendar_groupdav extends groupdav_handler
switch($filter['name']) switch($filter['name'])
{ {
case 'comp-filter': case 'comp-filter':
error_log(__METHOD__."($path,...) comp-filter='{$filter['attrs']['name']}'"); if ($this->debug > 1) error_log(__METHOD__."($path,...) comp-filter='{$filter['attrs']['name']}'");
switch($filter['attrs']['name']) switch($filter['attrs']['name'])
{ {
case 'VTODO': case 'VTODO':
return false; // return nothing for now, todo: check if we can pass it on to the infolog handler return false; // return nothing for now, todo: check if we can pass it on to the infolog handler
// todos are handled by the infolog handler // todos are handled by the infolog handler
$infolog_handler = new infolog_groupdav(); $infolog_handler = new groupdav_infolog();
return $infolog_handler->propfind($path,$options,$files,$user,$method); return $infolog_handler->propfind($path,$options,$files,$user,$method);
case 'VCALENDAR': case 'VCALENDAR':
CASE 'VEVENT': CASE 'VEVENT':
@ -145,14 +152,14 @@ class calendar_groupdav extends groupdav_handler
} }
break; break;
case 'prop-filter': case 'prop-filter':
error_log(__METHOD__."($path,...) prop-filter='{$filter['attrs']['name']}'"); if ($this->debug > 1) error_log(__METHOD__."($path,...) prop-filter='{$filter['attrs']['name']}'");
$prop_filter = $filter['attrs']['name']; $prop_filter = $filter['attrs']['name'];
break; break;
case 'text-match': case 'text-match':
error_log(__METHOD__."($path,...) text-match: $prop_filter='{$filter['data']}'"); if ($this->debug > 1) error_log(__METHOD__."($path,...) text-match: $prop_filter='{$filter['data']}'");
if (!isset($this->filter_prop2cal[strtoupper($prop_filter)])) if (!isset($this->filter_prop2cal[strtoupper($prop_filter)]))
{ {
error_log(__METHOD__."($path,".str_replace(array("\n",' '),'',print_r($options,true)).",,$user) unknown property '$prop_filter' --> ignored"); if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user) unknown property '$prop_filter' --> ignored");
} }
else else
{ {
@ -161,14 +168,15 @@ class calendar_groupdav extends groupdav_handler
unset($prop_filter); unset($prop_filter);
break; break;
case 'param-filter': case 'param-filter':
error_log(__METHOD__."($path,...) param-filter='{$filter['attrs']['name']}'"); if ($this->debug) error_log(__METHOD__."($path,...) param-filter='{$filter['attrs']['name']}' not (yet) implemented!");
break; break;
case 'time-range': case 'time-range':
error_log(__METHOD__."($path,...) time-range={$filter['attrs']['start']}-{$filter['attrs']['end']}"); if ($this->debug > 1) error_log(__METHOD__."($path,...) time-range={$filter['attrs']['start']}-{$filter['attrs']['end']}");
$cal_filters['start'] = $filter['attrs']['start'];
$cal_filters['end'] = $filter['attrs']['end'];
break; break;
default: default:
error_log(__METHOD__."($path,".str_replace(array("\n",' '),'',print_r($options,true)).",,$user) unknown filter --> ignored"); if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user) unknown filter --> ignored");
break; break;
} }
} }
@ -214,7 +222,7 @@ class calendar_groupdav extends groupdav_handler
{ {
$cal_filters['query'][] = 'egw_cal.cal_id IN ('.implode(',',array_map(create_function('$n','return (int)$n;'),$ids)).')'; $cal_filters['query'][] = 'egw_cal.cal_id IN ('.implode(',',array_map(create_function('$n','return (int)$n;'),$ids)).')';
} }
//error_log(__METHOD__."($path,,,$user,$id) calendar-multiget: ids=".implode(',',$ids)); if ($this->debug) error_log(__METHOD__."($path,,,$user,$id) calendar-multiget: ids=".implode(',',$ids));
} }
return true; return true;
} }
@ -249,7 +257,8 @@ class calendar_groupdav extends groupdav_handler
*/ */
function put(&$options,$id,$user=null) function put(&$options,$id,$user=null)
{ {
$event = $this->_common_get_put_delete('PUT',$options,$id); $return_no_access=true; // as handled by importVCal anyway and allows it to set the status for participants
$event = $this->_common_get_put_delete('PUT',$options,$id,$return_no_access);
if (!is_null($event) && !is_array($event)) if (!is_null($event) && !is_array($event))
{ {
return $event; return $event;
@ -257,14 +266,14 @@ class calendar_groupdav extends groupdav_handler
if (!($cal_id = ExecMethod2('calendar.boical.importVCal',$options['content'],is_numeric($id) ? $id : -1, if (!($cal_id = ExecMethod2('calendar.boical.importVCal',$options['content'],is_numeric($id) ? $id : -1,
self::etag2value($this->http_if_match)))) self::etag2value($this->http_if_match))))
{ {
if ($this->debug) error_log(__METHOD__."(,$id) import_vevent($options[content]) returned false"); if ($this->debug) error_log(__METHOD__."(,$id) importVCal($options[content]) returned false");
return false; // something went wrong ... return '403 Forbidden';
} }
header('ETag: '.$this->get_etag($cal_id)); header('ETag: '.$this->get_etag($cal_id));
if (is_null($event)) if (is_null($event) || !$return_no_access) // let lightning think the event is added
{ {
error_log(__METHOD__."(,$id,$user) cal_id=$cal_id, is_null(\$event)=".(int)is_null($event)); if ($this->debug) error_log(__METHOD__."(,$id,$user) cal_id=$cal_id, is_null(\$event)=".(int)is_null($event));
header('Location: '.$this->base_uri.'/calendar/'.$cal_id); header('Location: '.$this->base_uri.'/calendar/'.$cal_id);
return '201 Created'; return '201 Created';
} }
@ -274,14 +283,24 @@ class calendar_groupdav extends groupdav_handler
/** /**
* Handle delete request for an event * Handle delete request for an event
* *
* If current user has no right to delete the event, but is an attendee, we reject the event for him.
*
* @param array &$options * @param array &$options
* @param int $id * @param int $id
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found') * @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
*/ */
function delete(&$options,$id) function delete(&$options,$id)
{ {
if (!is_array($event = $this->_common_get_put_delete('DELETE',$options,$id))) $return_no_access=true; // to allow to check if current use is a participant and reject the event for him
if (!is_array($event = $this->_common_get_put_delete('DELETE',$options,$id,$return_no_access)) || !$return_no_access)
{ {
if (!$return_no_access)
{
$ret = isset($event['participants'][$this->bo->user]) &&
$this->bo->set_status($event,$this->bo->user,'R') ? true : '403 Forbidden';
if ($this->debug) error_log(__METHOD__."(,$id) return_no_access=$return_no_access, event[participants]=".array2string($event['participants']).", user={$this->bo->user} --> return $ret");
return $ret;
}
return $event; return $event;
} }
return $this->bo->delete($id); return $this->bo->delete($id);
@ -306,16 +325,18 @@ class calendar_groupdav extends groupdav_handler
*/ */
function get_etag($entry) function get_etag($entry)
{ {
$e_in = $entry;
if (!is_array($entry)) if (!is_array($entry))
{ {
$entry = $this->read($entry); $entry = $this->read($entry);
} }
if (!$entry['id'] || !isset($entry['etag']) || !isset($entry['participants'])) error_log(__METHOD__."($e_in): id=$entry[id], etag=$entry[etag], isset(participants)=".(int)isset($entry['participants']).", title=$entry[title]: id, etag or participants not set!!!");
$etag = $entry['id'].':'.$entry['etag']; $etag = $entry['id'].':'.$entry['etag'];
// add a hash over the participants and their stati // add a hash over the participants and their stati
ksort($entry['participants']); // create a defined order ksort($entry['participants']); // create a defined order
$etag .= ':'.md5(serialize($entry['participants'])); $etag .= ':'.md5(serialize($entry['participants']));
//error_log(__METHOD__."($entry[id] ($entry[etag]): $entry[title] --> etag=$etag"); //error_log(__METHOD__."($entry[id] ($entry[etag]): $entry[title] --> etag=$etag");
return $etag; return $etag.'x';
} }
/** /**