mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-22 16:03:47 +01:00
Backport of r28140-2, r28156 to EPL-9.1:
- using iterator to query AB in chunks of 500 entries - Brief: t header (no 404 for missing props)
This commit is contained in:
parent
2904c203ad
commit
d1859f90e6
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* eGroupWare: GroupDAV access: addressbook handler
|
||||
* EGroupware: GroupDAV access: addressbook handler
|
||||
*
|
||||
* @link http://www.egroupware.org
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
@ -12,7 +12,10 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* eGroupWare: GroupDAV access: addressbook handler
|
||||
* EGroupware: GroupDAV access: addressbook handler
|
||||
*
|
||||
* Propfind now uses a groupdav_propfind_iterator with a callback to query huge addressbooks in chunk,
|
||||
* without getting into problems with memory_limit.
|
||||
*/
|
||||
class addressbook_groupdav extends groupdav_handler
|
||||
{
|
||||
@ -84,7 +87,6 @@ class addressbook_groupdav extends groupdav_handler
|
||||
*/
|
||||
function propfind($path,$options,&$files,$user,$id='')
|
||||
{
|
||||
//$this->starttime = microtime(true);
|
||||
$filter = array();
|
||||
// show addressbook of a single user?
|
||||
if ($user && $path != '/addressbook/') $filter['contact_owner'] = $user;
|
||||
@ -99,25 +101,44 @@ class addressbook_groupdav extends groupdav_handler
|
||||
if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user,$id) filter=".array2string($filter));
|
||||
|
||||
// check if we have to return the full calendar data or just the etag's
|
||||
if (!($address_data = $options['props'] == 'all' && $options['root']['ns'] == groupdav::CARDDAV) && is_array($options['props']))
|
||||
if (!($filter['address_data'] = $options['props'] == 'all' && $options['root']['ns'] == groupdav::CARDDAV) && is_array($options['props']))
|
||||
{
|
||||
foreach($options['props'] as $prop)
|
||||
{
|
||||
if ($prop['name'] == 'address-data')
|
||||
{
|
||||
$address_data = true;
|
||||
$filter['address_data'] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($address_data)
|
||||
// return iterator, calling ourself to return result in chunks
|
||||
$files['files'] = new groupdav_propfind_iterator($this,$filter,$files['files']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for profind interator
|
||||
*
|
||||
* @param array $filter
|
||||
* @param array|boolean $start=false false=return all or array(start,num)
|
||||
* @return array with "files" array with values for keys path and props
|
||||
*/
|
||||
function &propfind_callback(array $filter,$start=false)
|
||||
{
|
||||
$starttime = microtime(true);
|
||||
|
||||
if (($address_data = $filter['address_data']))
|
||||
{
|
||||
$handler = self::_get_handler();
|
||||
}
|
||||
unset($filter['address_data']);
|
||||
|
||||
$files = array();
|
||||
// we query etag and modified, as LDAP does not have the strong sql etag
|
||||
if (($contacts =& $this->bo->search(array(),$address_data ? false : array('id','uid','etag','modified'),'contact_id','','',False,'AND',false,$filter)))
|
||||
{
|
||||
//$icount= 0;
|
||||
foreach($contacts as $contact)
|
||||
{
|
||||
|
||||
@ -131,10 +152,7 @@ class addressbook_groupdav extends groupdav_handler
|
||||
////error_log("groupdav-props\n".print_r($props,true));
|
||||
if ($address_data)
|
||||
{
|
||||
//$sta = microtime(true);
|
||||
$content = $handler->getVCard($contact,$this->charset,false);
|
||||
//$en = microtime(true) - $sta;
|
||||
//error_log("getVCard took : $en");
|
||||
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength',bytes($content));
|
||||
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'address-data',$content);
|
||||
}
|
||||
@ -142,7 +160,7 @@ class addressbook_groupdav extends groupdav_handler
|
||||
{
|
||||
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength', ''); // expensive to calculate and no CalDAV client uses it
|
||||
}
|
||||
$files['files'][] = array(
|
||||
$files[] = array(
|
||||
'path' => self::get_path($contact),
|
||||
'props' => $props,
|
||||
);
|
||||
@ -152,10 +170,8 @@ class addressbook_groupdav extends groupdav_handler
|
||||
//error_log("function propfind foreach : $end : $icount");
|
||||
}
|
||||
}
|
||||
|
||||
//$endtime = microtime(true) - $this->starttime;
|
||||
//error_log(__FILE__ ."->". __METHOD__ ." elapsed time : $endtime");
|
||||
return true;
|
||||
if ($this->debug) error_log(__METHOD__.'('.array2string($filter).','.array2string($start).") took ".(microtime(true) - $starttime).' to return '.count($files).' items');
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -600,8 +600,14 @@ class HTTP_WebDAV_Server
|
||||
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
||||
echo "<D:multistatus xmlns:D=\"DAV:\">\n";
|
||||
|
||||
// using an ArrayIterator to prevent foreach from copying the array,
|
||||
// as we cant loop by reference, when an iterator is given in $files['files']
|
||||
if (is_array($files['files']))
|
||||
{
|
||||
$files['files'] = new ArrayIterator($files['files']);
|
||||
}
|
||||
// now we loop over all returned file entries
|
||||
foreach ($files["files"] as &$file) {
|
||||
foreach ($files['files'] as $file) {
|
||||
|
||||
// collect namespaces here
|
||||
$ns_hash = array();
|
||||
@ -687,7 +693,8 @@ class HTTP_WebDAV_Server
|
||||
= $this->mkprop("DAV:",
|
||||
"lockdiscovery",
|
||||
$this->lockdiscovery($file['path']));
|
||||
} else {
|
||||
// only collect $file['noprops'] if we have NO Brief: t HTTP Header
|
||||
} elseif (!isset($this->_SERVER['HTTP_BRIEF']) || $this->_SERVER['HTTP_BRIEF'] != 't') {
|
||||
// add empty value for this property
|
||||
$file["noprops"][] =
|
||||
$this->mkprop($reqprop["xmlns"], $reqprop["name"], "");
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* eGroupWare: CalDAV/CardDAV/GroupDAV access
|
||||
* EGroupware: CalDAV/CardDAV/GroupDAV access
|
||||
*
|
||||
* @link http://www.egroupware.org
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
@ -14,7 +14,7 @@
|
||||
require_once('HTTP/WebDAV/Server.php');
|
||||
|
||||
/**
|
||||
* eGroupWare: GroupDAV access
|
||||
* EGroupware: GroupDAV access
|
||||
*
|
||||
* Using a modified PEAR HTTP/WebDAV/Server class from egw-pear!
|
||||
*
|
||||
@ -172,7 +172,6 @@ class groupdav extends HTTP_WebDAV_Server
|
||||
|
||||
$files = array('files' => array());
|
||||
|
||||
error_log(__METHOD__."($options[path],,$method) app=$app, user=$user, id=$id, user_prefix=$user_prefix");
|
||||
if (!$app) // root folder containing apps
|
||||
{
|
||||
// self url
|
||||
@ -375,41 +374,44 @@ error_log(__METHOD__."($options[path],,$method) app=$app, user=$user, id=$id, us
|
||||
}
|
||||
echo "</h1>\n";
|
||||
|
||||
$collection_props = self::props2array($files['files'][0]['props']);
|
||||
echo '<h3>'.lang('Collection listing').': '.htmlspecialchars($collection_props['DAV:displayname'])."</h3>\n";
|
||||
//_debug_array($files['files']);
|
||||
|
||||
if (count($files['files']) <= 1)
|
||||
$n = 0;
|
||||
foreach($files['files'] as $file)
|
||||
{
|
||||
if (!isset($collection_props))
|
||||
{
|
||||
$collection_props = self::props2array($file['props']);
|
||||
echo '<h3>'.lang('Collection listing').': '.htmlspecialchars($collection_props['DAV:displayname'])."</h3>\n";
|
||||
continue; // own entry --> displaying properies later
|
||||
}
|
||||
if(!$n++)
|
||||
{
|
||||
echo "<table>\n\t<tr class='th'><th>#</th><th>".lang('Name')."</th><th>".lang('Size')."</th><th>".lang('Last modified')."</th><th>".
|
||||
lang('ETag')."</th><th>".lang('Content type')."</th><th>".lang('Resource type')."</th></tr>\n";
|
||||
}
|
||||
$props = self::props2array($file['props']);
|
||||
//echo $file['path']; _debug_array($props);
|
||||
$class = $class == 'row_on' ? 'row_off' : 'row_on';
|
||||
if (substr($file['path'],-1) == '/')
|
||||
{
|
||||
$name = basename(substr($file['path'],0,-1)).'/';
|
||||
}
|
||||
else
|
||||
{
|
||||
$name = basename($file['path']);
|
||||
}
|
||||
echo "\t<tr class='$class'>\n\t\t<td>$n</td>\n\t\t<td>".html::a_href(htmlspecialchars($name),'/groupdav.php'.$file['path'])."</td>\n";
|
||||
echo "\t\t<td>".$props['DAV:getcontentlength']."</td>\n";
|
||||
echo "\t\t<td>".(!empty($props['DAV:getlastmodified']) ? date('Y-m-d H:i:s',$props['DAV:getlastmodified']) : '')."</td>\n";
|
||||
echo "\t\t<td>".$props['DAV:getetag']."</td>\n";
|
||||
echo "\t\t<td>".htmlspecialchars($props['DAV:getcontenttype'])."</td>\n";
|
||||
echo "\t\t<td>".self::prop_value($props['DAV:resourcetype'])."</td>\n\t</tr>\n";
|
||||
}
|
||||
if (!$n)
|
||||
{
|
||||
echo '<p>'.lang('Collection empty.')."</p>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<table>\n\t<tr class='th'><th>".lang('Name')."</th><th>".lang('Size')."</th><th>".lang('Last modified')."</th><th>".
|
||||
lang('ETag')."</th><th>".lang('Content type')."</th><th>".lang('Resource type')."</th></tr>\n";
|
||||
|
||||
foreach($files['files'] as $n => $file)
|
||||
{
|
||||
if (!$n) continue; // own entry --> displaying properies later
|
||||
|
||||
$props = self::props2array($file['props']);
|
||||
//echo $file['path']; _debug_array($props);
|
||||
$class = $class == 'row_on' ? 'row_off' : 'row_on';
|
||||
if (substr($file['path'],-1) == '/')
|
||||
{
|
||||
$name = basename(substr($file['path'],0,-1)).'/';
|
||||
}
|
||||
else
|
||||
{
|
||||
$name = basename($file['path']);
|
||||
}
|
||||
echo "\t<tr class='$class'>\n\t\t<td>".html::a_href(htmlspecialchars($name),'/groupdav.php'.$file['path'])."</td>\n";
|
||||
echo "\t\t<td>".$props['DAV:getcontentlength']."</td>\n";
|
||||
echo "\t\t<td>".(!empty($props['DAV:getlastmodified']) ? date('Y-m-d H:i:s',$props['DAV:getlastmodified']) : '')."</td>\n";
|
||||
echo "\t\t<td>".$props['DAV:getetag']."</td>\n";
|
||||
echo "\t\t<td>".htmlspecialchars($props['DAV:getcontenttype'])."</td>\n";
|
||||
echo "\t\t<td>".self::prop_value($props['DAV:resourcetype'])."</td>\n\t</tr>\n";
|
||||
}
|
||||
echo "</table>\n";
|
||||
}
|
||||
echo '<h3>'.lang('Properties')."</h3>\n";
|
||||
|
@ -1,18 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* eGroupWare: GroupDAV access: abstract baseclass for groupdav/caldav/carddav handlers
|
||||
* EGroupware: GroupDAV access: abstract baseclass for groupdav/caldav/carddav handlers
|
||||
*
|
||||
* @link http://www.egroupware.org
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @subpackage groupdav
|
||||
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
* @copyright (c) 2007/8 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
* @copyright (c) 2007-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* eGroupWare: GroupDAV access: abstract baseclass for groupdav/caldav/carddav handlers
|
||||
* EGroupware: GroupDAV access: abstract baseclass for groupdav/caldav/carddav handlers
|
||||
*/
|
||||
abstract class groupdav_handler
|
||||
{
|
||||
@ -86,7 +86,7 @@ abstract class groupdav_handler
|
||||
#if (!is_null($debug)) $this->debug = $debug = 3;
|
||||
$this->base_uri = is_null($base_uri) ? $base_uri : $_SERVER['SCRIPT_NAME'];
|
||||
$this->agent = self::get_agent();
|
||||
|
||||
|
||||
$this->translation =& $GLOBALS['egw']->translation;
|
||||
$this->egw_charset = $this->translation->charset();
|
||||
}
|
||||
@ -102,6 +102,16 @@ abstract class groupdav_handler
|
||||
*/
|
||||
abstract function propfind($path,$options,&$files,$user);
|
||||
|
||||
/**
|
||||
* Propfind callback, if interator is used
|
||||
*
|
||||
* @param array $filter
|
||||
* @param array|boolean $start false=return all or array(start,num)
|
||||
* @param int &$total
|
||||
* @return array with "files" array with values for keys path and props
|
||||
*/
|
||||
function &propfind_callback(array $filter,$start,&$total) { }
|
||||
|
||||
/**
|
||||
* Handle get request for an applications entry
|
||||
*
|
||||
@ -306,3 +316,134 @@ abstract class groupdav_handler
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator for propfinds using propfind callback of a groupdav_handler to query results in chunks
|
||||
*
|
||||
* The propfind method just computes a filter and then returns an instance of this iterator instead of the files:
|
||||
*
|
||||
* function propfind($path,$options,&$files,$user,$id='')
|
||||
* {
|
||||
* $filter = array();
|
||||
* // compute filter from path, options, ...
|
||||
*
|
||||
* $files['files'] = new groupdav_propfind_iterator($this,$filter,$files['files']);
|
||||
*
|
||||
* return true;
|
||||
* }
|
||||
*/
|
||||
class groupdav_propfind_iterator implements Iterator
|
||||
{
|
||||
/**
|
||||
* Handler to call for entries
|
||||
*
|
||||
* @var groupdav_handler
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
/**
|
||||
* Filter of propfind call
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $filter;
|
||||
|
||||
/**
|
||||
* Extra responses to return too
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Start value for callback
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $start=0;
|
||||
|
||||
/**
|
||||
* Number of entries queried from callback in one call
|
||||
*
|
||||
*/
|
||||
const CHUNK_SIZE = 500;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param groupdav_handler $handler
|
||||
* @param array $filter filter for propfind call
|
||||
* @param array $files=null extra files/responses to return too
|
||||
*/
|
||||
public function __construct(groupdav_handler $handler,array $filter,array &$files=null)
|
||||
{
|
||||
$this->handler = $handler;
|
||||
$this->filter = $filter;
|
||||
$this->files = $files;
|
||||
reset($this->files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the current element
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
$current = $this->current();
|
||||
|
||||
return $current['path']; // we return path as key
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element (called after each foreach loop)
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
if (next($this->files) !== false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!$this->handler)
|
||||
{
|
||||
return false; // no further entries
|
||||
}
|
||||
// try query further files via propfind callback of handler and store result in $this->files
|
||||
$this->files = $this->handler->propfind_callback($this->filter,array($this->start,self::CHUNK_SIZE));
|
||||
$this->start += self::CHUNK_SIZE;
|
||||
reset($this->files);
|
||||
|
||||
if (count($this->files) < self::CHUNK_SIZE) // less entries then asked --> no further available
|
||||
{
|
||||
unset($this->handler);
|
||||
}
|
||||
return current($this->files) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the Iterator to the first element (called at beginning of foreach loop)
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function valid ()
|
||||
{
|
||||
return current($this->files) !== false;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user