pricipals and groups collection for WebDAV Acl, to improve support for Apple iCal, you can now use http://domain.com/egroupware/groupdav.php/pricipals/username as account-url in iCal

This commit is contained in:
Ralf Becker 2008-05-10 20:15:02 +00:00
parent ce59f97204
commit 0afb2d0324
4 changed files with 386 additions and 14 deletions

View File

@ -137,14 +137,31 @@ class groupdav extends HTTP_WebDAV_Server
if (!$app) // root folder containing apps
{
// self url
$files['files'][] = array(
'path' => '/',
'props' => array(
self::mkprop('displayname','eGroupWare'),
self::mkprop('resourcetype','collection'),
// adding the calendar extra property (calendar-home-set, etc.) here, allows apple iCal to "autodetect" the URL
'path' => '/',
'props' => array(
self::mkprop('displayname','eGroupWare'),
self::mkprop('resourcetype','collection'),
// adding the calendar extra property (calendar-home-set, etc.) here, allows apple iCal to "autodetect" the URL
self::mkprop(groupdav::CALDAV,'calendar-home-set',$_SERVER['SCRIPT_NAME'].'/calendar/'),
),
);
// principals collection
$files['files'][] = array(
'path' => '/principals/',
'props' => array(
self::mkprop('displayname',lang('Accounts')),
self::mkprop('resourcetype','collection'),
),
);
// groups collection
$files['files'][] = array(
'path' => '/groups/',
'props' => array(
self::mkprop('displayname',lang('Groups')),
self::mkprop('resourcetype','collection'),
),
);
foreach($this->root as $app => $data)
@ -155,7 +172,7 @@ class groupdav extends HTTP_WebDAV_Server
$files['files'][] = array(
'path' => '/'.$app.'/',
'props' => call_user_func('groupdav_'.$app.'::extra_properties',array(
'props' => call_user_func($app.'_groupdav::extra_properties',array(
self::mkprop('displayname',$this->translation->convert(lang($app),$this->egw_charset,'utf-8')),
self::mkprop('resourcetype',$this->_resourcetype($app)),
)),
@ -163,9 +180,9 @@ class groupdav extends HTTP_WebDAV_Server
}
return true;
}
if (!$GLOBALS['egw_info']['user']['apps'][$app])
if (!in_array($app,array('principals','groups')) && !$GLOBALS['egw_info']['user']['apps'][$app])
{
error_log(__CLASS__."::$method(path=$options[path]) 403 Forbidden: no app rights");
error_log(__CLASS__."::$method(path=$options[path]) 403 Forbidden: no app rights for '$app'");
return '403 Forbidden'; // no rights for the given app
}
if (($handler = groupdav_handler::app_handler($app,$this->debug)))
@ -198,7 +215,7 @@ class groupdav extends HTTP_WebDAV_Server
$resourcetype = array(
self::mkprop('collection','collection'),
);
if (!$no_extra_types)
if (!$no_extra_types && isset($this->root[$app]))
{
foreach($this->root[$app] as $ns => $type)
{
@ -426,6 +443,13 @@ class groupdav extends HTTP_WebDAV_Server
{
$parts = explode('/',$path);
if (in_array($parts[1],array('principals','groups')))
{
$user = $GLOBALS['egw_info']['user']['account_id'];
list(,$app,$id) = $parts;
return true;
}
list($id) = explode('.',array_pop($parts)); // remove evtl. .ics extension
$app = array_pop($parts);
@ -438,7 +462,7 @@ class groupdav extends HTTP_WebDAV_Server
{
$user = $GLOBALS['egw_info']['user']['account_id'];
}
if (!($ok = $id && in_array($app,array('addressbook','calendar','infolog')) && $user))
if (!($ok = $id && in_array($app,array('addressbook','calendar','infolog','principals','groups')) && $user))
{
error_log(__METHOD__."('$path') returning false: id=$id, app='$app', user=$user");
}

View File

@ -0,0 +1,168 @@
<?php
/**
* eGroupWare: GroupDAV access: groupdav/caldav/carddav groups 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) 2008 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
/**
* eGroupWare: GroupDAV access: groupdav/caldav/carddav groups handlers
*/
class groupdav_groups extends groupdav_handler
{
/**
* Reference to the accounts class
*
* @var accounts
*/
var $accounts;
/**
* Constructor
*
* @param string $app
* @param int $debug=null
*/
function __construct($app,$debug=null)
{
parent::__construct($app,$debug);
$this->accounts = $GLOBALS['egw']->accounts;
}
/**
* Handle propfind request for an application folder
*
* @param string $path
* @param array $options
* @param array &$files
* @param int $user account_id
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
*/
function propfind($path,$options,&$files,$user)
{
list(,,$user) = explode('/',$path);
foreach($user ? array($this->accounts->read($user)) : $this->accounts->search(array('type' => 'groups')) as $account)
{
$props = array(
HTTP_WebDAV_Server::mkprop('displayname',lang('Group').' '.$account['account_lid']),
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)),
HTTP_WebDAV_Server::mkprop('resourcetype','principal'),
HTTP_WebDAV_Server::mkprop('alternate-URI-set',''),
HTTP_WebDAV_Server::mkprop('principal-URL',$_SERVER['SCRIPT_NAME'].'/groups/'.$account['account_lid']),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$_SERVER['SCRIPT_NAME'].'/calendar/'),
);
foreach($this->accounts->members($account['account_id']) as $uid => $user)
{
$props[] = HTTP_WebDAV_Server::mkprop('group-membership',$_SERVER['SCRIPT_NAME'].'/principals/'.$user);
}
$files['files'][] = array(
'path' => '/groups/'.$account['account_lid'],
'props' => $props,
);
}
//error_log(__METHOD__."($path,,,$user) files=".array2string($files['files']));
return true;
}
/**
* Handle get request for an applications entry
*
* @param array &$options
* @param int $id
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
*/
function get(&$options,$id)
{
if (!is_array($account = $this->_common_get_put_delete('GET',$options,$id)))
{
return $account;
}
$options['data'] = 'Principal: '.$account['account_lid'].
"\nURL: ".$_SERVER['SCRIPT_NAME'].$options['path'].
"\nName: ".lang('Group').' '.$account['account_lid'].
($account['account_email'] ? "\nEmail: ".$account['account_email'] : '').
"\nMembers: ".implode(', ',$this->accounts->members($id))."\n";
$options['mimetype'] = 'text/plain; charset=utf-8';
header('Content-Encoding: identity');
header('ETag: '.$this->get_etag($account));
return true;
}
/**
* Handle get request for an applications entry
*
* @param array &$options
* @param int $id
* @param int $user=null account_id of owner, default null
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
*/
function put(&$options,$id,$user=null)
{
return false;
}
/**
* Handle get request for an applications entry
*
* @param array &$options
* @param int $id
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
*/
function delete(&$options,$id)
{
return false;
}
/**
* Read an entry
*
* @param string/int $id
* @return array/boolean array with entry, false if no read rights, null if $id does not exist
*/
function read($id)
{
return $this->accounts->read($id);
}
/**
* Check if user has the neccessary rights on an entry
*
* @param int $acl EGW_ACL_READ, EGW_ACL_EDIT or EGW_ACL_DELETE
* @param array/int $entry entry-array or id
* @return boolean null if entry does not exist, false if no access, true if access permitted
*/
function check_access($acl,$entry)
{
if ($acl != EGW_ACL_READ)
{
return false;
}
if (!is_array($entry) && !$this->accounts->name2id($entry,'account_lid','g'))
{
return null;
}
return true;
}
/**
* Get the etag for an entry, can be reimplemented for other algorithm or field names
*
* @param array/int $event array with event or cal_id
* @return string/boolean string with etag or false
*/
function get_etag($account)
{
if (!is_array($account))
{
$account = $this->read($account);
}
return '"'.$account['account_id'].':'.md5(serialize($account)).'"';
}
}

View File

@ -60,6 +60,12 @@ abstract class groupdav_handler
*/
var $http_if_match;
/**
* Constructor
*
* @param string $app
* @param int $debug=null
*/
function __construct($app,$debug=null)
{
$this->app = $app;
@ -181,10 +187,10 @@ abstract class groupdav_handler
*/
function _common_get_put_delete($method,&$options,$id)
{
if (!$GLOBALS['egw_info']['user']['apps'][$this->app])
if (!in_array($this->app,array('principals','groups')) && !$GLOBALS['egw_info']['user']['apps'][$this->app])
{
if ($this->debug) error_log(__METHOD__."($method,,$id) 403 Forbidden: no app rights");
return '403 Forbidden'; // no calendar rights
if ($this->debug) error_log(__METHOD__."($method,,$id) 403 Forbidden: no app rights for '$this->app'");
return '403 Forbidden'; // no app rights
}
$extra_acl = $this->method2acl[$method];
if (!($entry = $this->read($id)) && ($method != 'PUT' || $event === false) ||
@ -228,7 +234,7 @@ abstract class groupdav_handler
if (!array_key_exists($app,$handler_cache))
{
$class = $app.'_groupdav';
if (!class_exists($class)) return null;
if (!class_exists($class) && !class_exists($class = 'groupdav_'.$app)) return null;
$handler_cache[$app] = new $class($app);
}

View File

@ -0,0 +1,174 @@
<?php
/**
* eGroupWare: GroupDAV access: groupdav/caldav/carddav principals 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) 2008 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
/**
* eGroupWare: GroupDAV access: groupdav/caldav/carddav principals handlers
*/
class groupdav_principals extends groupdav_handler
{
/**
* Reference to the accounts class
*
* @var accounts
*/
var $accounts;
/**
* Constructor
*
* @param string $app
* @param int $debug=null
*/
function __construct($app,$debug=null)
{
parent::__construct($app,$debug);
$this->accounts = $GLOBALS['egw']->accounts;
}
/**
* Handle propfind request for an application folder
*
* @param string $path
* @param array $options
* @param array &$files
* @param int $user account_id
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
*/
function propfind($path,$options,&$files,$user)
{
list(,,$id) = explode('/',$path);
if ($id && !($id = $this->accounts->id2name($id)))
{
return false;
}
foreach($id ? array($this->accounts->read($id)) : $this->accounts->search(array('type' => 'accounts')) as $account)
{
$props = array(
HTTP_WebDAV_Server::mkprop('displayname',trim($account['account_firstname'].' '.$account['account_lastname'])),
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)),
HTTP_WebDAV_Server::mkprop('resourcetype','principal'),
HTTP_WebDAV_Server::mkprop('alternate-URI-set',''),
HTTP_WebDAV_Server::mkprop('principal-URL',$_SERVER['SCRIPT_NAME'].'/principals/'.$account['account_lid']),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$_SERVER['SCRIPT_NAME'].'/'),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$account['account_email']),
);
foreach($this->accounts->memberships($account['account_id']) as $gid => $group)
{
$props[] = HTTP_WebDAV_Server::mkprop('group-membership',$_SERVER['SCRIPT_NAME'].'/groups/'.$group);
}
$files['files'][] = array(
'path' => '/principals/'.$account['account_lid'],
'props' => $props,
);
error_log(__METHOD__."($path) path=/principals/".$account['account_lid'].', props='.array2string($props));
}
//error_log(__METHOD__."($path,,,$user) files=".array2string($files['files']));
return true;
}
/**
* Handle get request for an applications entry
*
* @param array &$options
* @param int $id
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
*/
function get(&$options,$id)
{
if (!is_array($account = $this->_common_get_put_delete('GET',$options,$id)))
{
return $account;
}
$options['data'] = 'Principal: '.$account['account_lid'].
"\nURL: ".$_SERVER['SCRIPT_NAME'].$options['path'].
"\nName: ".$account['account_firstname'].' '.$account['account_lastname'].
"\nEmail: ".$account['account_email'].
"\nMemberships: ".implode(', ',$this->accounts->memberships($id))."\n";
$options['mimetype'] = 'text/plain; charset=utf-8';
header('Content-Encoding: identity');
header('ETag: '.$this->get_etag($account));
return true;
}
/**
* Handle get request for an applications entry
*
* @param array &$options
* @param int $id
* @param int $user=null account_id of owner, default null
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
*/
function put(&$options,$id,$user=null)
{
return false;
}
/**
* Handle get request for an applications entry
*
* @param array &$options
* @param int $id
* @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found')
*/
function delete(&$options,$id)
{
return false;
}
/**
* Read an entry
*
* @param string/int $id
* @return array/boolean array with entry, false if no read rights, null if $id does not exist
*/
function read($id)
{
return $this->accounts->read($id);
}
/**
* Check if user has the neccessary rights on an entry
*
* @param int $acl EGW_ACL_READ, EGW_ACL_EDIT or EGW_ACL_DELETE
* @param array/int $entry entry-array or id
* @return boolean null if entry does not exist, false if no access, true if access permitted
*/
function check_access($acl,$entry)
{
if ($acl != EGW_ACL_READ)
{
return false;
}
if (!is_array($entry) && !$this->accounts->name2id($entry,'account_lid','u'))
{
return null;
}
return true;
}
/**
* Get the etag for an entry, can be reimplemented for other algorithm or field names
*
* @param array/int $event array with event or cal_id
* @return string/boolean string with etag or false
*/
function get_etag($account)
{
if (!is_array($account))
{
$account = $this->read($account);
}
return '"'.$account['account_id'].':'.md5(serialize($account)).'"';
}
}