sync-collection report for InfoLog, thought reminder app on OS X 10.8.2 does not use it, moved logic for sync-token to baseclass and determine availablity on supported-report-set

This commit is contained in:
Ralf Becker 2012-09-24 10:26:29 +00:00
parent 7a3d4f99ed
commit f224fe5852
4 changed files with 136 additions and 42 deletions

View File

@ -430,7 +430,10 @@ class addressbook_groupdav extends groupdav_handler
}
break;
case 'sync-level':
// ignored for now
if ($option['data'] != '1')
{
$this->groupdav->log(__METHOD__."(...) only sync-level {$option['data']} requested, but only 1 supported! options[other]=".array2string($options['other']));
}
break;
default:
$this->groupdav->log(__METHOD__."(...) unknown xml tag '{$option['name']}': options[other]=".array2string($options['other']));
@ -723,21 +726,6 @@ class addressbook_groupdav extends groupdav_handler
return $ctags[$path] = max($ctag,$lists_ctag);
}
/**
* Query sync-token for addressbook
*
* @param string $path
* @param int $user
* @param int $modified=null default getctag
* @return string
*/
public function get_sync_token($path, $user, $modified=null)
{
if (!isset($modified)) $modified = $this->getctag($path, $user);
return $this->base_uri().$path.$modified;
}
/**
* Add extra properties for addressbook collections
*
@ -773,18 +761,18 @@ class addressbook_groupdav extends groupdav_handler
$props['max-image-size'] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'max-image-size',4096);
// supported reports (required property for CardDAV)
$props['supported-report-set'] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
HTTP_WebDAV_Server::mkprop('supported-report',array(
$props['supported-report-set'] = array(
'addressbook-query' => HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',array(
HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-query',''))))),
HTTP_WebDAV_Server::mkprop('supported-report',array(
'addressbook-multiget' => HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',array(
HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-multiget',''))))),
// rfc 6578 sync-collection report
HTTP_WebDAV_Server::mkprop('supported-report',array(
'sync-collection' => HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',array(
HTTP_WebDAV_Server::mkprop('sync-collection',''))))),
));
);
return $props;
}

View File

@ -148,7 +148,7 @@ class infolog_groupdav extends groupdav_handler
$filter = $this->get_infolog_filter($path, $user);
// process REPORT filters or multiget href's
if (($id || $options['root']['name'] != 'propfind') && !$this->_report_filters($options,$filter,$id))
if (($id || $options['root']['name'] != 'propfind') && !$this->_report_filters($options, $filter, $id, $nresults))
{
// return empty collection, as iCal under iOS 5 had problems with returning "404 Not found" status
// when trying to request not supported components, eg. VTODO on a calendar collection
@ -179,9 +179,28 @@ class infolog_groupdav extends groupdav_handler
}
}
// return iterator, calling ourself to return result in chunks
$files['files'] = new groupdav_propfind_iterator($this,$path,$filter,$files['files']);
// rfc 6578 sync-collection report: filter for sync-token is already set in _report_filters
if ($options['root']['name'] == 'sync-collection')
{
// query sync-token before result, so changed happening while result get queried are not lost
$files['sync-token'] = $this->get_sync_token($path, $user);
}
if (isset($nresults))
{
$files['files'] = $this->propfind_callback($path, $filter, array(0, (int)$nresults));
if ($options['root']['name'] == 'sync-collection' && isset($files['files']['sync-token']))
{
$files['sync-token'] = $this->get_sync_token($path, $user, $files['files']['sync-token']);
unset($files['files']['sync-token']);
}
}
else
{
// return iterator, calling ourself to return result in chunks
$files['files'] = new groupdav_propfind_iterator($this,$path,$filter,$files['files']);
}
return true;
}
@ -205,9 +224,18 @@ class infolog_groupdav extends groupdav_handler
$task_filter = $filter['filter'];
unset($filter['filter']);
$order = 'info_datemodified';
$sort = 'DESC';
if (preg_match('/^([a-z0-9_]+)( DESC| ASC)?$/i', $filter['order'], $matches))
{
$order = $matches[1];
if ($matches[2]) $sort = $matches[2];
unset($filter['order']);
}
$query = array(
'order' => 'info_datemodified',
'sort' => 'DESC',
'order' => $order,
'sort' => $sort,
'filter' => $task_filter,
'date_format' => 'server',
'col_filter' => $filter,
@ -216,7 +244,7 @@ class infolog_groupdav extends groupdav_handler
if (!$calendar_data)
{
$query['cols'] = array('info_id', 'info_datemodified', 'info_uid', 'caldav_name', 'info_subject');
$query['cols'] = array('info_id', 'info_datemodified', 'info_uid', 'caldav_name', 'info_subject', 'info_status');
}
if (is_array($start))
@ -236,6 +264,12 @@ class infolog_groupdav extends groupdav_handler
{
foreach($tasks as $task)
{
// sync-collection report: deleted entry need to be reported without properties
if ($task['info_status'] == 'deleted' && strpos($task_filter, '+deleted') !== false)
{
$files[] = array('path' => $path.urldecode($this->get_path($task)));
continue;
}
$props = array(
'getcontenttype' => $this->agent != 'kde' ? 'text/calendar; charset=utf-8; component=VTODO' : 'text/calendar', // Konqueror (3.5) dont understand it otherwise
'getlastmodified' => $task['info_datemodified'],
@ -250,6 +284,14 @@ class infolog_groupdav extends groupdav_handler
$files[] = $this->add_resource($path, $task, $props);
}
}
// hack to support limit with sync-collection report: tasks are returned in modified ASC order (oldest first)
// if limit is smaller then full result, return modified-1 as sync-token, so client requests next chunk incl. modified
// (which might contain further entries with identical modification time)
if (strpos($task_filter, '+deleted') !== false &&
$start[0] == 0 && $start[1] != groupdav_propfind_iterator::CHUNK_SIZE && $query['total'] > $start[1])
{
$files['sync-token'] = $task['info_datemodified']-1;
}
if ($this->debug) error_log(__METHOD__."($path) took ".(microtime(true) - $starttime).' to return '.count($files).' items');
return $files;
}
@ -260,9 +302,10 @@ class infolog_groupdav extends groupdav_handler
* @param array $options
* @param array &$cal_filters
* @param string $id
* @return boolean true if filter could be processed, false for requesting not here supported VTODO items
* @param int &$nresult on return limit for number or results or unchanged/null
* @return boolean true if filter could be processed
*/
function _report_filters($options,&$cal_filters,$id)
function _report_filters($options,&$filters,$id, &$nresults)
{
if ($options['filters'])
{
@ -311,12 +354,46 @@ class infolog_groupdav extends groupdav_handler
break;
}
}
// if client set an own filter, reset the open-standard filter
/* not longer necessary, as we use now own and user anyway
if ($cal_filters != $cal_filters_in)
}
// parse limit from $options['other']
/* Example limit
<B:limit>
<B:nresults>10</B:nresults>
</B:limit>
*/
foreach((array)$options['other'] as $option)
{
switch($option['name'])
{
$cal_filters['filter'] = str_replace(array('open', 'open-user'), array('own', 'user'), $cal_filters['filter']);
}*/
case 'nresults':
$nresults = (int)$option['data'];
//error_log(__METHOD__."(...) options[other]=".array2string($options['other'])." --> nresults=$nresults");
break;
case 'limit':
break;
case 'href':
break; // from addressbook-multiget, handled below
// rfc 6578 sync-report
case 'sync-token':
if (!empty($option['data']))
{
$parts = explode('/', $option['data']);
$sync_token = array_pop($parts);
$filters[] = 'info_datemodified>'.(int)$sync_token;
$filters['filter'] .= '+deleted'; // to return deleted entries too
$filters['order'] = 'info_datemodified ASC'; // return oldest modifications first
}
break;
case 'sync-level':
if ($option['data'] != '1')
{
$this->groupdav->log(__METHOD__."(...) only sync-level {$option['data']} requested, but only 1 supported! options[other]=".array2string($options['other']));
}
break;
default:
$this->groupdav->log(__METHOD__."(...) unknown xml tag '{$option['name']}': options[other]=".array2string($options['other']));
break;
}
}
// multiget or propfind on a given id
//error_log(__FILE__ . __METHOD__ . "multiget of propfind:");
@ -618,7 +695,7 @@ class infolog_groupdav extends groupdav_handler
/**
* Get the etag for an infolog entry
*
* etag currently uses the modifcation time (info_modified), 1.9.002 adds etag column, but it's not yet used!
* etag currently uses the modifcation time (info_datemodified), 1.9.002 adds etag column, but it's not yet used!
*
* @param array|int $info array with infolog entry or info_id
* @return string|boolean string with etag or false
@ -656,12 +733,18 @@ class infolog_groupdav extends groupdav_handler
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VTODO')),
));
// supported reports
$props['supported-report-set'] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
HTTP_WebDAV_Server::mkprop('supported-report',array(
$props['supported-report-set'] = array(
'calendar-query' => HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',array(
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-query',''))),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-query',''))))),
'calendar-multiget' => HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',array(
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-multiget','')))))));
HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'calendar-multiget',''))))),
// rfc 6578 sync-collection report
'sync-collection' => HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',array(
HTTP_WebDAV_Server::mkprop('sync-collection',''))))),
);
// get timezone of calendar
if ($this->groupdav->prop_requested('calendar-timezone'))

View File

@ -849,10 +849,10 @@ class groupdav extends HTTP_WebDAV_Server
$props['getctag'] = self::mkprop(
groupdav::CALENDARSERVER,'getctag',$handler->getctag($path,$user));
}
// add sync-token url if handler implements it
if (method_exists($handler,'get_sync_token') && $this->prop_requested('sync-token') === true)
// add sync-token url if handler supports sync-collection report
if (isset($props['supported-report-set']['sync-collection']) && $this->prop_requested('sync-token') === true)
{
$props['sync-token'] = self::mkprop('sync-token', $handler->get_sync_token($path,$user));
$props['sync-token'] = $handler->get_sync_token($path,$user);
}
}
if ($handler && $user)

View File

@ -579,6 +579,29 @@ abstract class groupdav_handler
}
return $full_uri ? $uri : $path;
}
/**
* Query sync-token
*
* We use ctag / max. modification time as sync-token. As garnularity is 1sec, we can never be sure,
* if there are more modifications to come in the current second.
*
* Therefor we are never returning current time, but 1sec less!
*
* @param string $path
* @param int $user
* @param int $modified=null default getctag
* @return string
*/
public function get_sync_token($path, $user, $modified=null)
{
if (!isset($modified)) $modified = $this->getctag($path, $user);
// never return current time, as more modifications might happen --> decrement it by 1sec
if ($modified == time()) --$modified;
return $this->base_uri().$path.$modified;
}
}
/**