returning and parsing of exceptions and alarms:

- AS does NOT support differnet participants or status for exceptions
- AS only supports a single alarm
This commit is contained in:
Ralf Becker 2011-03-24 23:31:03 +00:00
parent bd6889657e
commit cd4ff32143

View File

@ -120,7 +120,7 @@ class calendar_activesync implements activesync_plugin_write
$folderObj->type = SYNC_FOLDER_TYPE_USER_APPOINTMENT; $folderObj->type = SYNC_FOLDER_TYPE_USER_APPOINTMENT;
} }
//error_log(__METHOD__."('$id') folderObj=".array2string($folderObj)); //error_log(__METHOD__."('$id') folderObj=".array2string($folderObj));
debugLog(__METHOD__."('$id') folderObj=".array2string($folderObj)); //debugLog(__METHOD__."('$id') folderObj=".array2string($folderObj));
return $folderObj; return $folderObj;
} }
@ -148,7 +148,7 @@ class calendar_activesync implements activesync_plugin_write
'parent' => '0', 'parent' => '0',
); );
//error_log(__METHOD__."('$id') folderObj=".array2string($stat)); //error_log(__METHOD__."('$id') folderObj=".array2string($stat));
debugLog(__METHOD__."('$id') folderObj=".array2string($stat)); //debugLog(__METHOD__."('$id') folderObj=".array2string($stat));
return $stat; return $stat;
} }
@ -185,6 +185,7 @@ class calendar_activesync implements activesync_plugin_write
'filter' => 'default', // not rejected 'filter' => 'default', // not rejected
// @todo return only etag relevant information (seems not to work ...) // @todo return only etag relevant information (seems not to work ...)
//'cols' => array('egw_cal.cal_id', 'cal_start', 'recur_type', 'cal_modified', 'cal_uid', 'cal_etag'), //'cols' => array('egw_cal.cal_id', 'cal_start', 'recur_type', 'cal_modified', 'cal_uid', 'cal_etag'),
'query' => array('cal_recurrence' => 0), // do NOT return recurrence exceptions
); );
$messagelist = array(); $messagelist = array();
@ -293,6 +294,71 @@ class calendar_activesync implements activesync_plugin_write
error_log(__METHOD__."('$folderid',$id,".array2string($message).") no rights to add/edit event!"); error_log(__METHOD__."('$folderid',$id,".array2string($message).") no rights to add/edit event!");
return false; return false;
} }
if (!$id) $event['owner'] = $account; // we do NOT allow to change the owner of existing events
$event = $this->message2event($message, $account, $event);
if (!($id = $this->calendar->update($event,true))) // true = ignore conflicts
{
debugLog(__METHOD__."('$folderid',$id,...) error saving event=".array2string($event)."!");
return false;
}
// store non-delete exceptions
if ($message->exceptions)
{
foreach($message->exceptions as $exception)
{
if (!$exception->deleted)
{
$ex_event = $event;
unset($ex_event['id']);
unset($ex_event['etag']);
foreach($ex_event as $name => $value) if (substr($name,0,6) == 'recur_') unset($ex_event[$name]);
$ex_event['recur_type'] = calendar_rrule::NONE;
if ($event['id'] && ($ex_events = $this->calendar->search(array(
'user' => $user,
'enum_recuring' => false,
'daywise' => false,
'filter' => 'owner', // return all possible entries
'query' => array(
'cal_uid' => $event['uid'],
'cal_recurrence' => $exception->exceptionstarttime, // in servertime
),
))))
{
$ex_event = array_shift($ex_events);
$participants = $ex_event['participants'];
}
else
{
$participants = $event['participants'];
}
$ex_event = $this->message2event($exception, $account, $ex_event);
$ex_event['participants'] = $participants; // not contained in $exception
$ex_event['reference'] = $event['id'];
$ex_event['recurrence'] = egw_time::server2user($exception->exceptionstarttime);
$ex_ok = $this->calendar->save($ex_event);
debugLog(__METHOD__."('$folderid',$id,...) saving exception=".array2string($ex_event).' returned '.array2string($ex_ok));
error_log(__METHOD__."('$folderid',$id,...) exception=".array2string($exception).") saving exception=".array2string($ex_event).' returned '.array2string($ex_ok));
}
}
}
debugLog(__METHOD__."('$folderid',$id,...) SUCESS saving event=".array2string($event).", id=$id");
error_log(__METHOD__."('$folderid',$id,".array2string($message).") SUCESS saving event=".array2string($event).", id=$id");
return $this->StatMessage($folderid, $id);
}
/**
* Parse AS message into EGw event array
*
* @param SyncAppointment $message
* @param int $account
* @param array $event=array()
* @return array
*/
private function message2event(SyncAppointment $message, $account, $event=array())
{
// timestamps (created & modified are updated automatically) // timestamps (created & modified are updated automatically)
foreach(array( foreach(array(
'start' => 'starttime', 'start' => 'starttime',
@ -301,8 +367,6 @@ class calendar_activesync implements activesync_plugin_write
{ {
$event[$key] = egw_time::server2user($message->$attr); $event[$key] = egw_time::server2user($message->$attr);
} }
if (!$id) $event['owner'] = $account; // we do NOT allow to change the owner of existing events
// copying strings // copying strings
foreach(array( foreach(array(
'title' => 'subject', 'title' => 'subject',
@ -447,8 +511,9 @@ class calendar_activesync implements activesync_plugin_write
{ {
foreach($message->exceptions as $exception) foreach($message->exceptions as $exception)
{ {
$event['recur_exceptions'][] = egw_time::server2user($exception->starttime); // exceptions seems to be full SyncAppointments, with only starttime required $event['recur_exception'][] = egw_time::server2user($exception->exceptionstarttime);
} }
$event['recur_exception'] = array_unique($event['recur_exception']);
} }
if ($message->recurrence->occurrences > 0) if ($message->recurrence->occurrences > 0)
{ {
@ -461,17 +526,36 @@ class calendar_activesync implements activesync_plugin_write
$event['recur_enddate'] = $rtime->format('ts'); $event['recur_enddate'] = $rtime->format('ts');
} }
} }
// only import alarms in own calendar
// @todo: body or description if ($message->reminder && $account == $GLOBALS['egw_info']['user']['account_id'])
if (!($id = $this->calendar->update($event,true))) // true = ignore conflicts
{ {
debugLog(__METHOD__."('$folderid',$id,...) error saving event=".array2string($event)."!"); foreach((array)$event['alarm'] as $alarm)
return false; {
if (($alarm['all'] || $alarm['owner'] == $account) && $alarm['offset'] == 60*$message->reminder)
{
$alarm = true; // alarm already exists --> do nothing
break;
} }
debugLog(__METHOD__."('$folderid',$id,...) SUCESS saving event=".array2string($event).", id=$id"); }
error_log(__METHOD__."('$folderid',$id,".array2string($message).") SUCESS saving event=".array2string($event).", id=$id"); if ($alarm !== true) // new alarm
return $this->StatMessage($folderid, $id); {
// delete all earlier alarms of that user
// user get's per AS only the earliest alarm, as AS only supports one alarm
// --> if a later alarm is returned, user probably modifed an existing alarm
foreach((array)$event['alarm'] as $key => $alarm)
{
if ($alarm['owner'] == $account && $alarm['offset'] > 60*$message->reminder)
{
unset($event['alarm'][$key]);
}
}
$event['alarm'][] = $alarm = array(
'owner' => $account,
'offset' => 60*$message->reminder,
);
}
}
return $event;
} }
/** /**
@ -567,24 +651,35 @@ class calendar_activesync implements activesync_plugin_write
* Timezones are only used to get correct recurring events! * Timezones are only used to get correct recurring events!
* *
* @param string $folderid * @param string $folderid
* @param string $id * @param string|array $id cal_id or event array (used internally)
* @param int $truncsize * @param int $truncsize
* @param int $bodypreference * @param int|bool $bodypreference=false
* @param bool $mimesupport * @param int $mimesupport=0
* @return SyncAppointment|boolean false on error * @return SyncAppointment|boolean false on error
*/ */
public function GetMessage($folderid, $id, $truncsize, $bodypreference=false, $mimesupport = 0) public function GetMessage($folderid, $id, $truncsize, $bodypreference=false, $mimesupport = 0)
{ {
if (!isset($this->calendar)) $this->calendar = new calendar_boupdate(); if (!isset($this->calendar)) $this->calendar = new calendar_boupdate();
debugLog (__METHOD__."('$folderid', $id, truncsize=$truncsize, bodyprefence=$bodypreference, mimesupport=$mimesupport)"); debugLog (__METHOD__."('$folderid', ".array2string($id).", truncsize=$truncsize, bodyprefence=$bodypreference, mimesupport=$mimesupport)");
$this->backend->splitID($folderid, $type, $account); $this->backend->splitID($folderid, $type, $account);
if (is_array($id))
{
$event = $id;
$id = $event['id'];
}
else
{
list($id,$recur_date) = explode(':',$id); list($id,$recur_date) = explode(':',$id);
if ($type != 'calendar' || !($event = $this->calendar->read($id,$recur_date,false,'server',$account))) if ($type != 'calendar' || !($event = $this->calendar->read($id,$recur_date,false,'server',$account)))
{ {
error_log(__METHOD__."('$folderid', $id, ...) read($id,null,false,'server',$account) returned false"); error_log(__METHOD__."('$folderid', $id, ...) read($id,null,false,'server',$account) returned false");
return false; return false;
} }
}
debugLog(__METHOD__."($folderid,$id,...) start=$event[start]=".date('Y-m-d H:i:s',$event['start']).", recurrence=$event[recurrence]=".date('Y-m-d H:i:s',$event['recurrence']));
foreach($event['recur_exception'] as $ex) debugLog("exception=$ex=".date('Y-m-d H:i:s',$ex));
$message = new SyncAppointment(); $message = new SyncAppointment();
// set timezone // set timezone
@ -706,41 +801,69 @@ class calendar_activesync implements activesync_plugin_write
if ($rrule->exceptions) if ($rrule->exceptions)
{ {
// search real / non-virtual exceptions
$ex_events =& $this->calendar->search(array(
'query' => array('cal_uid' => $event['uid']),
'filter' => 'owner', // return all possible entries
'daywise' => false,
'date_format' => 'server',
));
$message->exceptions = array(); $message->exceptions = array();
foreach($rrule->exceptions_objs as $exception_time) foreach($ex_events as $ex_event)
{
if ($ex_event['id'] == $event['id']) continue; // ignore series master
$exception = $this->GetMessage($folderid, $ex_event, $truncsize, $bodypreference, $mimesupport);
$exception->exceptionstarttime = $exception_time = $ex_event['recurrence'];
foreach(array('attendees','recurrence','uid','timezone','organizername','organizeremail') as $not_supported)
{
$exception->$not_supported = null; // not allowed in exceptions :-(
}
$exception->deleted = 0;
if (($key = array_search($exception_time,$event['recur_exception'])) !== false)
{
unset($event['recur_exception'][$key]);
}
debugLog(__METHOD__."() added exception ".date('Y-m-d H:i:s',$exception_time).' '.array2string($exception));
$message->exceptions[] = $exception;
}
// add rest of exceptions as deleted
foreach($event['recur_exception'] as $exception_time)
{ {
$exception = new SyncAppointment(); // exceptions seems to be full SyncAppointments, with only starttime required $exception = new SyncAppointment(); // exceptions seems to be full SyncAppointments, with only starttime required
$exception->starttime = $exception_time->format('server'); $exception->deleted = 1;
$exception->exceptionstarttime = $exception_time;
debugLog(__METHOD__."() added deleted exception ".date('Y-m-d H:i:s',$exception_time).' '.array2string($exception));
$message->exceptions[] = $exception; $message->exceptions[] = $exception;
} }
} }
/* disabled virtual exceptions for now, as AS does NOT support changed participants or status
// add virtual exceptions here too (get_recurrence_exceptions should be able to return real-exceptions too!) // add virtual exceptions here too (get_recurrence_exceptions should be able to return real-exceptions too!)
foreach($this->calendar->so->get_recurrence_exceptions($event, foreach($this->calendar->so->get_recurrence_exceptions($event,
egw_time::$server_timezone->getName(), $cutoffdate, 0, 'all') as $virtual_exception_time) egw_time::$server_timezone->getName(), $cutoffdate, 0, 'all') as $exception_time)
{ {
$exception = new SyncAppointment(); // exceptions seems to be full SyncAppointments, with only starttime required // exceptions seems to be full SyncAppointments, with only exceptionstarttime required
$exception->starttime = $virtual_exception_time; $exception = $this->GetMessage($folderid, $event['id'].':'.$exception_time, $truncsize, $bodypreference, $mimesupport);
$exception->deleted = 0;
$exception->exceptionstarttime = $exception_time;
debugLog(__METHOD__."() added virtual exception ".date('Y-m-d H:i:s',$exception_time).' '.array2string($exception));
$message->exceptions[] = $exception; $message->exceptions[] = $exception;
}*/
//debugLog(__METHOD__."($id) message->exceptions=".array2string($message->exceptions));
}
// only return alarms if in own calendar
if ($account == $GLOBALS['egw_info']['user']['account_id'] && $event['alarm'])
{
foreach($event['alarm'] as $alarm)
{
if ($alarm['all'] || $alarm['owner'] == $account)
{
$message->reminder = $alarm['offset']/60; // is in minutes, not seconds as in EGw
break; // AS supports only one alarm! (we use the next/earliest one)
}
} }
} }
//$message->busystatus; //$message->busystatus;
//$message->reminder;
//$message->meetingstatus; //$message->meetingstatus;
//$message->deleted;
/*
if (isset($protocolversion) && $protocolversion < 12.0) {
$message->body;
$message->bodytruncated;
$message->rtf;
}
if(isset($protocolversion) && $protocolversion >= 12.0) {
$message->airsyncbasebody; // SYNC SyncAirSyncBaseBody
}
*/
// @todo: body or description
return $message; return $message;
} }
@ -761,7 +884,7 @@ class calendar_activesync implements activesync_plugin_write
{ {
if (!isset($this->calendar)) $this->calendar = new calendar_boupdate(); if (!isset($this->calendar)) $this->calendar = new calendar_boupdate();
if (!($etag = $this->calendar->get_etag($id,false))) // false: do NOT include exceptions into master etag if (!($etag = $this->calendar->get_etag($id)))
{ {
$stat = false; $stat = false;
// error_log why access is denied (should never happen for everything returned by calendar_bo::search) // error_log why access is denied (should never happen for everything returned by calendar_bo::search)