* CardDAV pref which addressbooks to sync and many fixes for iPhone OS 4.0, 4.1, 4.2beta and Mac iCal and contact app

Merge of following commits from Trunk:
r32609: * GroupDAV preference for addressbook-home-set (requires to register hooks)
r32610: missing groupdav hooks
r32611: fixed missing "users" of principal url in calendar-user-address-set
r32615: as the pricipal of current user is influenced by GroupDAV prefs, we have to include them in the etag
r32619: loop over existing addressbooks, to make sure each ab is only once in addressbook-home-set, even when selected multiple times in the prefs because of symbolic ab like "primary group"
r32620: urlencode and decode account_lid in url to cope with group-names with space in it, which stall iPhone OS 4.2 devices
r32621: fixed bug: GroupDAV/CardDAV PUT request to /addressbook/ changes owner, also checking now required ACL for moving contacts between addressbooks
r32622: returning "403 Forbidden" if addressbook_bo->save() fails, happens when writing new entries in ABs without ADD rights
r32623: * iCal on iPhone detects URL now correct
reverted calendar-home-set to report only users calendar, as reporting multiple break propfind
r32624: we need a real redirect, not just a proxy
r32631: fixed working in GroupDAV prefs and translation
This commit is contained in:
Ralf Becker 2010-10-21 11:26:47 +00:00
commit fa58c2adc4
10 changed files with 181 additions and 30 deletions

View File

@ -299,9 +299,10 @@ class addressbook_groupdav extends groupdav_handler
* @param array &$options
* @param int $id
* @param int $user=null account_id of owner, default null
* @param string $prefix=null user prefix from path (eg. /ralf from /ralf/addressbook)
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
*/
function put(&$options,$id,$user=null)
function put(&$options,$id,$user=null,$prefix=null)
{
if ($this->debug) error_log(__METHOD__.'('.array2string($options).",$id,$user)");
@ -383,12 +384,21 @@ class addressbook_groupdav extends groupdav_handler
$contact['id'] = $oldContact['id'];
// dont allow the client to overwrite certain values
$contact['uid'] = $oldContact['uid'];
//$contact['owner'] = $oldContact['owner'];
$contact['owner'] = $oldContact['owner'];
$contact['private'] = $oldContact['private'];
}
$contact['owner'] = $user;
// only set owner, if user is explicitly specified in URL (check via prefix, NOT for /addressbook/ !)
if ($prefix)
{
// check for modified owners, if user has an add right for the new addressbook and
// delete rights for the old addressbook (_common_get_put_delete checks for PUT only EGW_ACL_EDIT)
if ($oldContact && $user != $oldContact['owner'] && !($this->bo->grants[$user] & EGW_ACL_ADD) &&
(!$this->bo->grants[$oldContact['owner']] & EGW_ACL_DELETE))
{
return '403 Forbidden';
}
$contact['owner'] = $user;
}
if ($this->http_if_match) $contact['etag'] = self::etag2value($this->http_if_match);
if (!($save_ok = $this->bo->save($contact)))
@ -398,7 +408,7 @@ class addressbook_groupdav extends groupdav_handler
{
return '412 Precondition Failed';
}
return false;
return '403 Forbidden'; // happens when writing new entries in AB's without ADD rights
}
if (!isset($contact['etag']))

View File

@ -25,4 +25,4 @@ RewriteBase /
RewriteRule ^.well-known/(caldav|carddav)$ /egroupware/groupdav.php/ [R]
RewriteCond %{REQUEST_METHOD} ^(PROPFIND|OPTIONS)$
RewriteRule ^/$ /egroupware/groupdav.php/
RewriteRule ^/$ /egroupware/groupdav.php/ [R]

View File

