* CardDAV: store name part of URL from client PUT request, to fully comply with CardDAV spec

This commit is contained in:
Ralf Becker 2011-04-05 20:39:13 +00:00
parent ca7dfd3370
commit 87ee0f0088
8 changed files with 106 additions and 57 deletions

View File

@ -7,7 +7,7 @@
* @package addressbook
* @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-11 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
@ -77,9 +77,11 @@ class addressbook_groupdav extends groupdav_handler
var $charset = 'utf-8';
/**
* What attribute is used to construct the path, default id, can be uid too
* Which attribute to use to contruct name part of url/path
*
* @var string
*/
const PATH_ATTRIBUTE = 'id';
static $path_attr = 'id';
/**
* Constructor
@ -94,6 +96,18 @@ class addressbook_groupdav extends groupdav_handler
parent::__construct($app,$debug,$base_uri,$principalURL);
$this->bo = new addressbook_bo();
// since 1.9.007 we allow clients to specify the URL when creating a new contact, as specified by CardDAV
if ($this->bo->account_repository != 'ldap' &&
version_compare($GLOBALS['egw_info']['apps']['phpgwapi']['version'], '1.9.007', '>='))
{
self::$path_attr = 'carddav_name';
groupdav_handler::$path_extension = '';
}
else
{
groupdav_handler::$path_extension = '.vcf';
}
}
/**
@ -104,7 +118,7 @@ class addressbook_groupdav extends groupdav_handler
*/
static function get_path($contact)
{
return $contact[self::PATH_ATTRIBUTE].'.vcf';
return $contact[self::$path_attr].groupdav_handler::$path_extension;
}
/**
@ -169,7 +183,9 @@ class addressbook_groupdav extends groupdav_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(),array('id','uid','etag','modified'),'egw_addressbook.contact_id','','',False,'AND',$start,$filter)))
$cols = array('id','uid','etag','modified');
if (!in_array(self::$path_attr,$cols)) $cols[] = self::$path_attr;
if (($contacts =& $this->bo->search(array(),$cols,'egw_addressbook.contact_id','','',False,'AND',$start,$filter)))
{
foreach($contacts as &$contact)
{
@ -258,15 +274,18 @@ class addressbook_groupdav extends groupdav_handler
if ($option['name'] == 'href')
{
$parts = explode('/',$option['data']);
if (($id = array_pop($parts))) $ids[] = basename($id,'.vcf');
if (($id = array_pop($parts)))
{
$ids[] = groupdav_handler::$path_extension ? basename($id,groupdav_handler::$path_extension) : $id;
}
}
}
if ($ids) $filters[self::PATH_ATTRIBUTE] = $ids;
if ($ids) $filters[self::$path_attr] = $ids;
if ($this->debug) error_log(__METHOD__."($path,,,$user) addressbook-multiget: ids=".implode(',',$ids));
}
elseif ($id)
{
$filters[self::PATH_ATTRIBUTE] = basename($id,'.vcf');
$filters[self::$path_attr] = groupdav_handler::$path_extension ? basename($id,groupdav_handler::$path_extension) : $id;
}
return true;
}
@ -337,38 +356,20 @@ class addressbook_groupdav extends groupdav_handler
}
}
if (is_array($oldContact))
$contact = $handler->vcardtoegw($vCard, $charset);
if (is_array($oldContact) || ($oldContact = $this->bo->read(array('contact_uid' => $contact['uid']))))
{
$contactId = $oldContact['id'];
$retval = true;
}
else
{
// new entry?
if (($foundContacts = $handler->search($vCard, null, false, $charset)))
{
if (($contactId = array_shift($foundContacts)) &&
($oldContact = $this->bo->read($contactId)))
{
$retval = '301 Moved Permanently';
}
else
{
// to be safe
$contactId = -1;
$retval = '201 Created';
}
}
else
{
// new entry
$contactId = -1;
$retval = '201 Created';
}
// new entry
$contactId = -1;
$retval = '201 Created';
}
$contact = $handler->vcardtoegw($vCard, $charset);
if (is_array($contact['cat_id']))
{
$contact['cat_id'] = implode(',',$this->bo->find_or_add_categories($contact['cat_id'], $contactId));
@ -384,6 +385,11 @@ class addressbook_groupdav extends groupdav_handler
$contact['uid'] = $oldContact['uid'];
$contact['owner'] = $oldContact['owner'];
$contact['private'] = $oldContact['private'];
$contact['carddav_name'] = $oldContact['carddav_name'];
}
else
{
$contact['carddav_name'] = $id;
}
// only set owner, if user is explicitly specified in URL (check via prefix, NOT for /addressbook/ !)
if ($prefix)
@ -415,14 +421,15 @@ class addressbook_groupdav extends groupdav_handler
}
header('ETag: '.$this->get_etag($contact));
if ($retval !== true)
// send GroupDAV Location header only if we dont use carddav_name as path-attribute
if ($retval !== true && self::$path_attr == 'id')
{
$path = preg_replace('|(.*)/[^/]*|', '\1/', $options['path']);
header($h='Location: '.$this->base_uri.$path.self::get_path($contact));
if ($this->debug) error_log(__METHOD__."($method,,$id) header('$h'): $retval");
return $retval;
}
return true;
return $retval;
}
/**
@ -547,18 +554,18 @@ class addressbook_groupdav extends groupdav_handler
{
return '412 Precondition Failed';
}
//return $ok;
return true;
}
/**
* Read a contact
*
* @param string/id $id
* @param string|id $id
* @return array/boolean array with entry, false if no read rights, null if $id does not exist
*/
function read($id)
{
return $this->bo->read(self::PATH_ATTRIBUTE == 'id' ? $id : array(self::PATH_ATTRIBUTE => $id));
return $this->bo->read(array(self::$path_attr => $id));
}
/**

View File

@ -108,7 +108,7 @@ class addressbook_sql extends so_sql_cf
if (isset($param['advanced_search']) && !empty($param['advanced_search'])) $advanced_search = true;
$wildcard ='%';
if ($advanced_search || (isset($param['wildcard']) && !empty($param['wildcard']))) $wildcard = ($param['wildcard']?$param['wildcard']:'');
// fix cat_id filter to search in comma-separated multiple cats and return subcats
if ((int)$filter['cat_id'])
{
@ -632,12 +632,22 @@ class addressbook_sql extends so_sql_cf
}
}
$update = array();
// enforce a minium uid strength
if (!$err && (!isset($this->data['uid'])
|| strlen($this->data['uid']) < $minimum_uid_length)) {
parent::update(array('uid' => common::generate_uid('addressbook',$this->data['id'])));
if (!isset($this->data['uid']) || strlen($this->data['uid']) < $minimum_uid_length)
{
$update['uid'] = common::generate_uid('addressbook',$this->data['id']);
//echo "<p>set uid={$this->data['uid']}, etag={$this->data['etag']}</p>";
}
// set carddav_name, if not given by caller
if (empty($this->data['carddav_name']) && version_compare($GLOBALS['egw_info']['apps']['phpgwapi']['version'], '1.9.007', '>='))
{
$update['carddav_name'] = $this->data['id'].'.vcf';
}
if (!$err && $update)
{
parent::update($update);
}
return $err;
}

View File

@ -7,7 +7,7 @@
* @package api
* @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-11 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
@ -122,7 +122,6 @@ class groupdav extends HTTP_WebDAV_Server
*/
var $accounts;
function __construct()
{
if (!$this->debug) $this->debug = (int)$GLOBALS['egw_info']['user']['preferences']['groupdav']['debug_level'];
@ -966,10 +965,7 @@ class groupdav extends HTTP_WebDAV_Server
$user = $GLOBALS['egw_info']['user']['account_id'];
}
if (($id = array_pop($parts)))
{
list($id) = explode('.',$id); // remove evtl. .ics extension
}
$id = array_pop($parts);
$ok = $id && $user && in_array($app,array('addressbook','calendar','infolog','principals'));
if ($this->debug)

