mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-23 00:13:35 +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
|
<?php
|
||||||
/**
|
/**
|
||||||
* eGroupWare: GroupDAV access: addressbook handler
|
* EGroupware: GroupDAV access: addressbook handler
|
||||||
*
|
*
|
||||||
* @link http://www.egroupware.org
|
* @link http://www.egroupware.org
|
||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
* @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
|
class addressbook_groupdav extends groupdav_handler
|
||||||
{
|
{
|
||||||
@ -84,7 +87,6 @@ class addressbook_groupdav extends groupdav_handler
|
|||||||
*/
|
*/
|
||||||
function propfind($path,$options,&$files,$user,$id='')
|
function propfind($path,$options,&$files,$user,$id='')
|
||||||
{
|
{
|
||||||
//$this->starttime = microtime(true);
|
|
||||||
$filter = array();
|
$filter = array();
|
||||||
// show addressbook of a single user?
|
// show addressbook of a single user?
|
||||||
if ($user && $path != '/addressbook/') $filter['contact_owner'] = $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));
|
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
|
// 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)
|
foreach($options['props'] as $prop)
|
||||||
{
|
{
|
||||||
if ($prop['name'] == 'address-data')
|
if ($prop['name'] == 'address-data')
|
||||||
{
|
{
|
||||||
$address_data = true;
|
$filter['address_data'] = true;
|
||||||
break;
|
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();
|
$handler = self::_get_handler();
|
||||||
}
|
}
|
||||||
|
unset($filter['address_data']);
|
||||||
|
|
||||||
|
$files = array();
|
||||||
// we query etag and modified, as LDAP does not have the strong sql etag
|
// 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)))
|
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)
|
foreach($contacts as $contact)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -131,10 +152,7 @@ class addressbook_groupdav extends groupdav_handler
|
|||||||
////error_log("groupdav-props\n".print_r($props,true));
|
////error_log("groupdav-props\n".print_r($props,true));
|
||||||
if ($address_data)
|
if ($address_data)
|
||||||
{
|
{
|
||||||
//$sta = microtime(true);
|
|
||||||
$content = $handler->getVCard($contact,$this->charset,false);
|
$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('getcontentlength',bytes($content));
|
||||||
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'address-data',$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
|
$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),
|
'path' => self::get_path($contact),
|
||||||
'props' => $props,
|
'props' => $props,
|
||||||
);
|
);
|
||||||
@ -152,10 +170,8 @@ class addressbook_groupdav extends groupdav_handler
|
|||||||
//error_log("function propfind foreach : $end : $icount");
|
//error_log("function propfind foreach : $end : $icount");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($this->debug) error_log(__METHOD__.'('.array2string($filter).','.array2string($start).") took ".(microtime(true) - $starttime).' to return '.count($files).' items');
|
||||||
//$endtime = microtime(true) - $this->starttime;
|
return $files;
|
||||||
//error_log(__FILE__ ."->". __METHOD__ ." elapsed time : $endtime");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -600,8 +600,14 @@ class HTTP_WebDAV_Server
|
|||||||
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
||||||
echo "<D:multistatus xmlns:D=\"DAV:\">\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
|
// now we loop over all returned file entries
|
||||||
foreach ($files["files"] as &$file) {
|
foreach ($files['files'] as $file) {
|
||||||
|
|
||||||
// collect namespaces here
|
// collect namespaces here
|
||||||
$ns_hash = array();
|
$ns_hash = array();
|
||||||
@ -687,7 +693,8 @@ class HTTP_WebDAV_Server
|
|||||||
= $this->mkprop("DAV:",
|
= $this->mkprop("DAV:",
|
||||||
"lockdiscovery",
|
"lockdiscovery",
|
||||||
$this->lockdiscovery($file['path']));
|
$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
|
// add empty value for this property
|
||||||
$file["noprops"][] =
|
$file["noprops"][] =
|
||||||
$this->mkprop($reqprop["xmlns"], $reqprop["name"], "");
|
$this->mkprop($reqprop["xmlns"], $reqprop["name"], "");
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* eGroupWare: CalDAV/CardDAV/GroupDAV access
|
* EGroupware: CalDAV/CardDAV/GroupDAV access
|
||||||
*
|
*
|
||||||
* @link http://www.egroupware.org
|
* @link http://www.egroupware.org
|
||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||||
@ -14,7 +14,7 @@
|
|||||||
require_once('HTTP/WebDAV/Server.php');
|
require_once('HTTP/WebDAV/Server.php');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* eGroupWare: GroupDAV access
|
* EGroupware: GroupDAV access
|
||||||
*
|
*
|
||||||
* Using a modified PEAR HTTP/WebDAV/Server class from egw-pear!
|
* 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());
|
$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
|
if (!$app) // root folder containing apps
|
||||||
{
|
{
|
||||||
// self url
|
// self url
|
||||||
@ -375,23 +374,20 @@ error_log(__METHOD__."($options[path],,$method) app=$app, user=$user, id=$id, us
|
|||||||
}
|
}
|
||||||
echo "</h1>\n";
|
echo "</h1>\n";
|
||||||
|
|
||||||
$collection_props = self::props2array($files['files'][0]['props']);
|
$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";
|
echo '<h3>'.lang('Collection listing').': '.htmlspecialchars($collection_props['DAV:displayname'])."</h3>\n";
|
||||||
//_debug_array($files['files']);
|
continue; // own entry --> displaying properies later
|
||||||
|
|
||||||
if (count($files['files']) <= 1)
|
|
||||||
{
|
|
||||||
echo '<p>'.lang('Collection empty.')."</p>\n";
|
|
||||||
}
|
}
|
||||||
else
|
if(!$n++)
|
||||||
{
|
{
|
||||||
echo "<table>\n\t<tr class='th'><th>".lang('Name')."</th><th>".lang('Size')."</th><th>".lang('Last modified')."</th><th>".
|
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";
|
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']);
|
$props = self::props2array($file['props']);
|
||||||
//echo $file['path']; _debug_array($props);
|
//echo $file['path']; _debug_array($props);
|
||||||
$class = $class == 'row_on' ? 'row_off' : 'row_on';
|
$class = $class == 'row_on' ? 'row_off' : 'row_on';
|
||||||
@ -403,13 +399,19 @@ error_log(__METHOD__."($options[path],,$method) app=$app, user=$user, id=$id, us
|
|||||||
{
|
{
|
||||||
$name = basename($file['path']);
|
$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<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>".$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>".(!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>".$props['DAV:getetag']."</td>\n";
|
||||||
echo "\t\t<td>".htmlspecialchars($props['DAV:getcontenttype'])."</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 "\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";
|
echo "</table>\n";
|
||||||
}
|
}
|
||||||
echo '<h3>'.lang('Properties')."</h3>\n";
|
echo '<h3>'.lang('Properties')."</h3>\n";
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
<?php
|
<?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
|
* @link http://www.egroupware.org
|
||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||||
* @package api
|
* @package api
|
||||||
* @subpackage groupdav
|
* @subpackage groupdav
|
||||||
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
* @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$
|
* @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
|
abstract class groupdav_handler
|
||||||
{
|
{
|
||||||
@ -102,6 +102,16 @@ abstract class groupdav_handler
|
|||||||
*/
|
*/
|
||||||
abstract function propfind($path,$options,&$files,$user);
|
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
|
* 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