@ -32,6 +32,7 @@ require_once('HTTP/WebDAV/Server.php');
* Calling one of the above collections with a GET request / regular browser generates an automatic index
* from the data of a allprop PROPFIND, allow to browse CalDAV/CardDAV/GroupDAV tree with a regular browser.
*
* @todo All principal urls should either contain no account_lid (eg. base64 of it) or use urlencode($account_lid)
* @link http://www.groupdav.org GroupDAV spec
*/
class groupdav extends HTTP_WebDAV_Server
@ -81,6 +82,9 @@ class groupdav extends HTTP_WebDAV_Server
'resourcetype' => array(self::GROUPDAV => 'vtodo-collection', self::CALDAV => 'calendar'),
'component-set' => array(self::GROUPDAV => 'VTODO'),
),
'principals' => array(
'resourcetype' => array(self::DAV => 'principal'),
)
);
/**
* Debug level: 0 = nothing, 1 = function calls, 2 = more info, 3 = complete $_SERVER array
@ -134,12 +138,14 @@ class groupdav extends HTTP_WebDAV_Server
break;
case 'davkit': // iCal app in OS X 10.6 created wrong request, if full url given
$this->client_require_href_as_url = false;
$this->cnrnd = true;
break;
case 'cfnetwork_old':
$this->crrnd = true; // Older Apple Addressbook.app does not cope with namespace redundancy
break;
case 'neon':
$this->cnrnd = true; // neon clients like cadaver
break;
}
// adding EGroupware version to X-Dav-Powered-By header eg. "EGroupware 1.8.001 CalDAV/CardDAV/GroupDAV server"
$this->dav_powered_by = str_replace('EGroupware','EGroupware '.$GLOBALS['egw_info']['server']['versions']['phpgwapi'],
@ -252,8 +258,8 @@ class groupdav extends HTTP_WebDAV_Server
$user_prefix = '/'; //.$GLOBALS['egw_info']['user']['account_lid'].'/';
}
$calendar_user_address_set = array(
self::mkprop('href',$this->base_uri.'/principals/'.$principalType.'/'.$account['account_lid'].'/'),
self::mkprop('href','urn:uuid:'.$account['account_lid']));
self::mkprop('href','urn:uuid:'.$account['account_lid']),
);
if ($user < 0)
{
$principalType = 'groups';
@ -265,6 +271,8 @@ class groupdav extends HTTP_WebDAV_Server
$displayname = $account['account_fullname'];
$calendar_user_address_set[] = self::mkprop('href','MAILTO:'.$account['account_email']);
}
$calendar_user_address_set[] = self::mkprop('href',$this->base_uri.'/principals/'.$principalType.'/'.$account['account_lid'].'/');
if ($options['depth'] && $user_prefix == '/')
{
$displayname = 'EGroupware (Cal|Card|Group)DAV server';
@ -334,7 +342,7 @@ class groupdav extends HTTP_WebDAV_Server
}
return true;
}
if (!in_array($app,array('principals','groups')) && !$GLOBALS['egw_info']['user']['apps'][$app])
if ($app != 'principals' && !$GLOBALS['egw_info']['user']['apps'][$app])
{
if ($this->debug) error_log(__CLASS__."::$method(path=$options[path]) 403 Forbidden: no app rights for '$app'");
return "403 Forbidden: no app rights for '$app'"; // no rights for the given app
@ -729,13 +737,13 @@ class groupdav extends HTTP_WebDAV_Server
if ($this->debug) error_log(__METHOD__.'('.array2string($options).')');
if (!$this->_parse_path($options['path'],$id,$app,$user))
if (!$this->_parse_path($options['path'],$id,$app,$user,$prefix))
{
return '404 Not Found';
}
if (($handler = self::app_handler($app)))
{
$status = $handler->put($options,$id,$user);
$status = $handler->put($options,$id,$user,$prefix);
// set default stati: true --> 204 No Content, false --> should be already handled
if (is_bool($status)) $status = $status ? '204 No Content' : '400 Something went wrong';
return $status;
@ -918,7 +926,8 @@ class groupdav extends HTTP_WebDAV_Server
}
$parts = explode('/', $this->_unslashify($path));
if (($account_id = $this->accounts->name2id($parts[0], 'account_lid')))
if (($account_id = $this->accounts->name2id($parts[0], 'account_lid')) ||
($account_id = $this->accounts->name2id($parts[0]=urldecode($parts[0]))))
{
// /$user/$app/...
$user = array_shift($parts);
@ -942,7 +951,7 @@ class groupdav extends HTTP_WebDAV_Server
list($id) = explode('.',$id); // remove evtl. .ics extension
}
$ok = $id && $user && in_array($app,array('addressbook','calendar','infolog','principals','groups'));
$ok = $id && $user && in_array($app,array('addressbook','calendar','infolog','principals'));
if ($this->debug)
{
error_log(__METHOD__."('$path') returning " . ($ok ? 'true' : 'false') . ": id='$id', app='$app', user='$user', user_prefix='$user_prefix'");

View File

@ -235,7 +235,7 @@ abstract class groupdav_handler
*/
function _common_get_put_delete($method,&$options,$id,&$return_no_access=false)
{
if (!in_array($this->app,array('principals','groups')) && !$GLOBALS['egw_info']['user']['apps'][$this->app])
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'");
return '403 Forbidden'; // no app rights

View File

@ -0,0 +1,97 @@
<?php
/**
* EGroupware: GroupDAV hooks: eg. preferences
*
* @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) 2010 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
/**
* GroupDAV hooks: eg. preferences
*/
class groupdav_hooks
{
/**
* Show GroupDAV preferences link in preferences
*
* @param string|array $args
*/
public static function menus($args)
{
$appname = 'groupdav';
$location = is_array($args) ? $args['location'] : $args;
if ($location == 'preferences')
{
$file = array(
'Preferences' => egw::link('/index.php','menuaction=preferences.uisettings.index&appname='.$appname),
);
if ($location == 'preferences')
{
display_section($appname,$file);
}
else
{
display_sidebox($appname,lang('Preferences'),$file);
}
}
}
/**
* populates $settings for the preferences
*
* @param array|string $hook_data
* @return array
*/
static function settings($hook_data)
{
$settings = array();
if ($hook_data['setup'])
{
$addressbooks = array();
}
else
{
$user = $GLOBALS['egw_info']['user']['account_id'];
$addressbook_bo = new addressbook_bo();
$addressbooks = $addressbook_bo->get_addressbooks(EGW_ACL_READ);
unset($addressbooks[$user]); // Use P for personal addressbook
unset($addressbooks[$user.'p']);// ignore (optional) private addressbook for now
}
$addressbooks = array(
'P' => lang('Personal'),
'G' => lang('Primary Group'),
//'U' => lang('Accounts'), // not yet working
'O' => lang('All in one'),
'A' => lang('All'),
) + $addressbooks;
// rewriting owner=0 to 'U', as 0 get's always selected by prefs
if (!isset($addressbooks[0]))
{
unset($addressbooks['U']);
}
else
{
unset($addressbooks[0]);
}
$settings['add_default'] = array(
'type' => 'multiselect',
'label' => 'Addressbooks to sync with Apple clients',
'name' => 'addressbook-home-set',
'help' => 'Addressbooks for CardDAV attribute "addressbook-home-set".',
'values' => $addressbooks,
'xmlrpc' => True,
'admin' => False,
'default' => 'P',
);
return $settings;
}
}

View File

@ -13,6 +13,8 @@
/**
* EGroupware: GroupDAV access: groupdav/caldav/carddav principals handlers
*
* @todo All principal urls should either contain no account_lid (eg. base64 of it) or use urlencode($account_lid)
*/
class groupdav_principals extends groupdav_handler
{
@ -251,20 +253,41 @@ class groupdav_principals extends groupdav_handler
$this->base_uri.'/principals/groups/'.$group);
}
}
$addressbooks = array();
$calendars = array();
$addressbooks = $calendars = array();
if ($account['account_id'] == $GLOBALS['egw_info']['user']['account_id'])
{
$addr_bo = new addressbook_bo();
foreach ($addr_bo->get_addressbooks() as $id => $label)
$prefs = $GLOBALS['egw_info']['user']['preferences']['groupdav'];
$addressbook_home_set = $prefs['addressbook-home-set'];
if (empty($addressbook_home_set)) $addressbook_home_set = 'P'; // personal addressbook
$addressbook_home_set = explode(',',$addressbook_home_set);
// replace symbolic id's with real nummeric id's
foreach(array(
'P' => $GLOBALS['egw_info']['user']['account_id'],
'G' => $GLOBALS['egw_info']['user']['account_primary_group'],
'U' => '0',
) as $sym => $id)
{
if ($id && is_numeric($id))
if (($key = array_search($sym, $addressbook_home_set)) !== false)
{
$owner = $GLOBALS['egw']->accounts->id2name($id);
$addressbooks[] = HTTP_WebDAV_Server::mkprop('href',
$this->base_uri.'/'.$owner.'/');
$addressbook_home_set[$key] = $id;
}
}
if (in_array('O',$addressbook_home_set)) // "all in one" from groupdav.php/addressbook/
{
$addressbooks[] = HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/');
}
foreach(ExecMethod('addressbook.addressbook_bo.get_addressbooks',EGW_ACL_READ) as $id => $label)
{
if ((in_array('A',$addressbook_home_set) || in_array((string)$id,$addressbook_home_set)) &&
is_numeric($id) && ($owner = $GLOBALS['egw']->accounts->id2name($id)))
{
$addressbooks[] = HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.urlencode($owner).'/');
}
}
$calendars[] = HTTP_WebDAV_Server::mkprop('href',
$this->base_uri.'/'.$account['account_lid'].'/');
/* iCal send propfind to wrong url (concatinated href's), if we return multiple href in calendar-home-set
$cal_bo = new calendar_bo();
foreach ($cal_bo->list_cals() as $label => $entry)
{
@ -273,11 +296,12 @@ class groupdav_principals extends groupdav_handler
$calendars[] = HTTP_WebDAV_Server::mkprop('href',
$this->base_uri.'/'.$owner.'/');
}
*/
}
else
{
$addressbooks[] = HTTP_WebDAV_Server::mkprop('href',
$this->base_uri.'/'.$account['account_lid'].'/');
$this->base_uri.'/'.$account['account_lid'].'/');
$calendars[] = HTTP_WebDAV_Server::mkprop('href',
$this->base_uri.'/'.$account['account_lid'].'/');
}
@ -507,6 +531,9 @@ class groupdav_principals extends groupdav_handler
{
$account = $this->read($account);
}
return 'EGw-'.$account['account_id'].':'.md5(serialize($account)).'-wGE';
return 'EGw-'.$account['account_id'].':'.md5(serialize($account)).
// as the pricipal of current user is influenced by GroupDAV prefs, we have to include them in the etag
($account['account_id'] == $GLOBALS['egw_info']['user']['account_id'] ?
':'.md5(serialize($GLOBALS['egw_info']['user']['preferences']['groupdav'])) : '').'-wGE';
}
}