View File

@ -7,7 +7,7 @@
* @package api
* @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-11 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
@ -78,6 +78,13 @@ abstract class groupdav_handler
*/
var $agent;
/**
* Extension to append to url/path
*
* @var string
*/
static $path_extension = '.ics';
/**
* Constructor
*
@ -105,7 +112,6 @@ abstract class groupdav_handler
$this->agent = self::get_agent();
$this->egw_charset = translation::charset();
$this->accounts = $GLOBALS['egw']->accounts;
}
/**
@ -230,12 +236,14 @@ abstract class groupdav_handler
*
* @param string $method GET, PUT, DELETE
* @param array &$options
* @param int $id
* @param int|string &$id on return self::$path_extension got removed
* @param boolean &$return_no_access=false if set to true on call, instead of '403 Forbidden' the entry is returned and $return_no_access===false
* @return array|string entry on success, string with http-error-code on failure, null for PUT on an unknown id
*/
function _common_get_put_delete($method,&$options,$id,&$return_no_access=false)
function _common_get_put_delete($method,&$options,&$id,&$return_no_access=false)
{
if (self::$path_extension) $id = basename($id,self::$path_extension);
if ($this->app != 'principals' && !$GLOBALS['egw_info']['user']['apps'][$this->app])
{
if ($this->debug) error_log(__METHOD__."($method,,$id) 403 Forbidden: no app rights for '$this->app'");
@ -535,6 +543,7 @@ class groupdav_propfind_iterator implements Iterator
$this->start = 0;
$this->files = $this->common_files;
if (!$this->files) $this->next(); // otherwise valid will return false and nothing get returned
reset($this->files);
}

View File

@ -7,7 +7,7 @@
* @package api
* @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-11 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
@ -18,6 +18,12 @@
*/
class groupdav_principals extends groupdav_handler
{
/**
* Reference to the accounts class
*
* @var accounts
*/
var $accounts;
/**
* Constructor
@ -30,6 +36,8 @@ class groupdav_principals extends groupdav_handler
function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
{
parent::__construct($app,$debug,$base_uri,$principalURL);
$this->accounts = $GLOBALS['egw']->accounts;
}
/**

View File

@ -12,7 +12,7 @@
/* Basic information about this app */
$setup_info['phpgwapi']['name'] = 'phpgwapi';
$setup_info['phpgwapi']['title'] = 'eGroupWare API';
$setup_info['phpgwapi']['version'] = '1.9.006';
$setup_info['phpgwapi']['version'] = '1.9.007';
$setup_info['phpgwapi']['versions']['current_header'] = '1.29';
$setup_info['phpgwapi']['enable'] = 3;
$setup_info['phpgwapi']['app_order'] = 1;
@ -74,4 +74,3 @@ $setup_info['groupdav']['author'] = $setup_info['groupdav']['maintainer'] = arra
$setup_info['groupdav']['license'] = 'GPL';
$setup_info['groupdav']['hooks']['preferences'] = 'groupdav_hooks::menus';
$setup_info['groupdav']['hooks']['settings'] = 'groupdav_hooks::settings';

View File

@ -336,11 +336,12 @@ $phpgw_baseline = array(
'contact_etag' => array('type' => 'int','precision' => '4','default' => '0'),
'contact_uid' => array('type' => 'varchar','precision' => '255'),
'adr_one_countrycode' => array('type' => 'varchar','precision' => '2'),
'adr_two_countrycode' => array('type' => 'varchar','precision' => '2')
'adr_two_countrycode' => array('type' => 'varchar','precision' => '2'),
'carddav_name' => array('type' => 'varchar','precision' => '64','comment' => 'name part of CardDAV URL, if specified by client')
),
'pk' => array('contact_id'),
'fk' => array(),
'ix' => array('contact_owner','cat_id','n_fileas','contact_uid',array('n_family','n_given'),array('n_given','n_family'),array('org_name','n_family','n_given')),
'ix' => array('contact_owner','cat_id','n_fileas','contact_uid','caldav_name',array('n_family','n_given'),array('n_given','n_family'),array('org_name','n_family','n_given')),
'uc' => array('account_id')
),
'egw_addressbook_extra' => array(

View File

@ -154,3 +154,22 @@ function phpgwapi_upgrade1_9_005()
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.9.006';
}
/**
* Add column to store CalDAV name given by client
*/
function phpgwapi_upgrade1_9_006()
{
$GLOBALS['egw_setup']->oProc->AddColumn('egw_addressbook','carddav_name',array(
'type' => 'varchar',
'precision' => '64',
'comment' => 'name part of CardDAV URL, if specified by client'
));
$GLOBALS['egw_setup']->db->query($sql='UPDATE egw_addressbook SET carddav_name='.
$GLOBALS['egw_setup']->db->concat(
$GLOBALS['egw_setup']->db->to_varchar('contact_id'),"'.vcf'"),__LINE__,__FILE__);
$GLOBALS['egw_setup']->oProc->CreateIndex('egw_addressbook','carddav_name');
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.9.007';
}