complete rework of the ical import part: removed unclear 1:N relations, introduced get_event_info method which completely classifies incoming events first, removed code duplicates, re-organized importVCal() to have a more linear code structure and to make debugging easier in future. -- please forgive me if i may have introduced some bugs with that but its a huge change --"

This commit is contained in:
Christian Binder 2009-09-22 13:43:55 +00:00
parent 9ed090d1a6
commit 5602cac698

View File

@ -782,369 +782,317 @@ class calendar_ical extends calendar_boupdate
* @param int $cal_id=-1 must be -1 for new entries! * @param int $cal_id=-1 must be -1 for new entries!
* @param string $etag=null if an etag is given, it has to match the current etag or the import will fail * @param string $etag=null if an etag is given, it has to match the current etag or the import will fail
* @param boolean $merge=false merge data with existing entry * @param boolean $merge=false merge data with existing entry
* @param int $recur_date=0 if set, import the recurrance at this timestamp, * @param int $recur_date=0 if set, import the recurrence at this timestamp,
* default 0 => import whole series (or events, if not recurring) * default 0 => import whole series (or events, if not recurring)
* @return int|boolean cal_id > 0 on success, false on failure or 0 for a failed etag * @return int|boolean cal_id > 0 on success, false on failure or 0 for a failed etag
*/ */
function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0) function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0)
{ {
$Ok = false; // returning false, if file contains no components
if($this->log)error_log(__LINE__.__METHOD__.__FILE__.array2string($_vcalData)."\n",3,$this->logfile); if($this->log)error_log(__LINE__.__METHOD__.__FILE__.array2string($_vcalData)."\n",3,$this->logfile);
$vcal = new Horde_iCalendar; if(!$events = $this->icaltoegw($_vcalData,$cal_id,$etag,$recur_date))
if (!$vcal->parsevCalendar($_vcalData))
{ {
return $Ok; return false;
} }
$version = $vcal->getAttribute('VERSION');
if (!is_array($this->supportedFields)) $this->setSupportedFields(); if (!is_array($this->supportedFields)) $this->setSupportedFields();
foreach ($vcal->getComponents() as $component) foreach ($events as $event)
{ {
if (is_a($component, 'Horde_iCalendar_vevent')) $updated_id = false;
$event_info = $this->get_event_info($event);
// common adjustments for new events
if(!isset($event['id']))
{ {
$event = $this->vevent2egw($component, $version, $this->supportedFields); // set non blocking all day depending on the user setting
if($this->isWholeDay($event) && $this->nonBlockingAllday)
if ($cal_id > 0) {
$event['id'] = $cal_id;
}
if ($this->productManufacturer == '' && $this->productName == ''
&& !empty($event['recur_enddate']))
{ {
// syncevolution needs an adjusted recur_enddate $event['non_blocking'] = 1;
$event['recur_enddate'] = (int)$event['recur_enddate'] + 86400;
} }
if ($cal_id < 0 && (!isset($this->supportedFields['participants']) || // check if an owner is set and the current user has add rights
!isset($event['participants'][$GLOBALS['egw_info']['user']['account_id']]))) // for that owners calendar; if not set the current user
if(!isset($event['owner'])
|| !$this->check_perms(EGW_ACL_ADD,0,$event['owner']))
{
$event['owner'] = $GLOBALS['egw_info']['user']['account_id'];
}
// add ourself to new events as participant
if(!isset($this->supportedFields['participants'])
||!isset($event['participants'][$GLOBALS['egw_info']['user']['account_id']]))
{ {
// add ourself to new events as participant
$event['participants'][$GLOBALS['egw_info']['user']['account_id']] = 'A'; $event['participants'][$GLOBALS['egw_info']['user']['account_id']] = 'A';
} }
}
if ($event['recur_type'] != MCAL_RECUR_NONE) // common adjustments for existing events
{ if (isset($event['id']) && is_array($event_info['stored_event']))
// No RECURRENCE-ID for series events {
$event['reference'] = $event['recurrence'] = 0; if ($merge)
}
if (!$recur_date && $cal_id > 0 && ($egw_event = $this->read($cal_id)))
{ {
// overwrite with server data for merge // overwrite with server data for merge
if ($merge) foreach ($event_info['stored_event'] as $key => $value)
{ {
if ($egw_event['recur_type'] != MCAL_RECUR_NONE && $recur_date) switch ($key)
{ {
// update only the stati of the exception case 'participants_types':
if ($this->check_perms(EGW_ACL_EDIT, $cal_id)) continue;
{
$this->update_status($event, $egw_event, $recur_date);
}
$Ok = $cal_id . ':' . $recur_date;
continue; // nothing more to do
}
foreach ($egw_event as $key => $value)
{
switch ($key)
{
case 'participants_types':
continue;
case 'participants': case 'participants':
foreach ($egw_event['participants'] as $uid => $status) foreach ($event_info['stored_event']['participants'] as $uid => $status)
{
// Is a participant and no longer present in the event?
if (!isset($event['participants'][$uid]))
{ {
// Is a participant and no longer present in the event? // Add it back in
if (!isset($event['participants'][$uid])) $event['participants'][$uid] = $event['participant_types']['r'][substr($uid,1)] = $status;
{
// Add it back in
$event['participants'][$uid] = $event['participant_types']['r'][substr($uid,1)] = $status;
}
} }
break; }
break;
default: default:
if (!empty($value)) if (!empty($value))
{ {
$event[$key] = $value; $event[$key] = $value;
} }
}
} }
} }
else // not merge
{
if (!isset($this->supportedFields['participants']) || !count($event['participants']))
{
// If this is an updated meeting, and the client doesn't support
// participants OR the event no longer contains participants, add them back
$event['participants'] = $egw_event['participants'];
$event['participant_types'] = $egw_event['participant_types'];
}
foreach ($egw_event['participants'] as $uid => $status)
{
// Is it a resource and no longer present in the event?
if ( $uid[0] == 'r' && !isset($event['participants'][$uid]) )
{
// Add it back in
$event['participants'][$uid] = $event['participant_types']['r'][substr($uid,1)] = $status;
}
}
// avoid that iCal changes the organizer, which is not allowed
$event['owner'] = $egw_event['owner'];
}
} }
else else
{ {
// new event // no merge
$cal_id = -1; if (!isset($this->supportedFields['participants']) || !count($event['participants']))
$recur_date = $event['recurrence'];
}
if (empty($event['uid']) && $cal_id > 0 &&
($egw_event = $this->read($cal_id)))
{
$event['uid'] = $egw_event['uid'];
}
if ($event['recur_type'] == MCAL_RECUR_NONE
&& !empty($event['uid']) && $recur_date)
{
// We handle a recurrence exception
$recur_exceptions = $this->so->get_related($event['uid']);
$recur_id = array_search($recur_date, $recur_exceptions);
if ($recur_id === false || !($egw_event = $this->read($recur_id)))
{ {
// We found no real exception, let't try "status only" // If this is an updated meeting, and the client doesn't support
if (($egw_event = $this->read($event['uid'])) // participants OR the event no longer contains participants, add them back
&& $egw_event['recur_type'] != MCAL_RECUR_NONE) $event['participants'] = $event_info['stored_event']['participants'];
$event['participant_types'] = $event_info['stored_event']['participant_types'];
}
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]) )
{ {
// Same start and duration is obligatory for status only // Add it back in
$old_duration = $egw_event['end'] - $egw_event['start']; $event['participants'][$uid] = $event['participant_types']['r'][substr($uid,1)] = $status;
$new_duration = $event['end'] - $event['start']; }
$unchanged = ($event['start'] == $recur_date && $old_duration == $new_duration); }
foreach (array('uid','owner','title','description', // avoid that iCal changes the organizer, which is not allowed
'location','priority','public','special','non_blocking') as $key) $event['owner'] = $event_info['stored_event']['owner'];
}
}
// save event depending on the given event type
switch($event_info['type'])
{
case 'SINGLE':
Horde::logMessage('importVCAL event SINGLE',__FILE__, __LINE__, PEAR_LOG_DEBUG);
// update the event
if($event_info['acl_edit'])
{
$event_to_store = $event; // prevent $event from being changed by the update method
$updated_id = $this->update($event_to_store, true);
unset($event_to_store);
}
break;
case 'SERIES-MASTER':
Horde::logMessage('importVCAL event SERIES-MASTER',__FILE__, __LINE__, PEAR_LOG_DEBUG);
// remove all known "status only" exceptions and update the event
if($event_info['acl_edit'])
{
$days = $this->so->get_recurrence_exceptions($event);
if(is_array($days))
{
$recur_exceptions = array();
foreach($event['recur_exception'] as $recur_exception)
{ {
//Horde::logMessage('importVCAL test ' .$key . ': '. $egw_event[$key] . ' == ' .$event[$key], if(!in_array($recur_exception, $days))
// __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!$unchanged && !empty($event[$key]) //|| !empty($egw_event[$key]))
&& $egw_event[$key] != $event[$key])
{ {
$unchanged = false; $recur_exceptions[] = $recur_exception;
break;
} }
} }
if ($unchanged) $event['recur_exception'] = $recur_exceptions;
{ }
Horde::logMessage('importVCAL event unchanged',
__FILE__, __LINE__, PEAR_LOG_DEBUG); $event_to_store = $event; // prevent $event from being changed by the update method
$updated_id = $this->update($event_to_store, true);
$recur_exceptions = array(); unset($event_to_store);
foreach ($egw_event['recur_exception'] as $recur_exception) }
{ break;
//Horde::logMessage('importVCAL exception ' .$recur_exception,
// __FILE__, __LINE__, PEAR_LOG_DEBUG); case 'SERIES-EXCEPTION':
if ($recur_exception != $recur_date) Horde::logMessage('importVCAL event SERIES-EXCEPTION',__FILE__, __LINE__, PEAR_LOG_DEBUG);
{
$recur_exceptions[] = $recur_exception; // update event
} if($event_info['acl_edit'])
} {
$egw_event['recur_exception'] = $recur_exceptions; if(isset($event_info['stored_event']['id']))
//Horde::logMessage("importVCAL exceptions\n" . print_r($recur_exceptions, true), {
// __FILE__, __LINE__, PEAR_LOG_DEBUG); // We update an existing exception
$event['id'] = $event_info['stored_event']['id'];
$this->update($egw_event, true); $event['category'] = $event_info['stored_event']['category'];
// update the stati from the exception
if ($this->check_perms(EGW_ACL_EDIT, $egw_event['id']))
{
$this->update_status($event, $egw_event, $recur_date);
}
elseif (isset($event['participants'][$this->user]) || isset($egw_event['participants'][$this->user]))
{
// check if current user is an attendee and tried to change his status
$this->set_status($egw_event, $this->user,
($event['participants'][$this->user] ? $event['participants'][$this->user] : 'R'), $recur_date, true);
}
$Ok = $egw_event['id'] . ':' . $recur_date;
continue; // nothing more to do
}
else
{
// We need to create an new exception
$egw_event['recur_exception'] = array_unique(array_merge($egw_event['recur_exception'], array($recur_date)));
$this->update($egw_event, true);
$event['reference'] = $egw_event['id'];
$event['category'] = $egw_event['category'];
unset($event['id']);
$cal_id = -1;
}
} }
else else
{ {
// The series event was not found // We create a new exception
$cal_id = -1; unset($event['id']);
$event_info['master_event']['recur_exception'] = array_unique(array_merge($event_info['master_event']['recur_exception'], array($event['recurrence'])));
$event_to_store = $event_info['master_event']; // prevent the master_event from being changed by the update method
$this->update($event_to_store, true);
unset($event_to_store);
$event['reference'] = $event_info['master_event']['id'];
$event['category'] = $event_info['master_event']['category'];
} }
$event_to_store = $event; // prevent $event from being changed by update method
$updated_id = $this->update($event_to_store, true);
unset($event_to_store);
} }
else break;
case 'SERIES-EXCEPTION-STATUS':
Horde::logMessage('importVCAL event SERIES-EXCEPTION-STATUS',__FILE__, __LINE__, PEAR_LOG_DEBUG);
if($event_info['acl_edit'])
{ {
// We update an existing exception // truncate the status only exception from the series master
$cal_id = $egw_event['id'];
$event['id'] = $egw_event['id'];
$event['category'] = $egw_event['category'];
}
}
else
{
// We handle a single event or the series master
$days = $this->so->get_recurrence_exceptions($event);
if (is_array($days))
{
Horde::logMessage("importVCAL event\n" . print_r($event, true),
__FILE__, __LINE__, PEAR_LOG_DEBUG);
Horde::logMessage("importVCAL days\n" . print_r($days, true),
__FILE__, __LINE__, PEAR_LOG_DEBUG);
// remove all known "stati only" exceptions
$recur_exceptions = array(); $recur_exceptions = array();
foreach ($event['recur_exception'] as $recur_exception) foreach ($event_info['master_event']['recur_exception'] as $recur_exception)
{ {
if (!in_array($recur_exception, $days)) if ($recur_exception != $event['recurrence'])
{ {
$recur_exceptions[] = $recur_exception; $recur_exceptions[] = $recur_exception;
} }
} }
Horde::logMessage("importVCAL exceptions\n" . print_r($recur_exceptions, true), $event_info['master_event']['recur_exception'] = $recur_exceptions;
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$event['recur_exception'] = $recur_exceptions; // save the series master with the adjusted exceptions
$event_to_store = $event_info['master_event']; // prevent the master_event from being changed by the update method
$updated_id = $this->update($event_to_store, true);
unset($event_to_store);
} }
}
break;
if ($cal_id <= 0) }
{
// new entry // read stored event into info array for fresh stored (new) events
if ($this->isWholeDay($event) && $this->nonBlockingAllday) if(!is_array($event_info['stored_event']) && $updated_id > 0)
{
$event_info['stored_event'] = $this->read($updated_id);
}
// update status depending on the given event type
switch($event_info['type'])
{
case 'SINGLE':
case 'SERIES-MASTER':
case 'SERIES-EXCEPTION':
if(is_array($event_info['stored_event'])) // status update requires a stored event
{ {
$event['non_blocking'] = 1; if($event_info['acl_edit'])
}
if (!isset($event['owner'])
|| !$this->check_perms(EGW_ACL_ADD,0,$event['owner']))
{
// check for new events if an owner is set and the current user has add rights
// for that owners calendar; if not set the current user
$event['owner'] = $GLOBALS['egw_info']['user']['account_id'];
}
}
// if an etag is given, include it in the update
if (!is_null($etag))
{
$event['etag'] = $etag;
}
$original_event = $event;
if($this->log)error_log(__LINE__.__METHOD__.__FILE__.array2string($original_event)."\n",3,$this->logfile);
if (!($Ok = $this->update($event, true)))
{
if($this->log)error_log(__LINE__.__METHOD__.__FILE__.array2string($Ok)."\n",3,$this->logfile);
if ($Ok === false && $cal_id > 0 && ($egw_event = $this->read($cal_id)))
{
$unchanged = true;
if ($event['recur_type'] != MCAL_RECUR_NONE)
{ {
// Check if a recurring event "status only" exception is created by the client // update all participants if we have the right to do that
foreach (array('uid','owner','title','description', $this->update_status($event, $event_info['stored_event']);
'location','priority','public','special','non_blocking') as $key) }
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;
case 'SERIES-EXCEPTION-STATUS':
if(is_array($event_info['stored_event'])) // status update requires a stored master 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'], $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'), $event['recurrence'], true);
}
}
break;
}
// update alarms depending on the given event type
switch($event_info['type'])
{
case 'SINGLE':
case 'SERIES-MASTER':
case 'SERIES-EXCEPTION':
if(is_array($event_info['stored_event'])) // alarm update requires a stored event
{
// delete old alarms
if(count($event['alarm']) > 0
|| (isset($this->supportedFields['alarms']) && count($event['alarm']) == 0))
{
foreach ($event_info['stored_event']['alarm'] as $alarm_id => $alarm_data)
{ {
//Horde::logMessage('importVCAL test ' .$key . ': '. $egw_event[$key] . ' == ' .$event[$key], $this->delete_alarm($alarm_id);
// __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!empty($event[$key]) //|| !empty($egw_event[$key]))
&& $egw_event[$key] != $event[$key])
{
$unchanged = false;
break;
}
} }
} }
// check if current user is an attendee and tried to change his status
if (isset($event['participants'][$this->user]) || isset($egw_event['participants'][$this->user]))
{
$this->set_status($egw_event, $this->user,
($event['participants'][$this->user] ? $event['participants'][$this->user] : 'R'), $recur_date);
$Ok = $cal_id; // save given alarms
continue; if(count($event['alarm']) > 0)
}
if ($unchanged)
{ {
$Ok = $cal_id; foreach ($event['alarm'] as $alarm)
continue; {
if(!isset($alarm['offset']) && isset($alarm['time']))
{
$alarm['offset'] = $event['start'] - $alarm['time'];
}
if(!isset($alarm['time']) && isset($alarm['offset']))
{
$alarm['time'] = $event['start'] - $alarm['offset'];
}
$alarm['owner'] = $GLOBALS['egw_info']['user']['account_id'];
$alarm['all'] = true;
$this->save_alarm($event_info['stored_event']['id'], $alarm);
}
} }
} }
break; // stop with the first error break;
}
else case 'SERIES-EXCEPTION-STATUS':
{ // nothing to do here
$eventID = &$Ok; break;
if($this->log)error_log(__LINE__.__METHOD__.__FILE__.array2string($eventID)."\n",3,$this->logfile);
/* if(isset($egw_event)
&& $original_event['participants'] != $egw_event['participants'])
{
$this->update_status($original_event, $egw_event, $recur_date);
} */
$alarms = $event['alarm'];
// handle the alarms
foreach ($component->getComponents() as $valarm)
{
if (is_a($valarm, 'Horde_iCalendar_valarm'))
{
$this->valarm2egw($alarms, $valarm);
}
}
if (count($alarms) > 0
|| (isset($this->supportedFields['alarms'])
&& count($alarms) == 0))
{
// delete the old alarms
$updatedEvent = $this->read($eventID);
foreach ($updatedEvent['alarm'] as $alarmID => $alarmData)
{
$this->delete_alarm($alarmID);
}
}
foreach ($alarms as $alarm)
{
if (!isset($alarm['offset']))
{
$alarm['offset'] = $event['start'] - $alarm['time'];
}
if (!isset($alarm['time']))
{
$alarm['time'] = $event['start'] - $alarm['offset'];
}
$alarm['owner'] = $GLOBALS['egw_info']['user']['account_id'];
$alarm['all'] = true;
$this->save_alarm($eventID, $alarm);
}
}
$cal_id = -1;
} }
$egw_event = $this->read($eventID);
if($this->log)error_log(__LINE__.__METHOD__.__FILE__.array2string($egw_event)."\n",3,$this->logfile);
// choose which id to return to the client
switch($event_info['type'])
{
case 'SINGLE':
case 'SERIES-MASTER':
case 'SERIES-EXCEPTION':
$return_id = is_array($event_info['stored_event']) ? $event_info['stored_event']['id'] : false;
break;
case 'SERIES-EXCEPTION-STATUS':
$return_id = is_array($event_info['master_event']) ? $event_info['master_event']['id'] . ':' . $event['recurrence'] : false;
break;
}
if($this->log)
{
$egw_event = $this->read($event['id']);
error_log(__LINE__.__METHOD__.__FILE__.array2string($egw_event)."\n",3,$this->logfile);
}
} }
return $Ok;
return $return_id;
} }
/** /**
@ -1422,51 +1370,74 @@ class calendar_ical extends calendar_boupdate
} }
} }
function icaltoegw($_vcalData, $cal_id=-1) function icaltoegw($_vcalData, $cal_id=-1, $etag=null, $recur_date=0)
{ {
$event = false; // returning false, if file contains no components $events = array();
$vcal = new Horde_iCalendar; $vcal = new Horde_iCalendar;
if (!$vcal->parsevCalendar($_vcalData)) return false; if (!$vcal->parsevCalendar($_vcalData)) return false;
$version = $vcal->getAttribute('VERSION'); $version = $vcal->getAttribute('VERSION');
if (!is_array($this->supportedFields)) $this->setSupportedFields(); if (!is_array($this->supportedFields)) $this->setSupportedFields();
foreach ($vcal->getComponents() as $component) foreach ($vcal->getComponents() as $component)
{ {
if (is_a($component, 'Horde_iCalendar_vevent')) if (is_a($component, 'Horde_iCalendar_vevent'))
{ {
// We expect only a single VEVENT if ($event = $this->vevent2egw($component, $version, $this->supportedFields))
$event = $this->vevent2egw($component, $version, $this->supportedFields);
if ($event)
{ {
if ($cal_id > 0) { //common adjustments
$event['id'] = $cal_id;
}
else
{
if($this->isWholeDay($event)
&& $this->nonBlockingAllday)
{
$event['non_blocking'] = 1;
}
}
if ($this->productManufacturer == '' && $this->productName == '' if ($this->productManufacturer == '' && $this->productName == ''
&& !empty($event['recur_enddate'])) && !empty($event['recur_enddate']))
{ {
// syncevolution needs an adjusted recur_enddate // syncevolution needs an adjusted recur_enddate
$event['recur_enddate'] = (int)$event['recur_enddate'] + 86400; $event['recur_enddate'] = (int)$event['recur_enddate'] + 86400;
} }
if ($event['recur_type'] != MCAL_RECUR_NONE)
{
// No reference or RECURRENCE-ID for the series master
$event['reference'] = $event['recurrence'] = 0;
}
// handle the alarms
$alarms = $event['alarm'];
foreach ($component->getComponents() as $valarm)
{
if (is_a($valarm, 'Horde_iCalendar_valarm'))
{
$this->valarm2egw($alarms, $valarm);
}
}
$event['alarm'] = $alarms;
$events[] = $event;
} }
return $event;
} }
} }
return false;
// decide what to return
if(count($events) == 1)
{
$event = array_shift($events);
if($cal_id > 0) $event['id'] = $cal_id;
if(!is_null($etag)) $event['etag'] = $etag;
if($recur_date) $event['recurrence'] = $recur_date;
return array($event);
}
else if($count($events) == 0 || $cal_id > 0 || !is_null($etag) || $recur_date)
{
// no events to return
// or not allowed N:1 relation with params just meant for a single event
return false;
}
else
{
return $events;
}
} }
/** /**
* Parse an VEVENT * Parse a VEVENT
* *
* @param array $component VEVENT * @param array $component VEVENT
* @param string $version vCal version (1.0/2.0) * @param string $version vCal version (1.0/2.0)
@ -2018,16 +1989,16 @@ class calendar_ical extends calendar_boupdate
function search($_vcalData, $contentID=null, $relax=false) function search($_vcalData, $contentID=null, $relax=false)
{ {
$result = false; if($events = $this->icaltoegw($_vcalData,!is_null($contentID) ? $contentID : -1))
if($event = $this->icaltoegw($_vcalData))
{ {
if ($contentID) { // this function only supports searching a single event
$event['id'] = $contentID; if(count($events) == 1)
} {
$result = $this->find_event($event, $relax); $event = array_shift($events);
return $this->find_event($event, $relax);
}
} }
return $result; return false;
} }
/** /**
@ -2121,12 +2092,11 @@ class calendar_ical extends calendar_boupdate
* @param array $old_event event-array with the old stati * @param array $old_event event-array with the old stati
* @param int $recur_date=0 date to change, or 0 = all since now * @param int $recur_date=0 date to change, or 0 = all since now
*/ */
function update_status($new_event, $old_event , $recur_date) function update_status($new_event, $old_event , $recur_date=0)
{ {
// check the old list against the new list // check the old list against the new list
foreach ($old_event['participants'] as $userid => $status) foreach ($old_event['participants'] as $userid => $status)
{ {
if(!isset($new_event['participants'][$userid])){ if(!isset($new_event['participants'][$userid])){
// Attendee will be deleted this way // Attendee will be deleted this way
$new_event['participants'][$userid] = 'G'; $new_event['participants'][$userid] = 'G';
@ -2134,11 +2104,96 @@ class calendar_ical extends calendar_boupdate
// Same status -- nothing to do. // Same status -- nothing to do.
unset($new_event['participants'][$userid]); unset($new_event['participants'][$userid]);
} }
} }
// write the changes // write the changes
foreach ($new_event['participants'] as $userid => $status) foreach ($new_event['participants'] as $userid => $status)
{ {
$this->set_status($old_event, $userid, $status, $recur_date, true); $this->set_status($old_event, $userid, $status, $recur_date, true);
} }
}
/**
* classifies an incoming event from the eGW point-of-view
*
* exceptions: unlike other calendar apps eGW does not create an event exception
* if just the participant state changes - therefore we have to distinguish between
* real exceptions and status only exceptions
*
* @param array $event the event to check
*
* @return array
* type =>
* SINGLE a single event
* SERIES-MASTER the series master
* SERIES-EXCEPTION event is a real exception
* SERIES-EXCEPTION-STATUS event is a status only exception
* stored_event => if event already exists in the database array with event data or false
* master_event => if event is of type SERIES-EXCEPTION or SERIES-EXCEPTION-STATUS the corresponding series master event array
*/
private function get_event_info($event)
{
$type = 'SINGLE'; // default
$return_master = false; //default
if($event['recur_type'] != MCAL_RECUR_NONE)
{
$type = 'SERIES-MASTER';
}
else
{
// SINGLE, SERIES-EXCEPTION OR SERIES-EXCEPTON-STATUS
if(empty($event['uid']) && $event['id'] > 0 && ($stored_event = $this->read($event['id'])))
{
$event['uid'] = $stored_event['uid']; // restore the UID if it was not delivered
}
if(isset($event['uid'])
&& $event['recurrence']
&& ($master_event = $this->read($event['uid']))
&& isset($master_event['recur_type'])
&& $master_event['recur_type'] != MCAL_RECUR_NONE
)
{
// SERIES-EXCEPTION OR SERIES-EXCEPTON-STATUS
$return_master = true; // we have a valid master and can return it
if(isset($event['id']) && $master_event['id'] != $event['id'])
{
$type = 'SERIES-EXCEPTION'; //event is already a real exception
}
else
{
$type = 'SERIES-EXCEPTION-STATUS'; // default if we cannot find a proof for a fundamental change
$recurrence_event = $this->read($master_event['id'],$event['recurrence']);
// check for changed data
foreach (array('start','end','uid','owner','title','description',
'location','priority','public','special','non_blocking') as $key)
{
if (!empty($event[$key]) && $recurrence_event[$key] != $event[$key])
{
$type = 'SERIES-EXCEPTION';
break;
}
}
}
}
else
{
// SINGLE
$type = 'SINGLE';
}
}
if(isset($event['id']))
{
$stored_event = $this->read($event['id']);
}
return array(
'type' => $type,
'acl_edit' => is_array($stored_event) ? $this->check_perms(EGW_ACL_EDIT, $stored_event) : true,
'stored_event' => is_array($stored_event) ? $stored_event : false,
'master_event' => $return_master ? $master_event : false,
);
} }
} }