View File

@ -268,10 +268,12 @@ class hooks
foreach($GLOBALS['egw_info']['apps'] as $appname => $app)
{
$f = EGW_SERVER_ROOT . $SEP . $appname . $SEP . 'setup' . $SEP . 'setup.inc.php';
if(@file_exists($f))
$setup_info = array($appname => array());
if(@file_exists($f)) include($f);
// some apps have setup_info for more then themselfs (eg. phpgwapi for groupdav)
foreach($setup_info as $appname => $data)
{
include($f);
$this->register_hooks($appname,$setup_info[$appname]['hooks']);
if ($data['hooks']) $this->register_hooks($appname,$data['hooks']);
}
}
}

View File

@ -40,6 +40,8 @@ add category common de Kategorie hinzufügen
add shortcut common de Abkürzung hinzufügen
add sub common de Untergeordnete hinzufügen
addressbook common de Adressbuch
addressbooks for carddav attribute "addressbook-home-set". common de Adressbücher für CardDAV Attribut "addressbook-home-set".
addressbooks to sync with apple clients common de Adressbücher zum Synchronisieren mit Apple Programmen
admin common de Admin
administration common de Administration
afghanistan common de AFGHANISTAN
@ -291,6 +293,7 @@ group has been deleted common de Gruppe wurde gelöscht
group has been updated common de Gruppe wurde aktualisiert
group name common de Gruppenname
group public common de Group Public
groupdav common de CalDAV, CardDAV und GroupDAV Server
groups common de Gruppen
groups with permission for %1 common de Gruppen mit Berechtigung für %1
groups without permission for %1 common de Gruppen ohne Berechtigung für %1

View File

@ -40,6 +40,8 @@ add category common en Add category
add shortcut common en Add Shortcut
add sub common en Add sub
addressbook common en Addressbook
addressbooks for carddav attribute "addressbook-home-set". common en Addressbooks for CardDAV attribute "addressbook-home-set".
addressbooks to sync with apple clients common en Addressbooks to sync with Apple clients
admin common en Admin
administration common en Administration
afghanistan common en AFGHANISTAN
@ -291,6 +293,7 @@ group has been deleted common en Group has been deleted
group has been updated common en Group has been updated
group name common en group name
group public common en Group Public
groupdav common en CalDAV, CardDAV and GroupDAV server
groups common en Groups
groups with permission for %1 common en Groups with permission for %1
groups without permission for %1 common en Groups without permission for %1

View File

@ -75,5 +75,5 @@ $setup_info['groupdav']['author'] = $setup_info['groupdav']['maintainer'] = arra
'email' => 'RalfBecker@outdoor-training.de'
);
$setup_info['groupdav']['license'] = 'GPL';
$setup_info['groupdav']['hooks']['preferences'] = 'groupdav_hooks::menus';
$setup_info['groupdav']['hooks']['settings'] = 'groupdav_hooks::settings';