implement CalDAV <calendar-data><expand start="..." end="..."/></calendar-data> to return already expanded recurrences in a given time-range, instead of just a recurring event with a recurrence rule

This commit is contained in:
Ralf Becker 2012-02-10 08:50:19 +00:00
parent d6276d1c54
commit 02c5181735
3 changed files with 53 additions and 41 deletions

View File

@ -28,7 +28,7 @@ class calendar_groupdav extends groupdav_handler
/**
* vCalendar Instance for parsing
*
* @var array
* @var Horde_iCalendar
*/
var $vCalendar;
@ -191,19 +191,6 @@ class calendar_groupdav extends groupdav_handler
error_log(__METHOD__."($path,,,$user,$id) filter=".array2string($filter));
}
// check if we have to return the full calendar data or just the etag's
if (!($filter['calendar_data'] = $options['props'] == 'all' &&
$options['root']['ns'] == groupdav::CALDAV) && is_array($options['props']))
{
foreach($options['props'] as $prop)
{
if ($prop['name'] == 'calendar-data')
{
$filter['calendar_data'] = true;
break;
}
}
}
// return iterator, calling ourself to return result in chunks
$files['files'] = new groupdav_propfind_iterator($this,$path,$filter,$files['files']);
@ -222,8 +209,8 @@ class calendar_groupdav extends groupdav_handler
{
if ($this->debug) $starttime = microtime(true);
$calendar_data = $filter['calendar_data'];
unset($filter['calendar_data']);
$calendar_data = $this->groupdav->prop_requested('calendar-data', groupdav::CALDAV, true);
if (!is_array($calendar_data)) $calendar_data = false; // not in allprop or autoindex
$files = array();
@ -266,7 +253,10 @@ class calendar_groupdav extends groupdav_handler
//error_log(__FILE__ . __METHOD__ . "Calendar Data : $calendar_data");
if ($calendar_data)
{
$content = $this->iCal($event, $filter['users'], strpos($path, '/inbox/') !== false ? 'REQUEST' : null);
$content = $this->iCal($event, $filter['users'],
strpos($path, '/inbox/') !== false ? 'REQUEST' : null,
!isset($calendar_data['children']['expand']) ? false :
($calendar_data['children']['expand']['attrs'] ? $calendar_data['children']['expand']['attrs'] : true));
$props['getcontentlength'] = bytes($content);
$props['calendar-data'] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-data',$content);
}
@ -440,9 +430,10 @@ class calendar_groupdav extends groupdav_handler
* @param array $event
* @param int $user=null account_id of calendar to display
* @param string $method=null eg. 'PUBLISH' for inbox, nothing anywhere else
* @param boolean|array $expand=false true or array with values for 'start', 'end' to expand recurrences
* @return string
*/
private function iCal(array $event,$user=null, $method=null)
private function iCal(array $event,$user=null, $method=null, $expand=false)
{
static $handler = null;
if (is_null($handler)) $handler = $this->_get_handler();
@ -461,7 +452,12 @@ class calendar_groupdav extends groupdav_handler
// for recuring events we have to add the exceptions
if ($this->client_shared_uid_exceptions && $event['recur_type'] && !empty($event['uid']))
{
$events =& self::get_series($event['uid'],$this->bo);
if (is_array($expand))
{
if (isset($expand['start'])) $expand['start'] = $this->vCalendar->_parseDateTime($expand['start']);
if (isset($expand['end'])) $expand['end'] = $this->vCalendar->_parseDateTime($expand['end']);
}
$events =& self::get_series($event['uid'], $this->bo, $expand);
}
elseif(!$this->client_shared_uid_exceptions && $event['reference'])
{
@ -477,9 +473,10 @@ class calendar_groupdav extends groupdav_handler
*
* @param string $uid UID
* @param calendar_bo $bo=null calendar_bo object to reuse for search call
* @param boolean|array $expand=false true or array with values for 'start', 'end' to expand recurrences
* @return array
*/
private static function &get_series($uid,calendar_bo $bo=null)
private static function &get_series($uid,calendar_bo $bo=null, $expand=false)
{
if (is_null($bo)) $bo = new calendar_bopdate();
@ -488,22 +485,21 @@ class calendar_groupdav extends groupdav_handler
{
return array(); // should never happen
}
$exceptions =& $master['recur_exception'];
$exceptions = $master['recur_exception'];
$events =& $bo->search(array(
$params = array(
'query' => array('cal_uid' => $uid),
'filter' => 'owner', // return all possible entries
'daywise' => false,
'date_format' => 'server',
));
$events = array_merge(array($master), $events);
);
if (is_array($expand)) $params += $expand;
$events =& $bo->search($params);
foreach($events as $k => &$recurrence)
{
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
// "($uid)[$k]:" . array2string($recurrence));
if (!$k) continue; // nothing to change
//error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."($uid)[$k]:" . array2string($recurrence));
if ($recurrence['id'] != $master['id']) // real exception
{
//error_log('real exception: '.array2string($recurrence));
@ -518,7 +514,7 @@ class calendar_groupdav extends groupdav_handler
continue; // nothing to change
}
// now we need to check if this recurrence is an exception
if ($master['participants'] == $recurrence['participants'])
if (!$expand && $master['participants'] == $recurrence['participants'])
{
//error_log('NO exception: '.array2string($recurrence));
unset($events[$k]); // no exception --> remove it
@ -531,7 +527,10 @@ class calendar_groupdav extends groupdav_handler
$recurrence['recur_type'] = MCAL_RECUR_NONE; // is set, as this is a copy of the master
// not for included exceptions (Lightning): $master['recur_exception'][] = $recurrence['start'];
}
$events[0]['recur_exception'] = $exceptions;
if (!$expand)
{
$events = array_merge(array($master), $events);
}
return $events;
}

View File

@ -198,7 +198,6 @@ class _parse_propfind
if ($this->depth == 0) {
$this->root = array('name' => $tag, 'xmlns' => $ns, 'attrs' => $attrs);
}
// special tags at level 1: <allprop> and <propname>
if ($this->depth == 1) {
$this->use = 'props';
@ -221,21 +220,34 @@ class _parse_propfind
break;
}
}
//echo "$this->depth: use=$this->use $ns:$tag attrs=".array2string($attrs)."\n";
// requested properties are found at level 2
// CalDAV filters can be at deeper levels too and we need the attrs, same for other tags (eg. multiget href's)
if ($this->depth == 2 || $this->use == 'filters' && $this->depth >= 2 || $this->use == 'other') {
if ($this->depth == 2 || $this->use == 'filters' && $this->depth >= 2 || $this->use == 'other' ||
$this->use == 'props' && $this->depth >= 2) {
$prop = array("name" => $tag);
if ($ns)
$prop["xmlns"] = $ns;
if ($this->use != 'props') {
if ($this->use != 'props' || $this->depth > 2) {
$prop['attrs'] = $attrs;
$prop['depth'] = $this->depth;
}
// this can happen if we have allprop and prop in one propfind:
// <allprop /><prop><blah /></prop>, eg. blah is not automatic returned by allprop
if (!is_array($this->{$this->use}) && $this->{$this->use}) $this->{$this->use} = array($this->{$this->use});
$this->{$this->use}[] = $prop;
// collect sub-elements of props in the original props children attribute
// eg. required for CalDAV <calendar-data><expand start="..." end="..."/></calendar-data>
if ($this->use == 'props' && $this->depth > 2)
{
$this->last_prop['children'][$tag] = $prop;
}
else
{
// this can happen if we have allprop and prop in one propfind:
// <allprop /><prop><blah /></prop>, eg. blah is not automatic returned by allprop
if (!is_array($this->{$this->use}) && $this->{$this->use}) $this->{$this->use} = array($this->{$this->use});
$this->{$this->use}[] =& $prop;
$this->last_prop =& $prop;
unset($prop);
}
}
// increment depth count

View File

@ -513,9 +513,10 @@ class groupdav extends HTTP_WebDAV_Server
*
* @param string $name property name
* @param string $ns=null namespace, if that is to be checked too
* @return boolean|string true: $name explicitly requested (or autoindex), 'allprop' requested, false: $name was not requested
* @param boolean $return_prop=false if true return the property array with values for 'name', 'xmlns', 'attrs', 'children'
* @return boolean|string|array true: $name explicitly requested (or autoindex), 'allprop' requested, false: $name was not requested
*/
function prop_requested($name, $ns=null)
function prop_requested($name, $ns=null, $return_prop=false)
{
if (!is_array($this->propfind_options) || !isset($this->propfind_options['props']))
{
@ -526,7 +527,7 @@ class groupdav extends HTTP_WebDAV_Server
{
if ($prop['name'] == $name && (is_null($ns) || $prop['xmlns'] == $ns))
{
$ret = true;
$ret = $return_prop ? $prop : true;
break;
}
if ($prop['name'] == 'allprop') $ret = 'allprop';