GroupDAV updates

This commit is contained in:
Jörg Lehrke 2010-03-06 23:06:43 +00:00
parent 79824c2e0c
commit a8804a44ef
16 changed files with 1462 additions and 422 deletions

View File

@ -824,8 +824,8 @@ class addressbook_bo extends addressbook_so
/** /**
* reads contacts matched by key and puts all cols in the data array * reads contacts matched by key and puts all cols in the data array
* *
* @param int/string $contact_id * @param int|string $contact_id
* @return array/boolean array with contact data, null if not found or false on no view perms * @return array|boolean array with contact data, null if not found or false on no view perms
*/ */
function read($contact_id) function read($contact_id)
{ {

View File

@ -51,10 +51,11 @@ class addressbook_groupdav extends groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog' * @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set * @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler * @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
*/ */
function __construct($app,$debug=null,$base_uri=null) function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
{ {
parent::__construct($app,$debug,$base_uri); parent::__construct($app,$debug,$base_uri,$principalURL);
$this->bo = new addressbook_bo(); $this->bo = new addressbook_bo();
} }
@ -67,7 +68,7 @@ class addressbook_groupdav extends groupdav_handler
*/ */
static function get_path($contact) static function get_path($contact)
{ {
return '/addressbook/'.$contact[self::PATH_ATTRIBUTE].'.vcf'; return $contact[self::PATH_ATTRIBUTE].'.vcf';
} }
/** /**
@ -95,8 +96,9 @@ 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 contact data or just the etag's
if (!($filter['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)
{ {
@ -108,7 +110,7 @@ class addressbook_groupdav extends groupdav_handler
} }
} }
// return iterator, calling ourself to return result in chunks // return iterator, calling ourself to return result in chunks
$files['files'] = new groupdav_propfind_iterator($this,$filter,$files['files']); $files['files'] = new groupdav_propfind_iterator($this,$path,$filter,$files['files']);
return true; return true;
} }
@ -116,11 +118,12 @@ class addressbook_groupdav extends groupdav_handler
/** /**
* Callback for profind interator * Callback for profind interator
* *
* @param string $path
* @param array $filter * @param array $filter
* @param array|boolean $start=false false=return all or array(start,num) * @param array|boolean $start=false false=return all or array(start,num)
* @return array with "files" array with values for keys path and props * @return array with "files" array with values for keys path and props
*/ */
function &propfind_callback(array $filter,$start=false) function &propfind_callback($path,array $filter,$start=false)
{ {
$starttime = microtime(true); $starttime = microtime(true);
@ -129,7 +132,6 @@ class addressbook_groupdav extends groupdav_handler
$handler = self::_get_handler(); $handler = self::_get_handler();
} }
unset($filter['address_data']); unset($filter['address_data']);
$files = array(); $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',$start,$filter))) if (($contacts =& $this->bo->search(array(),$address_data ? false : array('id','uid','etag','modified'),'contact_id','','',False,'AND',$start,$filter)))
@ -138,27 +140,27 @@ class addressbook_groupdav extends groupdav_handler
{ {
$props = array( $props = array(
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($contact)), HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($contact)),
HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/x-vcard'), HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/vcard'),
// getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set // getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set
HTTP_WebDAV_Server::mkprop('getlastmodified', $contact['modified']), HTTP_WebDAV_Server::mkprop('getlastmodified', $contact['modified']),
); );
if ($address_data) if ($address_data)
{ {
$content = $handler->getVCard($contact,$this->charset,false); $content = $handler->getVCard($contact['id'],$this->charset,false);
$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,true);
} }
else else
{ {
$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[] = array( $files[] = array(
'path' => self::get_path($contact), 'path' => $path.self::get_path($contact),
'props' => $props, 'props' => $props,
); );
} }
} }
if ($this->debug) error_log(__METHOD__.'('.array2string($filter).','.array2string($start).") took ".(microtime(true) - $starttime).' to return '.count($files).' items'); if ($this->debug) error_log(__METHOD__."($path,".array2string($filter).','.array2string($start).") took ".(microtime(true) - $starttime).' to return '.count($files).' items');
return $files; return $files;
} }
@ -265,22 +267,70 @@ class addressbook_groupdav extends groupdav_handler
*/ */
function put(&$options,$id,$user=null) function put(&$options,$id,$user=null)
{ {
$ok = $this->_common_get_put_delete('PUT',$options,$id); if ($this->debug) error_log(__METHOD__.'('.array2string($options).",$id,$user)");
if (!is_null($ok) && !is_array($ok))
{
return $ok;
}
$handler = self::_get_handler();
$contact = $handler->vcardtoegw($options['content']);
if (!is_null($ok)) $oldContact = $this->_common_get_put_delete('PUT',$options,$id);
if (!is_null($oldContact) && !is_array($oldContact))
{ {
$contact['id'] = $ok['id']; return $oldContact;
// dont allow the client to overwrite certain values
$contact['uid'] = $ok['uid'];
$contact['owner'] = $ok['owner'];
$contact['private'] = $ok['private'];
} }
$handler = self::_get_handler();
$vCard = htmlspecialchars_decode($options['content']);
if (is_array($oldContact))
{
$contactId = $oldContact['id'];
$retval = true;
}
else
{
// new entry?
if (($foundContacts = $handler->search($vCard)))
{
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';
}
}
$contact = $handler->vcardtoegw($vCard);
if (is_array($contact['category']))
{
$contact['category'] = implode(',',$this->bo->find_or_add_categories($contact['category'], $contactId));
}
elseif ($contactId > 0)
{
$contact['category'] = $oldContact['category'];
}
if (is_array($oldContact))
{
$contact['id'] = $oldContact['id'];
// dont allow the client to overwrite certain values
$contact['uid'] = $oldContact['uid'];
$contact['owner'] = $oldContact['owner'];
$contact['private'] = $oldContact['private'];
}
else
{
$contact['owner'] = $user;
}
if ($this->http_if_match) $contact['etag'] = self::etag2value($this->http_if_match); if ($this->http_if_match) $contact['etag'] = self::etag2value($this->http_if_match);
if (!($save_ok = $this->bo->save($contact))) if (!($save_ok = $this->bo->save($contact)))
@ -292,24 +342,26 @@ class addressbook_groupdav extends groupdav_handler
} }
return false; return false;
} }
if (!isset($contact['etag'])) if (!isset($contact['etag']))
{ {
$contact = $this->read($contact['id']); $contact = $this->read($save_ok);
} }
header('ETag: '.$this->get_etag($contact)); header('ETag: '.$this->get_etag($contact));
if (is_null($ok)) if ($retval !== true)
{ {
header($h='Location: '.$this->base_uri.self::get_path($contact)); $path = preg_replace('|(.*)/[^/]*|', '\1/', $options['path']);
if ($this->debug) error_log(__METHOD__."($method,,$id) header('$h'): 201 Created"); header($h='Location: '.$this->base_uri.$path.self::get_path($contact));
return '201 Created'; if ($this->debug) error_log(__METHOD__."($method,,$id) header('$h'): $retval");
return $retval;
} }
return true; return true;
} }
/** /**
* Query ctag for addressbook * Query ctag for addressbook
* *
* @return string * @return string
*/ */
public function getctag($path,$user) public function getctag($path,$user)
@ -319,10 +371,40 @@ class addressbook_groupdav extends groupdav_handler
if ($user && $path != '/addressbook/') $filter['contact_owner'] = $user; if ($user && $path != '/addressbook/') $filter['contact_owner'] = $user;
// should we hide the accounts addressbook // should we hide the accounts addressbook
if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) $filter['account_id'] = null; if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) $filter['account_id'] = null;
$result = $this->bo->search(array(),'MAX(contact_modified) AS contact_modified','','','','','',$filter); $result = $this->bo->search(array(),'MAX(contact_modified) AS contact_modified','','','','','',$filter);
return '"'.$result[0]['modified'].'"'; $ctag = 'EGw-'.$result[0]['modified'].'-wGE';
return $ctag;
}
/**
* Add the privileges of the current user
*
* @param array $props=array() regular props by the groupdav handler
* @return array
*/
static function current_user_privilege_set(array $props=array())
{
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::DAV,'current-user-privilege-set',
array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'privilege',
array(//HTTP_WebDAV_Server::mkprop(groupdav::DAV,'all',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read',''),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'read-free-busy',''),
//HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read-current-user-privilege-set',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'bind',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'unbind',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-properties',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-content',''),
))));
return $props;
} }
/** /**
@ -342,12 +424,19 @@ class addressbook_groupdav extends groupdav_handler
* </supported-report> * </supported-report>
* </D:supported-report-set> * </D:supported-report-set>
* @link http://www.mail-archive.com/calendarserver-users@lists.macosforge.org/msg01156.html * @link http://www.mail-archive.com/calendarserver-users@lists.macosforge.org/msg01156.html
* *
* @param array $props=array() regular props by the groupdav handler * @param array $props=array() regular props by the groupdav handler
* @param string $displayname
* @param string $base_uri=null base url of handler
* @return array * @return array
*/ */
static function extra_properties(array $props=array()) static function extra_properties(array $props=array(), $displayname, $base_uri=null)
{ {
// addressbook description
$displayname = $GLOBALS['egw']->translation->convert(lang('Addressbook of') . ' ' .
$displayname,
$GLOBALS['egw']->translation->charset(),'utf-8');
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-description',$displayname);
// supported reports (required property for CardDAV) // supported reports (required property for CardDAV)
$props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array( $props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
HTTP_WebDAV_Server::mkprop('supported-report',array( HTTP_WebDAV_Server::mkprop('supported-report',array(
@ -357,6 +446,7 @@ class addressbook_groupdav extends groupdav_handler
HTTP_WebDAV_Server::mkprop('report', HTTP_WebDAV_Server::mkprop('report',
HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-multiget','')))), HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-multiget','')))),
)); ));
//$props = self::current_user_privilege_set($props);
return $props; return $props;
} }
@ -390,7 +480,7 @@ class addressbook_groupdav extends groupdav_handler
{ {
return '412 Precondition Failed'; return '412 Precondition Failed';
} }
return $ok; //return $ok;
} }
/** /**

View File

@ -339,7 +339,7 @@ class addressbook_so
/** /**
* Read all customfields of the given id's * Read all customfields of the given id's
* *
* @param int/array $ids * @param int|array $ids
* @param array $field_names=null custom fields to read, default all * @param array $field_names=null custom fields to read, default all
* @return array id => name => value * @return array id => name => value
*/ */
@ -374,7 +374,7 @@ class addressbook_so
/** /**
* Read all distributionlists of the given id's * Read all distributionlists of the given id's
* *
* @param int/array $ids * @param int|array $ids
* @return array id => name => value * @return array id => name => value
*/ */
function read_distributionlist($ids, $dl_allowed=array()) function read_distributionlist($ids, $dl_allowed=array())
@ -544,8 +544,8 @@ class addressbook_so
/** /**
* reads contact data including custom fields * reads contact data including custom fields
* *
* @param int/string $contact_id contact_id or 'a'.account_id * @param int|string $contact_id contact_id or 'a'.account_id
* @return array/boolean data if row could be retrived else False * @return array|boolean data if row could be retrived else False
*/ */
function read($contact_id) function read($contact_id)
{ {
@ -581,10 +581,10 @@ class addressbook_so
* *
* '*' and '?' are replaced with sql-wildcards '%' and '_' * '*' and '?' are replaced with sql-wildcards '%' and '_'
* *
* @param array/string $criteria array of key and data cols, OR string to search over all standard search fields * @param array|string $criteria array of key and data cols, OR string to search over all standard search fields
* @param boolean/string $only_keys=true True returns only keys, False returns all cols. comma seperated list of keys to return * @param boolean|string $only_keys=true True returns only keys, False returns all cols. comma seperated list of keys to return
* @param string $order_by='' fieldnames + {ASC|DESC} separated by colons ',', can also contain a GROUP BY (if it contains ORDER BY) * @param string $order_by='' fieldnames + {ASC|DESC} separated by colons ',', can also contain a GROUP BY (if it contains ORDER BY)
* @param string/array $extra_cols='' string or array of strings to be added to the SELECT, eg. "count(*) as num" * @param string|array $extra_cols='' string or array of strings to be added to the SELECT, eg. "count(*) as num"
* @param string $wildcard='' appended befor and after each criteria * @param string $wildcard='' appended befor and after each criteria
* @param boolean $empty=false False=empty criteria are ignored in query, True=empty have to be empty in row * @param boolean $empty=false False=empty criteria are ignored in query, True=empty have to be empty in row
* @param string $op='AND' defaults to 'AND', can be set to 'OR' too, then criteria's are OR'ed together * @param string $op='AND' defaults to 'AND', can be set to 'OR' too, then criteria's are OR'ed together
@ -985,7 +985,7 @@ class addressbook_so
/** /**
* Deletes a distribution list (incl. it's members) * Deletes a distribution list (incl. it's members)
* *
* @param int/array $list list_id(s) * @param int|array $list list_id(s)
* @return number of members deleted or false if list does not exist * @return number of members deleted or false if list does not exist
*/ */
function delete_list($list) function delete_list($list)
@ -1011,7 +1011,7 @@ class addressbook_so
/** /**
* Check if distribution lists are availible for a given addressbook * Check if distribution lists are availible for a given addressbook
* *
* @param int/string $owner='' addressbook (eg. 0 = accounts), default '' = "all" addressbook (uses the main backend) * @param int|string $owner='' addressbook (eg. 0 = accounts), default '' = "all" addressbook (uses the main backend)
* @return boolean * @return boolean
*/ */
function lists_available($owner='') function lists_available($owner='')

View File

@ -123,7 +123,7 @@ class addressbook_vcal extends addressbook_bo
*/ */
function addVCard($_vcard, $_abID=null, $merge=false) function addVCard($_vcard, $_abID=null, $merge=false)
{ {
if (!($contact = $this->vcardtoegw($_vcard, $_abID))) return false; if (!($contact = $this->vcardtoegw($_vcard))) return false;
if ($_abID) if ($_abID)
{ {
@ -145,6 +145,15 @@ class addressbook_vcal extends addressbook_bo
{ {
$contact['account_id'] = $old_contact['account_id']; $contact['account_id'] = $old_contact['account_id'];
} }
if (is_array($contact['category']))
{
$contact['category'] = implode(',',$this->find_or_add_categories($contact['category'], $_abID));
}
else
{
// restore from orignal
$contact['category'] = $old_contact['category'];
}
} }
} }
// update entry // update entry
@ -157,6 +166,10 @@ class addressbook_vcal extends addressbook_bo
{ {
$contact['owner'] = $GLOBALS['egw_info']['user']['account_primary_group']; $contact['owner'] = $GLOBALS['egw_info']['user']['account_primary_group'];
} }
if (is_array($contact['category']))
{
$contact['category'] = implode(',',$this->find_or_add_categories($contact['category'], -1));
}
} }
return $this->save($contact); return $this->save($contact);
} }
@ -452,8 +465,13 @@ class addressbook_vcal extends addressbook_bo
{ {
$result = array(); $result = array();
if (($contact = $this->vcardtoegw($_vcard, $contentID))) if (($contact = $this->vcardtoegw($_vcard)))
{ {
if (is_array($contact['category']))
{
$contact['category'] = implode(',',$this->find_or_add_categories($contact['category'],
$contentID ? $contentID : -1));
}
if ($contentID) if ($contentID)
{ {
$contact['id'] = $contentID; $contact['id'] = $contentID;
@ -472,7 +490,7 @@ class addressbook_vcal extends addressbook_bo
if (is_array($_supportedFields)) $this->supportedFields = $_supportedFields; if (is_array($_supportedFields)) $this->supportedFields = $_supportedFields;
} }
function vcardtoegw($_vcard, $_abID=null) function vcardtoegw($_vcard)
{ {
// the horde class does the charset conversion. DO NOT CONVERT HERE. // the horde class does the charset conversion. DO NOT CONVERT HERE.
// be as flexible as possible // be as flexible as possible
@ -886,7 +904,7 @@ class addressbook_vcal extends addressbook_bo
break; break;
case 'cat_id': case 'cat_id':
$contact[$fieldName] = implode(',',$this->find_or_add_categories($vcardValues[$vcardKey]['values'], $_abID)); $contact[$fieldName] = $vcardValues[$vcardKey]['values'];
break; break;
case 'jpegphoto': case 'jpegphoto':

View File

@ -145,9 +145,6 @@ class calendar_boupdate extends calendar_bo
else else
{ {
$old_event = $this->read((int)$event['id'],null,$ignore_acl); $old_event = $this->read((int)$event['id'],null,$ignore_acl);
// if no participants are set, set them from the old event, as we might need them to update recuring events
if (!isset($event['participants'])) $event['participants'] = $old_event['participants'];
//echo "old $event[id]="; _debug_array($old_event);
} }
// do we need to check, if user is allowed to invite the invited participants // do we need to check, if user is allowed to invite the invited participants
@ -185,7 +182,7 @@ class calendar_boupdate extends calendar_bo
self::has_cat_right(self::CAT_ACL_STATUS,$cat_id,$this->user) === false && self::has_cat_right(self::CAT_ACL_STATUS,$cat_id,$this->user) === false &&
(!$old_event || $old_event['start'] != $event['start'] || $old_event['end'] != $event['end'])) (!$old_event || $old_event['start'] != $event['start'] || $old_event['end'] != $event['end']))
{ {
foreach($event['participants'] as $uid => $status) foreach((array)$event['participants'] as $uid => $status)
{ {
calendar_so::split_status($status,$q,$r); calendar_so::split_status($status,$q,$r);
if ($status != 'U') if ($status != 'U')
@ -658,8 +655,6 @@ class calendar_boupdate extends calendar_bo
} }
} }
} }
//$currentPrefs = CreateObject('phpgwapi.preferences',$GLOBALS['egw_info']['user']['account_id']);
//$user_prefs = $currentPrefs->read_repository();
$user_prefs = $GLOBALS['egw_info']['user']['preferences']; $user_prefs = $GLOBALS['egw_info']['user']['preferences'];
foreach($to_notify as $userid => $statusid) foreach($to_notify as $userid => $statusid)
{ {
@ -676,8 +671,9 @@ class calendar_boupdate extends calendar_bo
continue; // dont notify rejected participants or groups continue; // dont notify rejected participants or groups
} }
if($userid != $GLOBALS['egw_info']['user']['account_id'] || if($userid != $GLOBALS['egw_info']['user']['account_id'] ||
($userid == $GLOBALS['egw_info']['user']['account_id'] && $user_prefs['calendar']['receive_own_updates']==1) || ($userid == $GLOBALS['egw_info']['user']['account_id'] &&
$user_prefs['calendar']['receive_own_updates']==1) ||
$msg_type == MSG_ALARM) $msg_type == MSG_ALARM)
{ {
$preferences = CreateObject('phpgwapi.preferences',$userid); $preferences = CreateObject('phpgwapi.preferences',$userid);
@ -1084,7 +1080,9 @@ class calendar_boupdate extends calendar_bo
"($cal_id, $uid, $status, $recur_date)"); "($cal_id, $uid, $status, $recur_date)");
} }
$old_event = $this->read($cal_id, $recur_date, false, 'server'); $old_event = $this->read($cal_id, $recur_date, false, 'server');
if (($Ok = $this->so->set_status($cal_id,is_numeric($uid)?'u':$uid[0],is_numeric($uid)?$uid:substr($uid,1),$status,$recur_date ? $this->date2ts($recur_date,true) : 0,$role))) if (($Ok = $this->so->set_status($cal_id,is_numeric($uid)?'u':$uid[0],
is_numeric($uid)?$uid:substr($uid,1),$status,
$recur_date?$this->date2ts($recur_date,true):0,$role)))
{ {
if ($updateTS) $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,'modify',time()); if ($updateTS) $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,'modify',time());
@ -1430,27 +1428,24 @@ class calendar_boupdate extends calendar_bo
} }
$cat_id_list = array(); $cat_id_list = array();
if(is_array($catname_list) && count($catname_list) > 0) foreach ((array)$catname_list as $cat_name)
{ {
foreach ($catname_list as $cat_name) $cat_name = trim($cat_name);
$cat_id = $this->categories->name2id($cat_name, 'X-');
if (!$cat_id)
{ {
$cat_name = trim($cat_name); // some SyncML clients (mostly phones) add an X- to the category names
$cat_id = $this->categories->name2id($cat_name, 'X-'); if (strncmp($cat_name, 'X-', 2) == 0)
if (!$cat_id)
{ {
// some SyncML clients (mostly phones) add an X- to the category names $cat_name = substr($cat_name, 2);
if (strncmp($cat_name, 'X-', 2) == 0)
{
$cat_name = substr($cat_name, 2);
}
$cat_id = $this->categories->add(array('name' => $cat_name, 'descr' => $cat_name, 'access' => 'private'));
} }
$cat_id = $this->categories->add(array('name' => $cat_name, 'descr' => $cat_name, 'access' => 'private'));
}
if ($cat_id) if ($cat_id)
{ {
$cat_id_list[] = $cat_id; $cat_id_list[] = $cat_id;
}
} }
} }
@ -1501,7 +1496,6 @@ class calendar_boupdate extends calendar_bo
{ {
$matchingEvents = array(); $matchingEvents = array();
$query = array(); $query = array();
$recur_date = 0;
if ($this->log) if ($this->log)
{ {
@ -1511,16 +1505,12 @@ class calendar_boupdate extends calendar_bo
if ($filter == 'master') if ($filter == 'master')
{ {
if (isset($event['recurrence'])) $query[] = 'recur_type!='. MCAL_RECUR_NONE;
{ $query['cal_recurrence'] = 0;
$recur_date = $this->date2usertime($event['recurrence']);
}
elseif (isset($event['start']))
{
$recur_date = $this->date2usertime($event['start']);
}
} }
if (!isset($event['recurrence'])) $event['recurrence'] = 0;
if ($event['id']) if ($event['id'])
{ {
if ($this->log) if ($this->log)
@ -1528,38 +1518,44 @@ class calendar_boupdate extends calendar_bo
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
'(' . $event['id'] . ")[EventID]"); '(' . $event['id'] . ")[EventID]");
} }
if (($egwEvent = $this->read($event['id'], $recur_date, false, 'server'))) if (($egwEvent = $this->read($event['id'], 0, false, 'server')))
{ {
if ($this->log) if ($this->log)
{ {
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
'()[FOUND]:' . array2string($egwEvent)); '()[FOUND]:' . array2string($egwEvent));
} }
// Just a simple consistency check if ($egwEvent['recur_type'] != MCAL_RECUR_NONE &&
if ($filter == 'exact' || (empty($event['uid']) || $event['uid'] == $egwEvent['uid']))
$filter == 'master' && $egwEvent['recur_type'] != MCAL_RECUR_NONE ||
$filter != 'master' && strpos($egwEvent['title'], $event['title']) === 0)
{ {
$retval = $egwEvent['id']; if ($filter == 'master')
if ($egwEvent['recur_type'] != MCAL_RECUR_NONE &&
$event['recur_type'] == MCAL_RECUR_NONE && $event['recurrence'] != 0)
{ {
$retval .= ':' . (int)$event['recurrence']; $matchingEvents[] = $egwEvent['id']; // we found the master
} }
$matchingEvents[] = $retval; if ($event['recur_type'] == $egwEvent['recur_type'])
return $matchingEvents; {
$matchingEvents[] = $egwEvent['id']; // we found the event
}
elseif ($event['recur_type'] == MCAL_RECUR_NONE &&
$event['recurrence'] != 0)
{
$exceptions = $this->so->get_recurrence_exceptions($egwEvent, $event['tzid']);
if (in_array($event['recurrence'], $exceptions))
{
$matchingEvents[] = $egwEvent['id'] . ':' . (int)$event['recurrence'];
}
}
} elseif ($event['recur_type'] == $egwEvent['recur_type'] &&
$filter != 'master' &&
strpos($egwEvent['title'], $event['title']) === 0)
{
$matchingEvents[] = $egwEvent['id']; // we found the event
} }
} }
if ($filter == 'exact') return array(); if (!empty($matchingEvents) || $filter == 'exact') return $matchingEvents;
} }
unset($event['id']); unset($event['id']);
if ($filter == 'master')
{
$query[] = 'recur_type!='. MCAL_RECUR_NONE;
$query['cal_recurrence'] = 0;
}
// only query calendars of users, we have READ-grants from // only query calendars of users, we have READ-grants from
$users = array(); $users = array();
foreach(array_keys($this->grants) as $user) foreach(array_keys($this->grants) as $user)
@ -1611,6 +1607,7 @@ class calendar_boupdate extends calendar_bo
} }
} }
} }
if ($filter != 'master' && ($filter != 'exact' || empty($event['uid']))) if ($filter != 'master' && ($filter != 'exact' || empty($event['uid'])))
{ {
if (isset($event['whole_day']) && $event['whole_day']) if (isset($event['whole_day']) && $event['whole_day'])
@ -1661,10 +1658,6 @@ class calendar_boupdate extends calendar_bo
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
'(' . $event['uid'] . ')[EventUID]'); '(' . $event['uid'] . ')[EventUID]');
} }
if ($filter != 'master' && isset($event['recurrence']))
{
$query['cal_recurrence'] = $event['recurrence'];
}
} }
if ($this->log) if ($this->log)
@ -1694,19 +1687,41 @@ class calendar_boupdate extends calendar_bo
} }
if (in_array($egwEvent['id'], $matchingEvents)) continue; if (in_array($egwEvent['id'], $matchingEvents)) continue;
if (in_array($filter, array('exact', 'master')) && !empty($event['uid']))
{
$matchingEvents[] = $egwEvent['id']; // UID found
if ($filter = 'master') break;
continue;
}
// convert timezone id of event to tzid (iCal id like 'Europe/Berlin') // convert timezone id of event to tzid (iCal id like 'Europe/Berlin')
if (!$egwEvent['tz_id'] || !($egwEvent['tzid'] = calendar_timezones::id2tz($egwEvent['tz_id']))) if (!$egwEvent['tz_id'] || !($egwEvent['tzid'] = calendar_timezones::id2tz($egwEvent['tz_id'])))
{ {
$egwEvent['tzid'] = egw_time::$server_timezone->getName(); $egwEvent['tzid'] = egw_time::$server_timezone->getName();
} }
if (in_array($filter, array('exact', 'master')) && !empty($event['uid']))
{
// UID found
if ($egwEvent['recurrence'] == $event['recurrence'])
{
// We found the exact match
$matchingEvents[] = $egwEvent['id'];
break;
}
if (!$egwEvent['recurrence'] && $event['recurrence'])
{
// We found the master
if ($filter == 'master')
{
$matchingEvents[] = $egwEvent['id'];
break;
}
$exceptions = $this->so->get_recurrence_exceptions($egwEvent, $event['tzid']);
if (in_array($event['recurrence'], $exceptions))
{
// We found a pseudo exception
$matchingEvents[] = $egwEvent['id'] . ':' . (int)$event['recurrence'];
break;
}
}
continue;
}
if (!empty($event['uid']) && $filter == 'exact') break;
// check times // check times
if ($filter != 'relax') if ($filter != 'relax')
@ -2129,4 +2144,4 @@ class calendar_boupdate extends calendar_bo
} }
} }
} }
} }

View File

@ -58,10 +58,11 @@ class calendar_groupdav extends groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog' * @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set * @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler * @param string $base_uri=null base url of handler
* @param string $principalURL=null principal url of handler
*/ */
function __construct($app,$debug=null, $base_uri=null) function __construct($app,$debug=null, $base_uri=null, $principalURL=null)
{ {
parent::__construct($app,$debug,$base_uri); parent::__construct($app,$debug,$base_uri,$principalURL);
$this->bo = new calendar_boupdate(); $this->bo = new calendar_boupdate();
} }
@ -83,7 +84,7 @@ class calendar_groupdav extends groupdav_handler
if (!is_array($event)) $event = $this->bo->read($event); if (!is_array($event)) $event = $this->bo->read($event);
$name = $event[self::PATH_ATTRIBUTE]; $name = $event[self::PATH_ATTRIBUTE];
} }
return '/calendar/'.$name.'.ics'; return $name.'.ics';
} }
/** /**
@ -171,7 +172,7 @@ class calendar_groupdav extends groupdav_handler
'text/calendar; charset=utf-8; component=VEVENT' : 'text/calendar'), 'text/calendar; charset=utf-8; component=VEVENT' : 'text/calendar'),
// getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set // getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set
HTTP_WebDAV_Server::mkprop('getlastmodified', $event['modified']), HTTP_WebDAV_Server::mkprop('getlastmodified', $event['modified']),
HTTP_WebDAV_Server::mkprop('resourcetype',''), // iPhone requires that attribute! HTTP_WebDAV_Server::mkprop('resourcetype',''), // DAVKit requires that attribute!
); );
//error_log(__FILE__ . __METHOD__ . "Calendar Data : $calendar_data"); //error_log(__FILE__ . __METHOD__ . "Calendar Data : $calendar_data");
if ($calendar_data) if ($calendar_data)
@ -185,7 +186,7 @@ class calendar_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['files'][] = array(
'path' => self::get_path($event), 'path' => $path.self::get_path($event),
'props' => $props, 'props' => $props,
); );
} }
@ -228,7 +229,7 @@ class calendar_groupdav extends groupdav_handler
return false; // return nothing for now, todo: check if we can pass it on to the infolog handler return false; // return nothing for now, todo: check if we can pass it on to the infolog handler
// todos are handled by the infolog handler // todos are handled by the infolog handler
//$infolog_handler = new groupdav_infolog(); //$infolog_handler = new groupdav_infolog();
//return $infolog_handler->propfind($path,$options,$files,$user,$method); //return $infolog_handler->propfind($options['path'],$options,$options['files'],$user,$method);
case 'VCALENDAR': case 'VCALENDAR':
case 'VEVENT': case 'VEVENT':
break; // that's our default anyway break; // that's our default anyway
@ -434,34 +435,78 @@ class calendar_groupdav extends groupdav_handler
function put(&$options,$id,$user=null) function put(&$options,$id,$user=null)
{ {
if ($this->debug) error_log(__METHOD__."($id, $user)".print_r($options,true)); if ($this->debug) error_log(__METHOD__."($id, $user)".print_r($options,true));
$return_no_access=true; // as handled by importVCal anyway and allows it to set the status for participants $return_no_access=true; // as handled by importVCal anyway and allows it to set the status for participants
$event = $this->_common_get_put_delete('PUT',$options,$id,$return_no_access); $oldEvent = $this->_common_get_put_delete('PUT',$options,$id,$return_no_access);
if (!is_null($oldEvent) && !is_array($oldEvent))
if (!is_null($event) && !is_array($event))
{ {
if ($this->debug) error_log(__METHOD__.print_r($event,true).function_backtrace()); if ($this->debug) error_log(__METHOD__.print_r($oldEvent,true).function_backtrace());
return $event; return $oldEvent;
} }
if (is_null($oldEvent) && !$this->bo->check_perms(EGW_ACL_ADD, 0, $user))
{
// we have no add permission on this user's calendar
if ($this->debug) error_log(__METHOD__."(,$user) we have not enough rights on this calendar");
return '403 Forbidden';
}
$handler = $this->_get_handler(); $handler = $this->_get_handler();
$vCalendar = htmlspecialchars_decode($options['content']);
if (!is_numeric($id) && ($foundEntries = $handler->find_event($options['content'], 'exact'))) if (is_array($oldEvent))
{ {
$id = array_shift($foundEntries); $eventId = $oldEvent['id'];
if ($return_no_access)
{
$retval = true;
}
else
{
// let lightning think the event is added
$retval = '201 Created';
}
}
else
{
// new entry?
if (($foundEvents = $handler->search($vCalendar)))
{
if (($eventId = array_shift($foundEvents)) &&
(list($eventId) = explode(':', $eventId)) &&
($oldEvent = $this->bo->read($eventId)))
{
$retval = '301 Moved Permanently';
}
else
{
// to be safe
$eventId = -1;
$retval = '201 Created';
}
}
else
{
// new entry
$eventId = -1;
$retval = '201 Created';
}
} }
if (!($cal_id = $handler->importVCal($options['content'],is_numeric($id) ? $id : -1, if (!($cal_id = $handler->importVCal($vCalendar, $eventId,
self::etag2value($this->http_if_match)))) self::etag2value($this->http_if_match), false, 0, $this->principalURL, $user)))
{ {
if ($this->debug) error_log(__METHOD__."(,$id) importVCal($options[content]) returned false"); if ($this->debug) error_log(__METHOD__."(,$id) importVCal($options[content]) returned false");
return '403 Forbidden'; return '403 Forbidden';
} }
header('ETag: '.$this->get_etag($cal_id)); header('ETag: '.$this->get_etag($cal_id));
if (is_null($event) || !$return_no_access) // let lightning think the event is added if ($retval !== true)
{ {
if ($this->debug) error_log(__METHOD__."(,$id,$user) cal_id=$cal_id, is_null(\$event)=".(int)is_null($event)); $path = preg_replace('|(.*)/[^/]*|', '\1/', $options['path']);
header('Location: '.$this->base_uri.self::get_path($cal_id)); if ($this->debug) error_log(__METHOD__."(,$id,$user) cal_id=$cal_id: $retval");
return '201 Created'; header('Location: '.$this->base_uri.$path.self::get_path($cal_id));
return $retval;
} }
return true; return true;
} }
@ -574,8 +619,7 @@ class calendar_groupdav extends groupdav_handler
*/ */
function read($id) function read($id)
{ {
//$cal_read = $this->bo->read($id,null,false,'server');//njv: do we actually get anything if ($this->debug > 1) error_log("bo-ical read :$id:");
if ($this->debug > 1) error_log("bo-ical read :$id:");//njv:
return $this->bo->read($id,null,false,'server'); return $this->bo->read($id,null,false,'server');
} }
@ -616,12 +660,12 @@ class calendar_groupdav extends groupdav_handler
{ {
if ($recurrence['reference']) // ignore series master if ($recurrence['reference']) // ignore series master
{ {
$etag .= ':'.substr($this->get_etag($recurrence),1,-1); $etag .= ':'.substr($this->get_etag($recurrence),4,-4);
} }
} }
} }
//error_log(__METHOD__ . "($entry[id] ($entry[etag]): $entry[title] --> etag=$etag"); //error_log(__METHOD__ . "($entry[id] ($entry[etag]): $entry[title] --> etag=$etag");
return '"'.$etag.'"'; return 'EGw-'.$etag.'-wGE';
} }
/** /**
@ -637,23 +681,74 @@ class calendar_groupdav extends groupdav_handler
} }
/** /**
* Add extra properties for calendar collections * Add the privileges of the current user
* *
* @param array $props=array() regular props by the groupdav handler * @param array $props=array() regular props by the groupdav handler
* @return array * @return array
*/ */
static function extra_properties(array $props=array()) static function current_user_privilege_set(array $props=array())
{ {
// calendaring URL of the current user $props[] = HTTP_WebDAV_Server::mkprop(groupdav::DAV,'current-user-privilege-set',
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$_SERVER['SCRIPT_NAME'].'/'); array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'privilege',
array(//HTTP_WebDAV_Server::mkprop(groupdav::DAV,'all',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read',''),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'read-free-busy',''),
//HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read-current-user-privilege-set',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'bind',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'unbind',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-properties',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-content',''),
))));
return $props;
}
/**
* Add extra properties for calendar collections
*
* @param array $props=array() regular props by the groupdav handler
* @param string $displayname
* @param string $base_uri=null base url of handler
* @return array
*/
static function extra_properties(array $props=array(), $displayname, $base_uri=null)
{
// calendar description
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-description',$displayname);
// BOX URLs of the current user
/*
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'schedule-inbox-URL',
array(HTTP_WebDAV_Server::mkprop(self::DAV,'href',$base_uri.'/calendar/')));
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'schedule-outbox-URL',
array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'href',$base_uri.'/calendar/')));
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'schedule-default-calendar-URL',
array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'href',$base_uri.'/calendar/')));
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALENDARSERVER,'dropbox-home-URL',
array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'href',$base_uri.'/calendar/')));
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALENDARSERVER,'notifications-URL',
array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'href',$base_uri.'/calendar/')));
*/
// email of the current user, see caldav-sheduling draft // email of the current user, see caldav-sheduling draft
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$GLOBALS['egw_info']['user']['email']); $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set',array(
HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email'])));
// supported components, currently only VEVENT // supported components, currently only VEVENT
$props[] = $sc = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-component-set',array( $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-component-set',array(
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VEVENT')), HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VEVENT')),
// HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VTODO')), // not yet supported // HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VTODO')), // not yet supported
)); ));
$props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-multiget'))))));
//$props = self::current_user_privilege_set($props);
return $props; return $props;
} }

View File

@ -53,6 +53,7 @@ class calendar_ical extends calendar_boupdate
'DECLINED' => 'R', 'DECLINED' => 'R',
'TENTATIVE' => 'T', 'TENTATIVE' => 'T',
'DELEGATED' => 'D', 'DELEGATED' => 'D',
'X-UNINVITED' => 'G', // removed
); );
/** /**
@ -148,6 +149,13 @@ class calendar_ical extends calendar_boupdate
*/ */
var $vCalendar; var $vCalendar;
/**
* Addressbook BO instance
*
* @var array
*/
var $addressbook;
/** /**
* Set Logging * Set Logging
* *
@ -168,21 +176,29 @@ class calendar_ical extends calendar_boupdate
if ($this->log) $this->logfile = $GLOBALS['egw_info']['server']['temp_dir']."/log-vcal"; if ($this->log) $this->logfile = $GLOBALS['egw_info']['server']['temp_dir']."/log-vcal";
$this->clientProperties = $_clientProperties; $this->clientProperties = $_clientProperties;
$this->vCalendar = new Horde_iCalendar; $this->vCalendar = new Horde_iCalendar;
$this->addressbook = new addressbook_bo;
} }
/** /**
* Exports one calendar event to an iCalendar item * Exports one calendar event to an iCalendar item
* *
* @param int/array $events (array of) cal_id or array of the events * @param int|array $events (array of) cal_id or array of the events
* @param string $version='1.0' could be '2.0' too * @param string $version='1.0' could be '2.0' too
* @param string $method='PUBLISH' * @param string $method='PUBLISH'
* @param int $recur_date=0 if set export the next recurrence at or after the timestamp, * @param int $recur_date=0 if set export the next recurrence at or after the timestamp,
* default 0 => export whole series (or events, if not recurring) * default 0 => export whole series (or events, if not recurring)
* @return string/boolean string with iCal or false on error (eg. no permission to read the event) * @param string $principalURL='' Used for CalDAV exports
* @return string|boolean string with iCal or false on error (eg. no permission to read the event)
*/ */
function &exportVCal($events, $version='1.0', $method='PUBLISH', $recur_date=0) function &exportVCal($events, $version='1.0', $method='PUBLISH', $recur_date=0, $principalURL='')
{ {
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
"($version, $method, $recur_date, $principalURL)\n",
3, $this->logfile);
}
$egwSupportedFields = array( $egwSupportedFields = array(
'CLASS' => 'public', 'CLASS' => 'public',
'SUMMARY' => 'title', 'SUMMARY' => 'title',
@ -222,7 +238,7 @@ class calendar_ical extends calendar_boupdate
$vtimezones_added = array(); $vtimezones_added = array();
foreach ($events as $event) foreach ($events as $event)
{ {
$mailtoOrganizer = false; $organizerURL = '';
$organizerCN = false; $organizerCN = false;
$recurrence = $this->date2usertime($recur_date); $recurrence = $this->date2usertime($recur_date);
$tzid = null; $tzid = null;
@ -453,17 +469,17 @@ class calendar_ical extends calendar_boupdate
foreach ((array)$event['participants'] as $uid => $status) foreach ((array)$event['participants'] as $uid => $status)
{ {
if (!($info = $this->resource_info($uid))) continue; if (!($info = $this->resource_info($uid))) continue;
$mailtoParticipant = $info['email'] ? 'MAILTO:'.$info['email'] : ''; $participantURL = $info['email'] ? 'MAILTO:'.$info['email'] : '';
$participantCN = '"' . ($info['cn'] ? $info['cn'] : $info['name']) . '"'; $participantCN = '"' . ($info['cn'] ? $info['cn'] : $info['name']) . '"';
calendar_so::split_status($status, $quantity, $role); calendar_so::split_status($status, $quantity, $role);
if ($role == 'CHAIR' && $uid != $this->user) if ($role == 'CHAIR' && $uid != $this->user)
{ {
$mailtoOrganizer = $mailtoParticipant; $organizerURL = $participantURL;
$organizerCN = $participantCN; $organizerCN = $participantCN;
if ($status == 'U') continue; // saved ORGANIZER $organizerUID = $uid;
} }
// RB: MAILTO href contains only the email-address, NO cn! // RB: MAILTO href contains only the email-address, NO cn!
$attributes['ATTENDEE'][] = $mailtoParticipant; $attributes['ATTENDEE'][] = $participantURL;
// RSVP={TRUE|FALSE} // resonse expected, not set in eGW => status=U // RSVP={TRUE|FALSE} // resonse expected, not set in eGW => status=U
$rsvp = $status == 'U' ? 'TRUE' : 'FALSE'; $rsvp = $status == 'U' ? 'TRUE' : 'FALSE';
// PARTSTAT={NEEDS-ACTION|ACCEPTED|DECLINED|TENTATIVE|DELEGATED|COMPLETED|IN-PROGRESS} everything from delegated is NOT used by eGW atm. // PARTSTAT={NEEDS-ACTION|ACCEPTED|DECLINED|TENTATIVE|DELEGATED|COMPLETED|IN-PROGRESS} everything from delegated is NOT used by eGW atm.
@ -511,20 +527,32 @@ class calendar_ical extends calendar_boupdate
case 'ORGANIZER': case 'ORGANIZER':
// according to iCalendar standard, ORGANIZER not used for events in the own calendar // according to iCalendar standard, ORGANIZER not used for events in the own calendar
if (!$organizerCN && if (!$organizerCN)
($event['owner'] != $this->user
|| $this->productManufacturer != 'groupdav'
|| $this->productName == 'kde'))
{ {
$mailtoOrganizer = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email'); $organizerURL = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email');
$mailtoOrganizer = $mailtoOrganizer ? 'MAILTO:'.$mailtoOrganizer : ''; $organizerURL = $organizerURL ? 'MAILTO:'.$organizerURL : '';
$organizerCN = '"' . trim($GLOBALS['egw']->accounts->id2name($event['owner'],'account_firstname') $organizerCN = '"' . trim($GLOBALS['egw']->accounts->id2name($event['owner'],'account_firstname')
. ' ' . $GLOBALS['egw']->accounts->id2name($event['owner'],'account_lastname')) . '"'; . ' ' . $GLOBALS['egw']->accounts->id2name($event['owner'],'account_lastname')) . '"';
$organizerUID = $event['owner'];
if (!isset($event['participants'][$event['owner']]))
{
$attributes['ATTENDEE'][] = $organizerURL;
$parameters['ATTENDEE'][] = array(
'CN' => $organizerCN,
'ROLE' => 'CHAIR',
'PARTSTAT' => 'DELEGATED',
'CUTYPE' => 'INDIVIDUAL',
'RSVP' => 'FALSE',
'X-EGROUPWARE-UID' => $event['owner'],
);
}
} }
if ($organizerCN) if ($this->productManufacturer != 'groupdav'
|| !$this->check_perms(EGW_ACL_EDIT,$event['id']))
{ {
$attributes['ORGANIZER'] = $mailtoOrganizer; $attributes['ORGANIZER'] = $organizerURL;
$parameters['ORGANIZER']['CN'] = $organizerCN; $parameters['ORGANIZER']['CN'] = $organizerCN;
$parameters['ORGANIZER']['X-EGROUPWARE-UID'] = $organizerUID;
} }
break; break;
@ -950,13 +978,15 @@ class calendar_ical extends calendar_boupdate
* @param boolean $merge=false merge data with existing entry * @param boolean $merge=false merge data with existing entry
* @param int $recur_date=0 if set, import the recurrence at this timestamp, * @param int $recur_date=0 if set, import the recurrence at this timestamp,
* default 0 => import whole series (or events, if not recurring) * default 0 => import whole series (or events, if not recurring)
* @param string $principalURL='' Used for CalDAV imports
* @param int $user=null account_id of owner, default null
* @return int|boolean cal_id > 0 on success, false on failure or 0 for a failed etag * @return int|boolean cal_id > 0 on success, false on failure or 0 for a failed etag
*/ */
function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0) function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0, $principalURL='', $user=null)
{ {
if (!is_array($this->supportedFields)) $this->setSupportedFields(); if (!is_array($this->supportedFields)) $this->setSupportedFields();
if (!($events = $this->icaltoegw($_vcalData))) if (!($events = $this->icaltoegw($_vcalData, $principalURL)))
{ {
return false; return false;
} }
@ -1001,8 +1031,9 @@ class calendar_ical extends calendar_boupdate
} }
if ($this->log) if ($this->log)
{ {
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
array2string($event)."\n",3,$this->logfile); ."($cal_id, $etag, $recur_date, $principalURL, $user)\n"
. array2string($event)."\n",3,$this->logfile);
} }
/* /*
@ -1090,7 +1121,7 @@ class calendar_ical extends calendar_boupdate
else else
{ {
// no merge // no merge
if(!isset($this->supportedFields['category'])) if(!isset($this->supportedFields['category']) || !isset($event['category']))
{ {
$event['category'] = $event_info['stored_event']['category']; $event['category'] = $event_info['stored_event']['category'];
} }
@ -1192,9 +1223,20 @@ class calendar_ical extends calendar_boupdate
$event['non_blocking'] = 1; $event['non_blocking'] = 1;
} }
if (!is_null($user))
{
if ($this->check_perms(EGW_ACL_ADD, 0, $user))
{
$event['owner'] = $user;
}
else
{
return false; // no permission
}
}
// check if an owner is set and the current user has add rights // check if an owner is set and the current user has add rights
// for that owners calendar; if not set the current user // for that owners calendar; if not set the current user
if (!isset($event['owner']) elseif (!isset($event['owner'])
|| !$this->check_perms(EGW_ACL_ADD, 0, $event['owner'])) || !$this->check_perms(EGW_ACL_ADD, 0, $event['owner']))
{ {
$event['owner'] = $this->user; $event['owner'] = $this->user;
@ -1945,11 +1987,18 @@ class calendar_ical extends calendar_boupdate
// __FILE__, __LINE__, PEAR_LOG_DEBUG); // __FILE__, __LINE__, PEAR_LOG_DEBUG);
} }
function icaltoegw($_vcalData) /**
* Convert vCalendar data in EGw events
*
* @param string $_vcalData
* @param string $principalURL='' Used for CalDAV imports
* @return array|boolean events on success, false on failure
*/
function icaltoegw($_vcalData, $principalURL='')
{ {
if ($this->log) if ($this->log)
{ {
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."($principalURL)\n" .
array2string($_vcalData)."\n",3,$this->logfile); array2string($_vcalData)."\n",3,$this->logfile);
} }
@ -1987,7 +2036,7 @@ class calendar_ical extends calendar_boupdate
{ {
if (is_a($component, 'Horde_iCalendar_vevent')) if (is_a($component, 'Horde_iCalendar_vevent'))
{ {
if (($event = $this->vevent2egw($component, $version, $this->supportedFields))) if (($event = $this->vevent2egw($component, $version, $this->supportedFields, $principalURL)))
{ {
//common adjustments //common adjustments
if ($this->productManufacturer == '' && $this->productName == '' if ($this->productManufacturer == '' && $this->productName == ''
@ -2020,6 +2069,14 @@ class calendar_ical extends calendar_boupdate
$events[] = $event; $events[] = $event;
} }
} }
else
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.'()' .
get_class($component)." found\n",3,$this->logfile);
}
}
} }
date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']); date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
@ -2033,12 +2090,21 @@ class calendar_ical extends calendar_boupdate
* @param array $component VEVENT * @param array $component VEVENT
* @param string $version vCal version (1.0/2.0) * @param string $version vCal version (1.0/2.0)
* @param array $supportedFields supported fields of the device * @param array $supportedFields supported fields of the device
* @param string $principalURL='' Used for CalDAV imports
* *
* @return array|boolean event on success, false on failure * @return array|boolean event on success, false on failure
*/ */
function vevent2egw(&$component, $version, $supportedFields) function vevent2egw(&$component, $version, $supportedFields, $principalURL='')
{ {
if (!is_a($component, 'Horde_iCalendar_vevent')) return false; if (!is_a($component, 'Horde_iCalendar_vevent'))
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.'()' .
get_class($component)." found\n",3,$this->logfile);
}
return false;
}
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) { if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) {
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length']; $minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
@ -2106,8 +2172,15 @@ class calendar_ical extends calendar_boupdate
$vcardData['end'] = $dtend_ts; $vcardData['end'] = $dtend_ts;
} }
} }
if (!isset($vcardData['start'])) return false; // not a valid entry if (!isset($vcardData['start']))
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "() DTSTART missing!\n",3,$this->logfile);
}
return false; // not a valid entry
}
// lets see what we can get from the vcard // lets see what we can get from the vcard
foreach ($component->_attributes as $attributes) foreach ($component->_attributes as $attributes)
{ {
@ -2436,7 +2509,7 @@ class calendar_ical extends calendar_boupdate
} }
break; break;
case 'ATTENDEE': case 'ATTENDEE':
case 'ORGANIZER': // will be written direct to the event case 'ORGANIZER':
if (isset($attributes['params']['PARTSTAT'])) if (isset($attributes['params']['PARTSTAT']))
{ {
$attributes['params']['STATUS'] = $attributes['params']['PARTSTAT']; $attributes['params']['STATUS'] = $attributes['params']['PARTSTAT'];
@ -2450,14 +2523,64 @@ class calendar_ical extends calendar_boupdate
{ {
$status = 'X'; // client did not return the status $status = 'X'; // client did not return the status
} }
$cn = ''; $uid = $email = $cn = '';
if (preg_match('/MAILTO:([@.a-z0-9_-]+)|MAILTO:"?([.a-z0-9_ -]*)"?[ ]*<([@.a-z0-9_-]*)>/i', $quantity = 1;
$role = 'REQ-PARTICIPANT';
if (!empty($attributes['params']['ROLE']))
{
$role = $attributes['params']['ROLE'];
}
// try pricipal url from CalDAV
if (strpos($attributes['value'], 'http') === 0)
{
if (!empty($principalURL) && strstr($attributes['value'], $principalURL) !== false)
{
$uid = $this->user;
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "(): Found myself: '$uid'\n",3,$this->logfile);
}
}
else
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '(): Unknown URI: ' . $attributes['value']
. "\n",3,$this->logfile);
}
$attributes['value'] = '';
}
}
// try X-EGROUPWARE-UID
if (!$uid && !empty($attributes['params']['X-EGROUPWARE-UID']))
{
$uid = $attributes['params']['X-EGROUPWARE-UID'];
if (!empty($attributes['params']['X-EGROUPWARE-QUANTITY']))
{
$quantity = $attributes['params']['X-EGROUPWARE-QUANTITY'];
}
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "(): Found X-EGROUPWARE-UID: '$uid'\n",3,$this->logfile);
}
}
elseif ($attributes['value'] == 'Unknown')
{
// we use the current user
$uid = $this->user;
}
// try to find an email address
elseif (preg_match('/MAILTO:([@.a-z0-9_-]+)|MAILTO:"?([.a-z0-9_ -]*)"?[ ]*<([@.a-z0-9_-]*)>/i',
$attributes['value'],$matches)) $attributes['value'],$matches))
{ {
$email = $matches[1] ? $matches[1] : $matches[3]; $email = $matches[1] ? $matches[1] : $matches[3];
$cn = isset($matches[2]) ? $matches[2]: ''; $cn = isset($matches[2]) ? $matches[2]: '';
} }
elseif (preg_match('/"?([.a-z0-9_ -]*)"?[ ]*<([@.a-z0-9_-]*)>/i', elseif (!empty($attributes['value']) &&
preg_match('/"?([.a-z0-9_ -]*)"?[ ]*<([@.a-z0-9_-]*)>/i',
$attributes['value'],$matches)) $attributes['value'],$matches))
{ {
$cn = $matches[1]; $cn = $matches[1];
@ -2467,82 +2590,92 @@ class calendar_ical extends calendar_boupdate
{ {
$email = $attributes['value']; $email = $attributes['value'];
} }
else if (!$uid && $email && ($uid = $GLOBALS['egw']->accounts->name2id($email, 'account_email')))
{ {
$email = false; // no email given // we use the account we found
} if ($this->log)
$searcharray = array();
if ($email)
{
$searcharray = array('email' => $email, 'email_home' => $email);
}
if (isset($attributes['params']['CN']) && $attributes['params']['CN'])
{
if ($attributes['params']['CN'][0] == '"'
&& substr($attributes['params']['CN'],-1) == '"')
{ {
$attributes['params']['CN'] = substr($attributes['params']['CN'],1,-1); error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "() Found account: '$uid', '$cn', '$email'\n",3,$this->logfile);
} }
$searcharray['n_fn'] = $attributes['params']['CN'];
} }
elseif ($cn) if (!$uid)
{ {
$searcharray['n_fn'] = $cn; $searcharray = array();
} // search for provided email address ...
if (($uid = $attributes['params']['X-EGROUPWARE-UID']) if ($email)
&& ($info = $this->resource_info($uid))
&& (!$email || $info['email'] == $email))
{
// we use the (checked) X-EGROUPWARE-UID
}
//elseif (//$attributes['params']['CUTYPE'] == 'GROUP'
elseif (preg_match('/(.*) Group/', $searcharray['n_fn'], $matches))
{
if (($uid = $GLOBALS['egw']->accounts->name2id($matches[1], 'account_lid', 'g')))
{ {
//Horde::logMessage("vevent2egw: group participant $uid", $searcharray = array('email' => $email, 'email_home' => $email);
// __FILE__, __LINE__, PEAR_LOG_DEBUG); }
if ($status != 'X' && $status != 'U') // ... and for provided CN
if (!empty($attributes['params']['CN']))
{
if ($attributes['params']['CN'][0] == '"'
&& substr($attributes['params']['CN'],-1) == '"')
{ {
// User tries to reply to the group invitiation $cn = substr($attributes['params']['CN'],1,-1);
$members = $GLOBALS['egw']->accounts->members($uid, true); }
if (in_array($this->user, $members)) $searcharray['n_fn'] = $cn;
}
elseif ($cn)
{
$searcharray['n_fn'] = $cn;
}
//elseif (//$attributes['params']['CUTYPE'] == 'GROUP'
if (preg_match('/(.*) Group/', $cn, $matches))
{
if (($uid = $GLOBALS['egw']->accounts->name2id($matches[1], 'account_lid', 'g')))
{
//Horde::logMessage("vevent2egw: group participant $uid",
// __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!isset($vcardData['participants'][$this->user]) &&
$status != 'X' && $status != 'U')
{ {
//Horde::logMessage("vevent2egw: set status to " . $status, // User tries to reply to the group invitiation
// __FILE__, __LINE__, PEAR_LOG_DEBUG); $members = $GLOBALS['egw']->accounts->members($uid, true);
$vcardData['participants'][$this->user] = if (in_array($this->user, $members))
calendar_so::combine_status($status); {
//Horde::logMessage("vevent2egw: set status to " . $status,
// __FILE__, __LINE__, PEAR_LOG_DEBUG);
$vcardData['participants'][$this->user] =
calendar_so::combine_status($status,$quantity,$role);
}
} }
$status = 'U'; // keep the group $status = 'U'; // keep the group
} }
else continue; // can't find this group
} }
else continue; // can't find this group elseif (empty($searcharray))
}
elseif ($attributes['value'] == 'Unknown')
{
$uid = $this->user;
}
elseif ($email && ($uid = $GLOBALS['egw']->accounts->name2id($email,'account_email')))
{
// we use the account we found
}
elseif (!$searcharray)
{
continue; // participants without email AND CN --> ignore it
}
elseif ((list($data) = ExecMethod2('addressbook.addressbook_bo.search',$searcharray,
array('id','egw_addressbook.account_id as account_id','n_fn'),'egw_addressbook.account_id IS NOT NULL DESC, n_fn IS NOT NULL DESC','','',false,'OR')))
{
$uid = $data['account_id'] ? (int)$data['account_id'] : 'c'.$data['id'];
}
else
{
if (!$email)
{ {
$email = 'no-email@egroupware.org'; // set dummy email to store the CN continue; // participants without email AND CN --> ignore it
}
elseif ((list($data) = $this->addressbook->search($searcharray,
array('id','egw_addressbook.account_id as account_id','n_fn'),
'egw_addressbook.account_id IS NOT NULL DESC, n_fn IS NOT NULL DESC',
'','',false,'OR')))
{
// found an addressbook entry
$uid = $data['account_id'] ? (int)$data['account_id'] : 'c'.$data['id'];
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "() Found addressbook entry: '$uid', '$cn', '$email'\n",3,$this->logfile);
}
}
else
{
if (!$email)
{
$email = 'no-email@egroupware.org'; // set dummy email to store the CN
}
$uid = 'e'. ($cn ? '"' . $cn . '" <' . $email . '>' : $email);
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "() Not Found, create dummy: '$uid', '$cn', '$email'\n",3,$this->logfile);
}
} }
$uid = 'e'.($attributes['params']['CN'] ? $attributes['params']['CN'].' <'.$email.'>' : $email);
} }
switch($attributes['name']) switch($attributes['name'])
{ {
@ -2558,9 +2691,15 @@ class calendar_ical extends calendar_boupdate
// for multiple entries the ACCEPT wins // for multiple entries the ACCEPT wins
// add quantity and role // add quantity and role
$vcardData['participants'][$uid] = $vcardData['participants'][$uid] =
calendar_so::combine_status($status, calendar_so::combine_status($status, $quantity, $role);
$attributes['params']['X-EGROUPWARE-QUANTITY'],
$attributes['params']['ROLE']); if (!$this->calendarOwner && is_numeric($uid) &&
$role == 'CHAIR' &&
is_a($component->getAttribute('ORGANIZER'), 'PEAR_Error'))
{
// we can store the ORGANIZER as event owner
$event['owner'] = $uid;
}
} }
break; break;
@ -2572,7 +2711,7 @@ class calendar_ical extends calendar_boupdate
$vcardData['participants'][$uid] = $vcardData['participants'][$uid] =
calendar_so::combine_status($status, $quantity, 'CHAIR'); calendar_so::combine_status($status, $quantity, 'CHAIR');
} }
if (is_numeric($uid) && ($uid == $this->calendarOwner || !$this->calendarOwner)) if (!$this->calendarOwner && is_numeric($uid))
{ {
// we can store the ORGANIZER as event owner // we can store the ORGANIZER as event owner
$event['owner'] = $uid; $event['owner'] = $uid;
@ -2585,7 +2724,7 @@ class calendar_ical extends calendar_boupdate
{ {
// save the ORGANIZER as event CHAIR // save the ORGANIZER as event CHAIR
$vcardData['participants'][$uid] = $vcardData['participants'][$uid] =
calendar_so::combine_status('U', 1, 'CHAIR'); calendar_so::combine_status('D', 1, 'CHAIR');
} }
} }
} }
@ -2694,9 +2833,14 @@ class calendar_ical extends calendar_boupdate
{ {
$filter = $relax ? 'relax' : 'check'; $filter = $relax ? 'relax' : 'check';
$event = array_shift($events); $event = array_shift($events);
$eventId = -1;
if ($this->so->isWholeDay($event)) $event['whole_day'] = true; if ($this->so->isWholeDay($event)) $event['whole_day'] = true;
if ($contentID)
{
$parts = preg_split('/:/', $contentID);
$event['id'] = $eventId = $parts[0];
}
$event['category'] = $this->find_or_add_categories($event['category'], $eventId); $event['category'] = $this->find_or_add_categories($event['category'], $eventId);
if ($contentID) $event['id'] = $contentID;
return $this->find_event($event, $filter); return $this->find_event($event, $filter);
} }
if ($this->log) if ($this->log)

View File

@ -758,15 +758,35 @@ ORDER BY cal_user_type, cal_usre_id
array('cal_id' => $event['cal_id']),__LINE__,__FILE__,'calendar'); array('cal_id' => $event['cal_id']),__LINE__,__FILE__,'calendar');
} }
// write information about recuring event, if recur_type is present in the array // write information about recuring event, if recur_type is present in the array
if (isset($event['recur_type'])) if ($event['recur_type'] != MCAL_RECUR_NONE)
{ {
// fetch information about the currently saved (old) event // fetch information about the currently saved (old) event
$old_min = (int) $this->db->select($this->dates_table,'MIN(cal_start)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchColumn(); $old_min = (int) $this->db->select($this->dates_table,'MIN(cal_start)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchColumn();
$old_duration = (int) $this->db->select($this->dates_table,'MIN(cal_end)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchColumn() - $old_min; $old_duration = (int) $this->db->select($this->dates_table,'MIN(cal_end)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchColumn() - $old_min;
$old_repeats = $this->db->select($this->repeats_table,'*',array('cal_id' => $cal_id),__LINE__,__FILE__,false,'','calendar')->fetch(); $old_repeats = $this->db->select($this->repeats_table,'*',array('cal_id' => $cal_id),__LINE__,__FILE__,false,'','calendar')->fetch();
$old_exceptions = $old_repeats['recur_exception'] ? explode(',',$old_repeats['recur_exception']) : array(); $old_exceptions = $old_repeats['recur_exception'] ? explode(',',$old_repeats['recur_exception']) : array();
if (!empty($old_exceptions))
{
sort($old_exceptions);
if ($old_min > $old_exceptions[0]) $old_min = $old_exceptions[0];
}
$event['recur_exception'] = is_array($event['recur_exception']) ? $event['recur_exception'] : array(); $event['recur_exception'] = is_array($event['recur_exception']) ? $event['recur_exception'] : array();
if (!empty($event['recur_exception']))
{
sort($event['recur_exception']);
}
$where = array('cal_id' => $cal_id,
'cal_recur_date' => 0);
$old_participants = array();
foreach ($this->db->select($this->user_table,'cal_user_type,cal_user_id,cal_status,cal_quantity,cal_role', $where,
__LINE__,__FILE__,false,'','calendar') as $row)
{
$uid = self::combine_user($row['cal_user_type'], $row['cal_user_id']);
$status = self::combine_status($row['cal_status'], $row['cal_quantity'], $row['cal_role']);
$old_participants[$uid] = $status;
}
// re-check: did so much recurrence data change that we have to rebuild it from scratch? // re-check: did so much recurrence data change that we have to rebuild it from scratch?
if (!$set_recurrences) if (!$set_recurrences)
@ -795,18 +815,20 @@ ORDER BY cal_user_type, cal_usre_id
{ {
// we adjust some possibly changed recurrences manually // we adjust some possibly changed recurrences manually
// deleted exceptions: re-insert recurrences into the user and dates table // deleted exceptions: re-insert recurrences into the user and dates table
if(count($deleted_exceptions = array_diff($old_exceptions,$event['recur_exception']))) if (count($deleted_exceptions = array_diff($old_exceptions,$event['recur_exception'])))
{ {
if (isset($event['cal_participants']))
{
$participants = $event['cal_participants'];
}
else
{
// use old default
$participants = $old_participants;
}
foreach($deleted_exceptions as $id => $deleted_exception) foreach($deleted_exceptions as $id => $deleted_exception)
{ {
// rebuild participants for the re-inserted recurrence // rebuild participants for the re-inserted recurrence
$participants = array();
$participants_only = $this->get_participants($cal_id); // participants without states
foreach($participants_only as $id => $participant_only)
{
$states = $this->get_recurrences($cal_id, $participant_only['uid']);
$participants[$participant_only['uid']] = $states[0]; // insert main status as default
}
$this->recurrence($cal_id, $deleted_exception, $deleted_exception + $old_duration, $participants); $this->recurrence($cal_id, $deleted_exception, $deleted_exception + $old_duration, $participants);
} }
} }
@ -1058,11 +1080,12 @@ ORDER BY cal_user_type, cal_usre_id
/** /**
* updates the participants of an event, taken into account the evtl. recurrences of the event(!) * updates the participants of an event, taken into account the evtl. recurrences of the event(!)
* this method just adds new participants or removes not longer set participants * this method just adds new participants or removes not longer set participants
* this method does never overwrite existing entries (except for delete) * this method does never overwrite existing entries (except the 0-recurrence and for delete)
* *
* @param int $cal_id * @param int $cal_id
* @param array $participants uid => status pairs * @param array $participants uid => status pairs
* @param int|boolean $change_since=0, false=new entry, 0=all, > 0 time from which on the repetitions should be changed * @param int|boolean $change_since=0, false=new event,
* 0=all, > 0 time from which on the repetitions should be changed
* @param boolean $add_only=false * @param boolean $add_only=false
* false = add AND delete participants if needed (full list of participants required in $participants) * false = add AND delete participants if needed (full list of participants required in $participants)
* true = only add participants if needed, no participant will be deleted (participants to check/add required in $participants) * true = only add participants if needed, no participant will be deleted (participants to check/add required in $participants)
@ -1148,7 +1171,7 @@ ORDER BY cal_user_type, cal_usre_id
// update participants // update participants
foreach($participants as $uid => $status) foreach($participants as $uid => $status)
{ {
$id = null; $type = $id = $quantity = $role = null;
self::split_user($uid,$type,$id); self::split_user($uid,$type,$id);
self::split_status($status,$quantity,$role); self::split_status($status,$quantity,$role);
$set = array( $set = array(

View File

@ -30,30 +30,25 @@ class infolog_bo
var $vfs_basedir='/infolog'; var $vfs_basedir='/infolog';
var $link_pathes = array(); var $link_pathes = array();
var $send_file_ips = array(); var $send_file_ips = array();
/** /**
* Set Logging * Set Logging
* *
* @var boolean * @var boolean
*/ */
var $log = false; var $log = false;
/** /**
* Cached timezone data * Cached timezone data
* *
* @var array id => data * @var array id => data
*/ */
protected static $tz_cache = array(); protected static $tz_cache = array();
/** /**
* current time as timestamp in user-time and server-time * current time as timestamp in user-time and server-time
* *
* var int * @var int
*/ */
var $user_time_now; var $user_time_now;
var $now; var $now;
/** /**
* name of timestamps in an InfoLog entry * name of timestamps in an InfoLog entry
* *
@ -288,7 +283,7 @@ class infolog_bo
/** /**
* check's if user has the requiered rights on entry $info_id * check's if user has the requiered rights on entry $info_id
* *
* @param int/array $info data or info_id of infolog entry to check * @param int|array $info data or info_id of infolog entry to check
* @param int $required_rights EGW_ACL_{READ|EDIT|ADD|DELETE} * @param int $required_rights EGW_ACL_{READ|EDIT|ADD|DELETE}
* @return boolean * @return boolean
*/ */
@ -514,13 +509,13 @@ class infolog_bo
/** /**
* Read an infolog entry specified by $info_id * Read an infolog entry specified by $info_id
* *
* @param int/array $info_id integer id or array with key 'info_id' of the entry to read * @param int|array $info_id integer id or array with key 'info_id' of the entry to read
* @param boolean $run_link_id2from=true should link_id2from run, default yes, * @param boolean $run_link_id2from=true should link_id2from run, default yes,
* need to be set to false if called from link-title to prevent an infinit recursion * need to be set to false if called from link-title to prevent an infinit recursion
* @param string $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time, * @param string $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time,
* 'array'=array or string with date-format * 'array'=array or string with date-format
* *
* @return array/boolean infolog entry, null if not found or false if no permission to read it * @return array|boolean infolog entry, null if not found or false if no permission to read it
*/ */
function &read($info_id,$run_link_id2from=true,$date_format='ts') function &read($info_id,$run_link_id2from=true,$date_format='ts')
{ {
@ -558,7 +553,7 @@ class infolog_bo
{ {
$time = new egw_time($data['info_enddate'], egw_time::$server_timezone); $time = new egw_time($data['info_enddate'], egw_time::$server_timezone);
// Set due date to 00:00 // Set due date to 00:00
$time->setTime(0, 0,0 ); $time->setTime(0, 0, 0);
$data['info_enddate'] = egw_time::to($time,'ts'); $data['info_enddate'] = egw_time::to($time,'ts');
} }
@ -568,9 +563,9 @@ class infolog_bo
/** /**
* Delete an infolog entry, evtl. incl. it's children / subs * Delete an infolog entry, evtl. incl. it's children / subs
* *
* @param int/array $info_id int id * @param int|array $info_id int id
* @param boolean $delete_children should the children be deleted * @param boolean $delete_children should the children be deleted
* @param int/boolean $new_parent parent to use for not deleted children if > 0 * @param int|boolean $new_parent parent to use for not deleted children if > 0
* @return boolean True if delete was successful, False otherwise ($info_id does not exist or no rights) * @return boolean True if delete was successful, False otherwise ($info_id does not exist or no rights)
*/ */
function delete($info_id,$delete_children=False,$new_parent=False) function delete($info_id,$delete_children=False,$new_parent=False)
@ -691,7 +686,7 @@ class infolog_bo
'info_id' => $values['info_id'], 'info_id' => $values['info_id'],
'info_datemodified' => $values['info_datemodified'], 'info_datemodified' => $values['info_datemodified'],
); );
foreach($this->responsible_edit as $name) foreach ($this->responsible_edit as $name)
{ {
if (isset($backup_values[$name])) $values[$name] = $backup_values[$name]; if (isset($backup_values[$name])) $values[$name] = $backup_values[$name];
} }
@ -790,7 +785,7 @@ class infolog_bo
} }
else else
{ {
$time = new egw_time($values['info_enddate'], egw_time::$user_timezone); $time = new egw_time($values['info_enddate'], egw_time::$server_timezone);
// Set due date to 00:00 // Set due date to 00:00
$time->setTime(0, 0, 0); $time->setTime(0, 0, 0);
$to_write['info_enddate'] = egw_time::to($time,'ts'); $to_write['info_enddate'] = egw_time::to($time,'ts');
@ -821,13 +816,13 @@ class infolog_bo
{ {
$old = $this->read($values['info_id'], false, 'server'); $old = $this->read($values['info_id'], false, 'server');
} }
if(($info_id = $this->so->write($to_write,$check_modified))) if (($info_id = $this->so->write($to_write,$check_modified)))
{ {
if (!isset($values['info_type']) || $status_only) if (!isset($values['info_type']) || $status_only)
{ {
$values = $this->read($info_id); $values = $this->read($info_id, true, 'server');
} }
if($values['info_id'] && $old['info_status'] != 'deleted') if ($values['info_id'] && $old['info_status'] != 'deleted')
{ {
// update // update
$GLOBALS['egw']->contenthistory->updateTimeStamp( $GLOBALS['egw']->contenthistory->updateTimeStamp(
@ -910,11 +905,12 @@ class infolog_bo
{ {
$query['start'] = egw_time::user2server($query['start'],'ts'); $query['start'] = egw_time::user2server($query['start'],'ts');
} }
$ret = $this->so->search($query); $ret = $this->so->search($query);
if (is_array($ret)) if (is_array($ret))
{ {
foreach($ret as $id => &$data) foreach ($ret as $id => &$data)
{ {
if (!$this->check_access($data,EGW_ACL_READ)) if (!$this->check_access($data,EGW_ACL_READ))
{ {
@ -922,7 +918,7 @@ class infolog_bo
continue; continue;
} }
// convert system- to user-time // convert system- to user-time
foreach($this->timestamps as $key) foreach ($this->timestamps as $key)
{ {
if ($data[$key]) if ($data[$key])
{ {
@ -1043,8 +1039,8 @@ class infolog_bo
* *
* Is called as hook to participate in the linking * Is called as hook to participate in the linking
* *
* @param int/array $info int info_id or array with infolog entry * @param int|array $info int info_id or array with infolog entry
* @return string/boolean string with the title, null if $info not found, false if no perms to view * @return string|boolean string with the title, null if $info not found, false if no perms to view
*/ */
function link_title($info) function link_title($info)
{ {
@ -1068,13 +1064,13 @@ class infolog_bo
function link_titles(array $ids) function link_titles(array $ids)
{ {
$titles = array(); $titles = array();
foreach($this->search($params=array( foreach ($this->search($params=array(
'col_filter' => array('info_id' => $ids), 'col_filter' => array('info_id' => $ids),
)) as $info) )) as $info)
{ {
$titles[$info['info_id']] = $this->link_title($info); $titles[$info['info_id']] = $this->link_title($info);
} }
foreach(array_diff($ids,array_keys($titles)) as $id) foreach (array_diff($ids,array_keys($titles)) as $id)
{ {
$titles[$id] = false; // we assume every not returned entry to be not readable, as we notify the link class about all deletes $titles[$id] = false; // we assume every not returned entry to be not readable, as we notify the link class about all deletes
} }
@ -1103,7 +1099,7 @@ class infolog_bo
$content = array(); $content = array();
if (is_array($ids)) if (is_array($ids))
{ {
foreach($ids as $id => $info ) foreach ($ids as $id => $info )
{ {
$content[$id] = $this->link_title($id); $content[$id] = $this->link_title($id);
} }
@ -1173,7 +1169,7 @@ class infolog_bo
} }
while ($infos = $this->search($query)) while ($infos = $this->search($query))
{ {
foreach($infos as $info) foreach ($infos as $info)
{ {
$start = new egw_time($info['info_startdate'],egw_time::$user_timezone); $start = new egw_time($info['info_startdate'],egw_time::$user_timezone);
$time = (int) $start->format('Hi'); $time = (int) $start->format('Hi');
@ -1189,7 +1185,7 @@ class infolog_bo
$info['info_subject']; $info['info_subject'];
$view = egw_link::view('infolog',$info['info_id']); $view = egw_link::view('infolog',$info['info_id']);
$content=array(); $content=array();
foreach($icons = array( foreach ($icons = array(
$info['info_type'] => 'infolog', $info['info_type'] => 'infolog',
$this->status[$info['info_type']][$info['info_status']] => 'infolog', $this->status[$info['info_type']][$info['info_status']] => 'infolog',
) as $name => $app) ) as $name => $app)
@ -1228,7 +1224,7 @@ class infolog_bo
if (isset($args['infolog']) && count($args['infolog'])) if (isset($args['infolog']) && count($args['infolog']))
{ {
$icons = $this->so->get_status($args['infolog']); $icons = $this->so->get_status($args['infolog']);
foreach((array) $icons as $id => $status) foreach ((array) $icons as $id => $status)
{ {
if ($status && substr($status,-1) != '%') if ($status && substr($status,-1) != '%')
{ {
@ -1262,11 +1258,11 @@ class infolog_bo
// preserve categories without users read access // preserve categories without users read access
$old_infolog = $this->read($info_id); $old_infolog = $this->read($info_id);
$old_categories = explode(',',$old_infolog['info_cat']); $old_categories = explode(',',$old_infolog['info_cat']);
if(is_array($old_categories) && count($old_categories) > 0) if (is_array($old_categories) && count($old_categories) > 0)
{ {
foreach($old_categories as $cat_id) foreach ($old_categories as $cat_id)
{ {
if($cat_id && !$this->categories->check_perms(EGW_ACL_READ, $cat_id)) if ($cat_id && !$this->categories->check_perms(EGW_ACL_READ, $cat_id))
{ {
$old_cats_preserve[] = $cat_id; $old_cats_preserve[] = $cat_id;
} }
@ -1275,7 +1271,7 @@ class infolog_bo
} }
$cat_id_list = array(); $cat_id_list = array();
foreach ($catname_list as $cat_name) foreach ((array)$catname_list as $cat_name)
{ {
$cat_name = trim($cat_name); $cat_name = trim($cat_name);
$cat_id = $this->categories->name2id($cat_name, 'X-'); $cat_id = $this->categories->name2id($cat_name, 'X-');
@ -1550,7 +1546,7 @@ class infolog_bo
&& ($egwData = $this->read($infoData['info_id'], true, 'server'))) && ($egwData = $this->read($infoData['info_id'], true, 'server')))
{ {
// we only do a simple consistency check // we only do a simple consistency check
if (strpos($egwData['info_subject'], $infoData['info_subject']) === 0) if (!$relax || strpos($egwData['info_subject'], $infoData['info_subject']) === 0)
{ {
return array($egwData['info_id']); return array($egwData['info_id']);
} }

View File

@ -29,10 +29,11 @@ class infolog_groupdav extends groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog' * @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set * @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler * @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
*/ */
function __construct($app,$debug=null,$base_uri=null) function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
{ {
parent::__construct($app,$debug,$base_uri); parent::__construct($app,$debug,$base_uri,$principalURL);
$this->bo = new infolog_bo(); $this->bo = new infolog_bo();
} }
@ -56,7 +57,7 @@ class infolog_groupdav extends groupdav_handler
if (!is_array($info)) $info = $this->bo->read($info); if (!is_array($info)) $info = $this->bo->read($info);
$name = $info[self::PATH_ATTRIBUTE]; $name = $info[self::PATH_ATTRIBUTE];
} }
return '/infolog/'.$name.'.ics'; return $name.'.ics';
} }
/** /**
@ -72,33 +73,88 @@ class infolog_groupdav extends groupdav_handler
{ {
$starttime = microtime(true); $starttime = microtime(true);
$myself = ($user == $GLOBALS['egw_info']['user']['account_id']);
if ($options['filters'])
{
foreach($options['filters'] as $filter)
{
switch($filter['name'])
{
case 'comp-filter':
if ($this->debug > 1) error_log(__METHOD__."($options[path],...) comp-filter='{$filter['attrs']['name']}'");
switch($filter['attrs']['name'])
{
case 'VCALENDAR':
continue;
case 'VTODO':
break 3;
default: // We don't handle this
return false;
}
}
}
}
// check if we have to return the full calendar data or just the etag's
if (!($calendar_data = $options['props'] == 'all' && $options['root']['ns'] == groupdav::CALDAV) && is_array($options['props']))
{
foreach($options['props'] as $prop)
{
if ($prop['name'] == 'calendar-data')
{
$calendar_data = true;
break;
}
}
}
// todo add a filter to limit how far back entries from the past get synced // todo add a filter to limit how far back entries from the past get synced
$filter = array( $filter = array(
'info_type' => 'task', 'info_type' => 'task',
); );
//if (!$myself) $filter['info_owner'] = $user;
if ($id) $filter['info_id'] = $id; // propfind on a single id if ($id) $filter['info_id'] = $id; // propfind on a single id
// ToDo: add parameter to only return id & etag // ToDo: add parameter to only return id & etag
if (($tasks =& $this->bo->search($params=array( if (($tasks =& $this->bo->search($params=array(
'order' => 'info_datemodified', 'order' => 'info_datemodified',
'sort' => 'DESC', 'sort' => 'DESC',
'filter' => 'own', // filter my: entries user is responsible for, 'filter' => ($myself ? 'own' : 'own'), // filter my: entries user is responsible for,
// filter own: entries the user own or is responsible for // filter own: entries the user own or is responsible for
'date_format' => 'server',
'col_filter' => $filter, 'col_filter' => $filter,
)))) ))))
{ {
foreach($tasks as &$task) foreach($tasks as &$task)
{ {
$props = array(
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($task)),
HTTP_WebDAV_Server::mkprop('getcontenttype',$this->agent != 'kde' ?
'text/calendar; charset=utf-8; component=VTODO' : 'text/calendar'), // Konqueror (3.5) dont understand it otherwise
// getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set
HTTP_WebDAV_Server::mkprop('getlastmodified', $task['info_datemodified']),
HTTP_WebDAV_Server::mkprop('resourcetype',''), // DAVKit requires that attribute!
HTTP_WebDAV_Server::mkprop('getcontentlength',''),
);
if ($calendar_data)
{
$handler = $this->_get_handler();
$content = $handler->exportVTODO($task,'2.0','PUBLISH');
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength',bytes($content));
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-data',$content);
}
else
{
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength', ''); // expensive to calculate and no CalDAV client uses it
}
$files['files'][] = array( $files['files'][] = array(
'path' => self::get_path($task), 'path' => $path.self::get_path($task),
'props' => array( 'props' => $props,
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($task)),
HTTP_WebDAV_Server::mkprop('getcontenttype',$this->agent != 'kde' ?
'text/calendar; charset=utf-8; component=VTODO' : 'text/calendar'), // Konqueror (3.5) dont understand it otherwise
// getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set
HTTP_WebDAV_Server::mkprop('getlastmodified', $task['info_datemodified']),
HTTP_WebDAV_Server::mkprop('getcontentlength',''),
),
); );
} }
} }
@ -120,7 +176,7 @@ class infolog_groupdav extends groupdav_handler
return $task; return $task;
} }
$handler = $this->_get_handler(); $handler = $this->_get_handler();
$options['data'] = $handler->exportVTODO($id,'2.0',false,false); // keep UID the client set and no extra charset attributes $options['data'] = $handler->exportVTODO($id,'2.0','PUBLISH');
$options['mimetype'] = 'text/calendar; charset=utf-8'; $options['mimetype'] = 'text/calendar; charset=utf-8';
header('Content-Encoding: identity'); header('Content-Encoding: identity');
header('ETag: '.$this->get_etag($task)); header('ETag: '.$this->get_etag($task));
@ -137,22 +193,61 @@ class infolog_groupdav extends groupdav_handler
*/ */
function put(&$options,$id,$user=null) function put(&$options,$id,$user=null)
{ {
$ok = $this->_common_get_put_delete('PUT',$options,$id); if ($this->debug) error_log(__METHOD__."($id, $user)".print_r($options,true));
if (!is_null($ok) && !is_array($ok))
$oldTask = $this->_common_get_put_delete('PUT',$options,$id);
if (!is_null($oldTask) && !is_array($oldTask))
{ {
return $ok; return $oldTask;
} }
$handler = $this->_get_handler(); $handler = $this->_get_handler();
if (!($info_id = $handler->importVTODO($options['content'],is_numeric($id) ? $id : -1))) $vTodo = htmlspecialchars_decode($options['content']);
if (is_array($oldTask))
{
$taskId = $oldTask['info_id'];
$retval = true;
}
else
{
// new entry?
if (($foundTasks = $handler->searchVTODO($vTodo)))
{
if (($taskId = array_shift($foundTasks)) &&
($oldTask = $this->bo->read($taskId)))
{
$retval = '301 Moved Permanently';
}
else
{
// to be safe
$taskId = -1;
$retval = '201 Created';
}
}
else
{
// new entry
$taskId = -1;
$retval = '201 Created';
}
}
if (!($infoId = $handler->importVTODO($vTodo, $taskId, false, $user)))
{ {
if ($this->debug) error_log(__METHOD__."(,$id) import_vtodo($options[content]) returned false"); if ($this->debug) error_log(__METHOD__."(,$id) import_vtodo($options[content]) returned false");
return '403 Forbidden'; return '403 Forbidden';
} }
header('ETag: '.$this->get_etag($info_id));
if (is_null($ok) || $id != $info_id) if ($infoId != $taskId) $retval = '201 Created';
header('ETag: '.$this->get_etag($infoId));
if ($retval !== true)
{ {
header('Location: '.$this->base_uri.self::get_path($info_id)); $path = preg_replace('|(.*)/[^/]*|', '\1/', $options['path']);
return '201 Created'; header('Location: '.$this->base_uri.$path.self::get_path($infoId));
return $retval;
} }
return true; return true;
} }
@ -212,7 +307,39 @@ class infolog_groupdav extends groupdav_handler
{ {
return false; return false;
} }
return '"'.$info['info_id'].':'.$info['info_datemodified'].'"'; return 'EGw-'.$info['info_id'].':'.$info['info_datemodified'].'-wGE';
}
/**
* Add extra properties for calendar collections
*
* @param array $props=array() regular props by the groupdav handler
* @param string $displayname
* @param string $base_uri=null base url of handler
* @return array
*/
static function extra_properties(array $props=array(), $displayname, $base_uri=null)
{
// calendar description
$displayname = $GLOBALS['egw']->translation->convert(lang('Tasks of') . ' ' .
$displayname,
$GLOBALS['egw']->translation->charset(),'utf-8');
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-description',$displayname);
// email of the current user, see caldav-sheduling draft
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set',array(
HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email'])));
// supported components, currently only VEVENT
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-component-set',array(
// HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VEVENT')),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VTODO')),
));
$props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-multiget'))))));
return $props;
} }
/** /**

View File

@ -111,14 +111,21 @@ class infolog_ical extends infolog_bo
/** /**
* Exports one InfoLog tast to an iCalendar VTODO * Exports one InfoLog tast to an iCalendar VTODO
* *
* @param int $_taskID info_id * @param int|array $task infolog_id or infolog-tasks data
* @param string $_version='2.0' could be '1.0' too * @param string $_version='2.0' could be '1.0' too
* @param string $_method='PUBLISH' * @param string $_method='PUBLISH'
* @return string/boolean string with vCal or false on error (eg. no permission to read the event) * @return string/boolean string with vCal or false on error (eg. no permission to read the event)
*/ */
function exportVTODO($_taskID, $_version='2.0',$_method='PUBLISH') function exportVTODO($task, $_version='2.0',$_method='PUBLISH')
{ {
$taskData = $this->read($_taskID, true, 'server'); if (is_array($task))
{
$taskData = $task;
}
else
{
if (!($taskData = $this->read($task, true, 'server'))) return false;
}
if ($taskData['info_id_parent']) if ($taskData['info_id_parent'])
{ {
@ -151,6 +158,12 @@ class infolog_ical extends infolog_bo
$taskData = $GLOBALS['egw']->translation->convert($taskData, $taskData = $GLOBALS['egw']->translation->convert($taskData,
$GLOBALS['egw']->translation->charset(), 'UTF-8'); $GLOBALS['egw']->translation->charset(), 'UTF-8');
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
array2string($taskData)."\n",3,$this->logfile);
}
$vcal = new Horde_iCalendar; $vcal = new Horde_iCalendar;
$vcal->setAttribute('VERSION',$_version); $vcal->setAttribute('VERSION',$_version);
$vcal->setAttribute('METHOD',$_method); $vcal->setAttribute('METHOD',$_method);
@ -407,9 +420,10 @@ class infolog_ical extends infolog_bo
* @param string $_vcalData * @param string $_vcalData
* @param int $_taskID=-1 info_id, default -1 = new entry * @param int $_taskID=-1 info_id, default -1 = new entry
* @param boolean $merge=false merge data with existing entry * @param boolean $merge=false merge data with existing entry
* @param int $user=null delegate new task to this account_id, default null
* @return int|boolean integer info_id or false on error * @return int|boolean integer info_id or false on error
*/ */
function importVTODO(&$_vcalData, $_taskID=-1, $merge=false) function importVTODO(&$_vcalData, $_taskID=-1, $merge=false, $user=null)
{ {
if ($this->tzid) if ($this->tzid)
@ -438,6 +452,11 @@ class infolog_ical extends infolog_bo
$taskData['info_datecompleted'] = 0; $taskData['info_datecompleted'] = 0;
} }
if (!is_null($user))
{
$taskData['info_owner'] = $user;
}
if ($this->log) if ($this->log)
{ {
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
@ -615,7 +634,7 @@ class infolog_ical extends infolog_bo
// check if we (still) have X-INFOLOG-STATUS set AND it would give an unchanged status (no change by the user) // check if we (still) have X-INFOLOG-STATUS set AND it would give an unchanged status (no change by the user)
foreach ($component->_attributes as $attr) foreach ($component->_attributes as $attr)
{ {
if ($attr['name'] == 'X-INFOLOG-STATUS') break; if ($attr['name'] == 'X-INFOLOG-STATUS') break;
} }
$taskData['info_status'] = $this->vtodo2status($attribute['value'], $taskData['info_status'] = $this->vtodo2status($attribute['value'],
$attr['name'] == 'X-INFOLOG-STATUS' ? $attr['value'] : null); $attr['name'] == 'X-INFOLOG-STATUS' ? $attr['value'] : null);
@ -668,7 +687,8 @@ class infolog_ical extends infolog_bo
*/ */
function exportVNOTE($_noteID, $_type) function exportVNOTE($_noteID, $_type)
{ {
$note = $this->read($_noteID, true, 'server'); if(!($note = $this->read($_noteID, true, 'server'))) return false;
$note = $GLOBALS['egw']->translation->convert($note, $note = $GLOBALS['egw']->translation->convert($note,
$GLOBALS['egw']->translation->charset(), 'UTF-8'); $GLOBALS['egw']->translation->charset(), 'UTF-8');

View File

@ -121,7 +121,7 @@ class infolog_sif extends infolog_bo
* *
* @param int|string|DateTime $time in server-time as returned by calendar_bo for $data_format='server' * @param int|string|DateTime $time in server-time as returned by calendar_bo for $data_format='server'
* @param string $tzid TZID of event or 'UTC' or NULL for palmos timestamps in usertime * @param string $tzid TZID of event or 'UTC' or NULL for palmos timestamps in usertime
* @return mixed attribute value to set: integer timestamp if $tzid == 'UTC' otherwise Ymd\THis string IN $tzid *
*/ */
function getDateTime($time, $tzid) function getDateTime($time, $tzid)
{ {
@ -142,12 +142,14 @@ class infolog_sif extends infolog_bo
{ {
$arr = egw_time::to($time, 'array'); $arr = egw_time::to($time, 'array');
$time = new egw_time($arr, self::$tz_cache[$tzid]); $time = new egw_time($arr, self::$tz_cache[$tzid]);
$value = $time->format('Y-m-d');
} }
else else
{ {
$time->setTimezone(self::$tz_cache[$tzid]); $time->setTimezone(self::$tz_cache[$tzid]);
$value = $time->format('Ymd\THis');
} }
return $time->format('Ymd\THis'); return $value;
} }
function startElement($_parser, $_tag, $_attributes) function startElement($_parser, $_tag, $_attributes)
@ -509,12 +511,12 @@ class infolog_sif extends infolog_bo
case 'DueDate': case 'DueDate':
case 'StartDate': case 'StartDate':
$sifTask .= '<$sifField>'; $sifTask .= "<$sifField>";
if (!empty($value)) if (!empty($value))
{ {
$sifTask .= $this->getDateTime($value, $this->tzid); $sifTask .= $this->getDateTime($value, $this->tzid);
} }
$sifTask .= '</$sifField>'; $sifTask .= "</$sifField>";
break; break;
case 'Importance': case 'Importance':

View File

@ -36,6 +36,10 @@ require_once('HTTP/WebDAV/Server.php');
*/ */
class groupdav extends HTTP_WebDAV_Server class groupdav extends HTTP_WebDAV_Server
{ {
/**
* DAV namespace
*/
const DAV = 'DAV:';
/** /**
* GroupDAV namespace * GroupDAV namespace
*/ */
@ -70,7 +74,7 @@ class groupdav extends HTTP_WebDAV_Server
'component-set' => array(self::GROUPDAV => 'VCARD'), 'component-set' => array(self::GROUPDAV => 'VCARD'),
), ),
'infolog' => array( 'infolog' => array(
'resourcetype' => array(self::GROUPDAV => 'vtodo-collection'), 'resourcetype' => array(self::GROUPDAV => 'vtodo-collection', self::CALDAV => 'calendar'),
'component-set' => array(self::GROUPDAV => 'VTODO'), 'component-set' => array(self::GROUPDAV => 'VTODO'),
), ),
); );
@ -81,7 +85,7 @@ class groupdav extends HTTP_WebDAV_Server
* *
* @var integer * @var integer
*/ */
var $debug = 0; var $debug = 3;
/** /**
* eGW's charset * eGW's charset
@ -101,6 +105,19 @@ class groupdav extends HTTP_WebDAV_Server
* @var groupdav_handler * @var groupdav_handler
*/ */
var $handler; var $handler;
/**
* principal URL
*
* @var string
*/
var $principalURL;
/**
* Reference to the accounts class
*
* @var accounts
*/
var $accounts;
function __construct() function __construct()
{ {
@ -115,11 +132,24 @@ class groupdav extends HTTP_WebDAV_Server
case 'davkit': // iCal app in OS X 10.6 created wrong request, if full url given case 'davkit': // iCal app in OS X 10.6 created wrong request, if full url given
$this->client_require_href_as_url = false; $this->client_require_href_as_url = false;
break; break;
case 'cfnetwork':
$this->crrnd = true; // Apple Addressbook.app does not cope with namespace redundancy
} }
parent::HTTP_WebDAV_Server(); parent::HTTP_WebDAV_Server();
$this->translation =& $GLOBALS['egw']->translation; $this->translation =& $GLOBALS['egw']->translation;
$this->egw_charset = $this->translation->charset(); $this->egw_charset = $this->translation->charset();
if (strpos($this->base_uri, 'http') === 0)
{
$this->principalURL = $this->_slashify($this->base_uri);
}
else
{
$this->principalURL = (@$_SERVER["HTTPS"] === "on" ? "https:" : "http:") .
'//' . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . '/';
}
$this->principalURL .= 'principals/users/'.$GLOBALS['egw_info']['user']['account_lid'].'/';
$this->accounts = $GLOBALS['egw']->accounts;
} }
/** /**
@ -130,7 +160,7 @@ class groupdav extends HTTP_WebDAV_Server
*/ */
function app_handler($app) function app_handler($app)
{ {
return groupdav_handler::app_handler($app,$this->debug,$this->base_uri); return groupdav_handler::app_handler($app,$this->debug,$this->base_uri,$this->principalURL);
} }
/** /**
@ -146,11 +176,24 @@ class groupdav extends HTTP_WebDAV_Server
switch($app) switch($app)
{ {
case 'calendar': case 'calendar':
$dav[] = 2;
$dav[] = 'access-control';
$dav[] = 'calendar-access'; $dav[] = 'calendar-access';
//$dav[] = 'calendar-schedule';
//$dav[] = 'calendar-proxy';
//$dav[] = 'calendar-avialibility';
//$dav[] = 'calendarserver-private-events';
break; break;
case 'addressbook': case 'addressbook':
$dav[] = 'addressbook'; $dav[] = 2;
$dav[] = 3;
$dav[] = 'access-control';
$dav[] = 'addressbook-access';
break; break;
default:
$dav[] = 2;
$dav[] = 'access-control';
$dav[] = 'calendar-access';
} }
// not yet implemented: $dav[] = 'access-control'; // not yet implemented: $dav[] = 'access-control';
} }
@ -162,62 +205,101 @@ class groupdav extends HTTP_WebDAV_Server
* @param array return array for file properties * @param array return array for file properties
* @return bool true on success * @return bool true on success
*/ */
function PROPFIND(&$options, &$files,$method='PROPFIND') function PROPFIND(&$options, &$files, $method='PROPFIND')
{ {
if ($this->debug) error_log(__CLASS__."::$method(".array2string($options,true).')'); if ($this->debug) error_log(__CLASS__."::$method(".array2string($options,true).')');
if (groupdav_handler::get_agent() == 'cfnetwork' && // Apple Addressbook
$options['root']['name'] == 'propfind')
{
foreach ($options['props'] as $props)
{
if ($props['name'] == 'current-user-privilege-set')
{
if ($this->debug > 2) error_log(__CLASS__."::$method: current-user-privilege-set not implemented!");
return '501 Not Implemented';
}
}
}
// parse path in form [/account_lid]/app[/more] // parse path in form [/account_lid]/app[/more]
if (!self::_parse_path($options['path'],$id,$app,$user,$user_prefix) && $app && !$user) if (!self::_parse_path($options['path'],$id,$app,$user,$user_prefix) && $app && !$user)
{ {
if ($this->debug > 1) error_log(__CLASS__."::$method: user=$user, app=$app, id=$id: 404 not found!"); if ($this->debug > 1) error_log(__CLASS__."::$method: user='$user', app='$app', id='$id': 404 not found!");
return '404 Not Found'; return '404 Not Found';
} }
if ($this->debug > 1) error_log(__CLASS__."::$method: user=$user, app='$app', id=$id"); if ($this->debug > 1) error_log(__CLASS__."::$method: user='$user', app='$app', id='$id'");
if ($user)
{
$account_lid = $this->accounts->id2name($user);
}
else
{
$account_lid = $GLOBALS['egw_info']['user']['account_lid'];
}
$account = $this->accounts->read($account_lid);
$displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'],
$GLOBALS['egw']->translation->charset(),'utf-8');
$files = array('files' => array()); $files = array('files' => array());
$path = $user_prefix = $this->_slashify($user_prefix);
if (!$app) // root folder containing apps if (!$app) // user root folder containing apps
{ {
if (empty($user_prefix))
{
$user_prefix = '/'.$GLOBALS['egw_info']['user']['account_lid'].'/';
}
if ($options['depth'])
{
$displayname = 'EGroupware (Cal|Card|Group)DAV server';
}
// self url // self url
$files['files'][] = array( $props = array(
'path' => $user_prefix.'/', self::mkprop('displayname',$displayname),
'props' => array( self::mkprop('resourcetype',array(self::mkprop('collection',''))),
self::mkprop('displayname','EGroupware (Cal|Card|Group)DAV server'),
self::mkprop('resourcetype','collection'),
// adding the calendar extra property (calendar-home-set, etc.) here, allows apple iCal to "autodetect" the URL // adding the calendar extra property (calendar-home-set, etc.) here, allows apple iCal to "autodetect" the URL
self::mkprop(groupdav::CALDAV,'calendar-home-set',$this->base_uri.'/calendar/'), self::mkprop(groupdav::CALDAV,'calendar-home-set',array(
self::mkprop('current-user-principal',array(self::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))), self::mkprop('href',$this->base_uri.$user_prefix.'calendar/'))),
), self::mkprop(groupdav::CARDDAV,'addressbook-home-set',array(
self::mkprop('href',$this->base_uri.$user_prefix))),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))),
self::mkprop(groupdav::CALDAV,'calendar-user-address-set',array(
self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))),
//self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))),
//self::mkprop('principal-collection-set',array(self::mkprop('href',$this->base_uri.'/principals/'))),
);
//$props = self::current_user_privilege_set($props);
$files['files'][] = array(
'path' => $path,
'props' => $props,
); );
if ($options['depth']) if ($options['depth'])
{ {
if (empty($user_prefix)) if (strlen($path) == 1) // GroupDAV Root
{ {
// principals collection // principals collection
$files['files'][] = array( $files['files'][] = array(
'path' => '/principals/', 'path' => '/principals/',
'props' => array( 'props' => array(
self::mkprop('displayname',lang('Accounts')), self::mkprop('displayname',lang('Accounts')),
self::mkprop('resourcetype','collection'), self::mkprop('resourcetype',array(self::mkprop('collection',''))),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))), self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))),
), self::mkprop(groupdav::CALDAV,'calendar-home-set',array(
); self::mkprop('href',$this->base_uri.$user_prefix.'calendar/'))),
// groups collection self::mkprop(groupdav::CARDDAV,'addressbook-home-set',array(
$files['files'][] = array( self::mkprop('href',$this->base_uri.'/'))),
'path' => '/groups/', self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))),
'props' => array( ),
self::mkprop('displayname',lang('Groups')), );
self::mkprop('resourcetype','collection'),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))),
),
);
} }
foreach($this->root as $app => $data) foreach($this->root as $app => $data)
{ {
if (!$GLOBALS['egw_info']['user']['apps'][$app]) continue; // no rights for the given app if (!$GLOBALS['egw_info']['user']['apps'][$app]) continue; // no rights for the given app
$files['files'][] = array( $files['files'][] = array(
'path' => $user_prefix.'/'.$app.'/', 'path' => $path.$app.'/',
'props' => $this->_properties($app,false,$user), 'props' => $this->_properties($app,false,$user),
); );
} }
@ -234,12 +316,12 @@ class groupdav extends HTTP_WebDAV_Server
if ($method != 'REPORT' && !$id) // no self URL for REPORT requests (only PROPFIND) or propfinds on an id if ($method != 'REPORT' && !$id) // no self URL for REPORT requests (only PROPFIND) or propfinds on an id
{ {
$files['files'][0] = array( $files['files'][0] = array(
'path' => '/'.$app.'/', 'path' => $path.$app.'/',
// KAddressbook doubles the folder, if the self URL contains the GroupDAV/CalDAV resourcetypes // KAddressbook doubles the folder, if the self URL contains the GroupDAV/CalDAV resourcetypes
'props' => $this->_properties($app,$app=='addressbook'&&strpos($_SERVER['HTTP_USER_AGENT'],'KHTML') !== false), 'props' => $this->_properties($app,$app=='addressbook'&&strpos($_SERVER['HTTP_USER_AGENT'],'KHTML') !== false,$user),
); );
} }
if (!$options['depth'] && !$id) if (isset($options['depth']) && !$options['depth'] && !$id)
{ {
// add ctag if handler implements it (only for depth 0) // add ctag if handler implements it (only for depth 0)
if (method_exists($handler,'getctag')) if (method_exists($handler,'getctag'))
@ -249,7 +331,7 @@ class groupdav extends HTTP_WebDAV_Server
} }
return true; // depth 0 --> show only the self url return true; // depth 0 --> show only the self url
} }
return $handler->propfind($options['path'],$options,$files,$user,$id); return $handler->propfind($this->_slashify($options['path']),$options,$files,$user,$id);
} }
return '501 Not Implemented'; return '501 Not Implemented';
} }
@ -264,12 +346,54 @@ class groupdav extends HTTP_WebDAV_Server
*/ */
function _properties($app,$no_extra_types=false,$user=null) function _properties($app,$no_extra_types=false,$user=null)
{ {
if (!$user) $user = $GLOBALS['egw_info']['user']['account_fullname']; if ($this->debug) error_log(__CLASS__."::$method: user='$user', app='$app'");
if ($user)
{
$account_lid = $this->accounts->id2name($user);
}
else
{
$account_lid = $GLOBALS['egw_info']['user']['account_lid'];
}
$account = $this->accounts->read($account_lid);
$displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'],
$GLOBALS['egw']->translation->charset(),'utf-8');
$props = array( $props = array(
self::mkprop('displayname',$this->translation->convert(lang($app).' '.common::grab_owner_name($user),$this->egw_charset,'utf-8')), self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))), self::mkprop('owner',$displayname),
); self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))),
self::mkprop('alternate-URI-set',array(
self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))),
self::mkprop(groupdav::CALDAV,'calendar-user-address-set',array(
self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))),
self::mkprop('principal-collection-set',array(
self::mkprop('href',$this->base_uri.'/principals/users/'),
self::mkprop('href',$this->base_uri.'/principals/groups/'),
)),
);
switch ($app)
{
case 'calendar':
$props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array(
self::mkprop('href',$this->base_uri.'/'.$account_lid.'/calendar/')));
break;
case 'infolog':
$props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array(
self::mkprop('href',$this->base_uri.'/'.$account_lid.'/infolog/')));
$displayname = $this->translation->convert(lang($app).' '.
common::grab_owner_name($user),$this->egw_charset,'utf-8');
break;
default:
$props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array(
self::mkprop('href',$this->base_uri.'/'.$account_lid.'/calendar/')));
$displayname = $this->translation->convert(lang($app).' '.
common::grab_owner_name($user),$this->egw_charset,'utf-8');
}
$props[] = self::mkprop(groupdav::CARDDAV,'addressbook-home-set',array(
self::mkprop('href',$this->base_uri.'/'.$account_lid.'/')));
$props[] = self::mkprop('displayname',$displayname);
foreach((array)$this->root[$app] as $prop => $values) foreach((array)$this->root[$app] as $prop => $values)
{ {
if ($prop == 'resourcetype') if ($prop == 'resourcetype')
@ -296,7 +420,9 @@ class groupdav extends HTTP_WebDAV_Server
} }
if (method_exists($app.'_groupdav','extra_properties')) if (method_exists($app.'_groupdav','extra_properties'))
{ {
$props = ExecMethod($app.'_groupdav::extra_properties',$props); $displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'],
$GLOBALS['egw']->translation->charset(),'utf-8');
$props = ExecMethod2($app.'_groupdav::extra_properties',$props,$displayname,$this->base_uri);
} }
return $props; return $props;
} }
@ -401,8 +527,7 @@ class groupdav extends HTTP_WebDAV_Server
$props = $this->props2array($file['props']); $props = $this->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';
$name = $this->_slashify(basename($this->_unslashify($file['path'])));
/*
if (substr($file['path'],-1) == '/') if (substr($file['path'],-1) == '/')
{ {
$name = basename(substr($file['path'],0,-1)).'/'; $name = basename(substr($file['path'],0,-1)).'/';
@ -411,7 +536,7 @@ class groupdav extends HTTP_WebDAV_Server
{ {
$name = basename($file['path']); $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<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";
@ -664,6 +789,34 @@ class groupdav extends HTTP_WebDAV_Server
return egw_vfs::checkLock($path); return egw_vfs::checkLock($path);
} }
/**
* ACL method handler
*
* @param array general parameter passing array
* @return string HTTP status
*/
function ACL(&$options)
{
self::_parse_path($options['path'],$id,$app,$user);
if ($this->debug) error_log(__METHOD__.'('.array2string($options).") path=$path");
$options['errors'] = array();
switch ($app)
{
case 'calendar':
case 'addressbook':
case 'infolog':
$status = '200 OK'; // grant all
break;
default:
$options['errors'][] = 'no-inherited-ace-conflict';
$status = '403 Forbidden';
}
return $status;
}
/** /**
* Parse a path into it's id, app and user parts * Parse a path into it's id, app and user parts
* *
@ -686,7 +839,7 @@ class groupdav extends HTTP_WebDAV_Server
} }
$parts = explode('/', $this->_unslashify($path)); $parts = explode('/', $this->_unslashify($path));
if ($GLOBALS['egw']->accounts->name2id($parts[0])) if ($this->accounts->name2id($parts[0]))
{ {
// /$user/$app/... // /$user/$app/...
$user = array_shift($parts); $user = array_shift($parts);
@ -697,7 +850,7 @@ class groupdav extends HTTP_WebDAV_Server
if ($user) if ($user)
{ {
$user_prefix = '/'.$user; $user_prefix = '/'.$user;
$user = $GLOBALS['egw']->accounts->name2id($user,'account_lid',$app != 'addressbook' ? 'u' : null); $user = $this->accounts->name2id($user,'account_lid',$app != 'addressbook' ? 'u' : null);
} }
else else
{ {
@ -710,13 +863,39 @@ class groupdav extends HTTP_WebDAV_Server
list($id) = explode('.',$id); // remove evtl. .ics extension list($id) = explode('.',$id); // remove evtl. .ics extension
} }
if (!($ok = $id && in_array($app,array('addressbook','calendar','infolog','principals','groups')) && $user)) $ok = $id && $user && in_array($app,array('addressbook','calendar','infolog','principals','groups'));
if ($this->debug)
{ {
if ($this->debug) error_log(__METHOD__."('$path') returning " . ($ok ? 'true' : 'false') . ": id='$id', app='$app', user='$user', user_prefix='$user_prefix'");
{
error_log(__METHOD__."('$path') returning false: id=$id, app='$app', user=$user");
}
} }
return $ok; return $ok;
} }
/**
* Add the privileges of the current user
*
* @param array $props=array() regular props by the groupdav handler
* @return array
*/
static function current_user_privilege_set(array $props=array())
{
$props[] = HTTP_WebDAV_Server::mkprop('current-user-privilege-set',
array(HTTP_WebDAV_Server::mkprop('privilege',
array(//HTTP_WebDAV_Server::mkprop('all',''),
HTTP_WebDAV_Server::mkprop('read',''),
HTTP_WebDAV_Server::mkprop('read-free-busy',''),
//HTTP_WebDAV_Server::mkprop('read-current-user-privilege-set',''),
HTTP_WebDAV_Server::mkprop('bind',''),
HTTP_WebDAV_Server::mkprop('unbind',''),
HTTP_WebDAV_Server::mkprop('schedule-post',''),
HTTP_WebDAV_Server::mkprop('schedule-post-vevent',''),
HTTP_WebDAV_Server::mkprop('schedule-respond',''),
HTTP_WebDAV_Server::mkprop('schedule-respond-vevent',''),
HTTP_WebDAV_Server::mkprop('schedule-deliver',''),
HTTP_WebDAV_Server::mkprop('schedule-deliver-vevent',''),
HTTP_WebDAV_Server::mkprop('write',''),
HTTP_WebDAV_Server::mkprop('write-properties',''),
HTTP_WebDAV_Server::mkprop('write-content',''),
))));
return $props;
}
} }

View File

@ -37,6 +37,12 @@ abstract class groupdav_handler
* @var translation * @var translation
*/ */
var $translation; var $translation;
/**
* Reference to the accounts class
*
* @var accounts
*/
var $accounts;
/** /**
* Translates method names into ACL bits * Translates method names into ACL bits
* *
@ -59,6 +65,12 @@ abstract class groupdav_handler
* @var string * @var string
*/ */
var $base_uri; var $base_uri;
/**
* principal URL
*
* @var string
*/
var $principalURL;
/** /**
* HTTP_IF_MATCH / etag of current request / last call to _common_get_put_delete() method * HTTP_IF_MATCH / etag of current request / last call to _common_get_put_delete() method
* *
@ -78,17 +90,29 @@ abstract class groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog' * @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set * @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler * @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
*/ */
function __construct($app,$debug=null,$base_uri=null) function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
{ {
//error_log(__METHOD__." called");
$this->app = $app; $this->app = $app;
#if (!is_null($debug)) $this->debug = $debug = 3; if (!is_null($debug)) $this->debug = $debug;
$this->base_uri = is_null($base_uri) ? $base_uri : $_SERVER['SCRIPT_NAME']; $this->base_uri = is_null($base_uri) ? $base_uri : $_SERVER['SCRIPT_NAME'];
if (is_null($principalURL))
{
$this->principalURL = (@$_SERVER["HTTPS"] === "on" ? "https:" : "http:") .
'//'.$_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . '/';
}
else
{
$this->principalURL = $principalURL.'principals/users/'.
$GLOBALS['egw_info']['user']['account_lid'].'/';
}
$this->agent = self::get_agent(); $this->agent = self::get_agent();
$this->translation =& $GLOBALS['egw']->translation; $this->translation =& $GLOBALS['egw']->translation;
$this->egw_charset = $this->translation->charset(); $this->egw_charset = $this->translation->charset();
$this->accounts = $GLOBALS['egw']->accounts;
} }
/** /**
@ -105,12 +129,13 @@ abstract class groupdav_handler
/** /**
* Propfind callback, if interator is used * Propfind callback, if interator is used
* *
* @param string $path
* @param array $filter * @param array $filter
* @param array|boolean $start false=return all or array(start,num) * @param array|boolean $start false=return all or array(start,num)
* @param int &$total * @param int &$total
* @return array with "files" array with values for keys path and props * @return array with "files" array with values for keys path and props
*/ */
function &propfind_callback(array $filter,$start,&$total) { } function &propfind_callback($path, array $filter,$start,&$total) { }
/** /**
* Handle get request for an applications entry * Handle get request for an applications entry
@ -161,9 +186,11 @@ abstract class groupdav_handler
* Add extra properties for collections * Add extra properties for collections
* *
* @param array $props=array() regular props by the groupdav handler * @param array $props=array() regular props by the groupdav handler
* @param string $displayname
* @param string $base_uri=null base url of handler
* @return array * @return array
*/ */
static function extra_properties(array $props=array()) static function extra_properties(array $props=array(), $displayname, $base_uri=null)
{ {
return $props; return $props;
} }
@ -185,7 +212,7 @@ abstract class groupdav_handler
// error_log(__METHOD__."(".array2string($entry).") Cant create etag!"); // error_log(__METHOD__."(".array2string($entry).") Cant create etag!");
return false; return false;
} }
return '"'.$entry['id'].':'.(isset($entry['etag']) ? $entry['etag'] : $entry['modified']).'"'; return 'EGw-'.$entry['id'].':'.(isset($entry['etag']) ? $entry['etag'] : $entry['modified']).'-wGE';
} }
/** /**
@ -196,7 +223,7 @@ abstract class groupdav_handler
*/ */
static function etag2value($etag) static function etag2value($etag)
{ {
list(,$val) = explode(':',substr($etag,1,-1),2); list(,$val) = explode(':',substr($etag,4,-4),2);
return $val; return $val;
} }
@ -211,7 +238,7 @@ abstract class groupdav_handler
* @param array &$options * @param array &$options
* @param int $id * @param int $id
* @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 * @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 * @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)
{ {
@ -260,11 +287,13 @@ abstract class groupdav_handler
* *
* @static * @static
* @param string $app 'calendar', 'addressbook' or 'infolog' * @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $user=null owner of the collection, default current user
* @param int $debug=null debug-level to set * @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler * @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
* @return groupdav_handler * @return groupdav_handler
*/ */
static function &app_handler($app,$debug=null,$base_uri=null) static function &app_handler($app,$debug=null,$base_uri=null,$principalURL=null)
{ {
static $handler_cache = array(); static $handler_cache = array();
@ -273,8 +302,14 @@ abstract class groupdav_handler
$class = $app.'_groupdav'; $class = $app.'_groupdav';
if (!class_exists($class) && !class_exists($class = 'groupdav_'.$app)) return null; if (!class_exists($class) && !class_exists($class = 'groupdav_'.$app)) return null;
$handler_cache[$app] = new $class($app,$debug,$base_uri); $handler_cache[$app] = new $class($app,$debug,$base_uri,$principalURL);
} }
$handler_cache[$app]->$debug = $debug;
$handler_cache[$app]->$base_uri = $base_uri;
$handler_cache[$app]->$principalURL = $principalURL;
if ($debug) error_log(__METHOD__."('$app', '$base_uri', '$principalURL')");
return $handler_cache[$app]; return $handler_cache[$app];
} }
@ -294,6 +329,7 @@ abstract class groupdav_handler
$user_agent = strtolower($_SERVER['HTTP_USER_AGENT']); $user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
foreach(array( foreach(array(
'davkit' => 'davkit', // Apple iCal 'davkit' => 'davkit', // Apple iCal
'cfnetwork' => 'cfnetwork', // Apple Addressbook
'bionicmessage.net' => 'funambol', // funambol GroupDAV connector from bionicmessage.net 'bionicmessage.net' => 'funambol', // funambol GroupDAV connector from bionicmessage.net
'zideone' => 'zideone', // zideone outlook plugin 'zideone' => 'zideone', // zideone outlook plugin
'lightning' => 'lightning', // Lighting (SOGo connector for addressbook) 'lightning' => 'lightning', // Lighting (SOGo connector for addressbook)
@ -333,6 +369,13 @@ abstract class groupdav_handler
*/ */
class groupdav_propfind_iterator implements Iterator class groupdav_propfind_iterator implements Iterator
{ {
/**
* current path
*
* @var string
*/
protected $path;
/** /**
* Handler to call for entries * Handler to call for entries
* *
@ -352,8 +395,16 @@ class groupdav_propfind_iterator implements Iterator
* *
* @var array * @var array
*/ */
protected $common_files;
/**
* current chunk
*
* @var array
*/
protected $files; protected $files;
/** /**
* Start value for callback * Start value for callback
* *
@ -374,6 +425,8 @@ class groupdav_propfind_iterator implements Iterator
*/ */
public $debug = false; public $debug = false;
/**
/** /**
* Constructor * Constructor
* *
@ -381,13 +434,14 @@ class groupdav_propfind_iterator implements Iterator
* @param array $filter filter for propfind call * @param array $filter filter for propfind call
* @param array $files=null extra files/responses to return too * @param array $files=null extra files/responses to return too
*/ */
public function __construct(groupdav_handler $handler,array $filter,array &$files=null) public function __construct(groupdav_handler $handler, $path, array $filter,array &$files=null)
{ {
if ($this->debug) error_log(__METHOD__."(,".array2string($filter).",)"); if ($this->debug) error_log(__METHOD__."('$path', ".array2string($filter).",)");
$this->path = $path;
$this->handler = $handler; $this->handler = $handler;
$this->filter = $filter; $this->filter = $filter;
$this->files = $files; $this->files = $files;
$this->common_files = $files;
reset($this->files); reset($this->files);
} }
@ -399,7 +453,6 @@ class groupdav_propfind_iterator implements Iterator
public function current() public function current()
{ {
if ($this->debug) error_log(__METHOD__."() returning ".array2string(current($this->files))); if ($this->debug) error_log(__METHOD__."() returning ".array2string(current($this->files)));
return current($this->files); return current($this->files);
} }
@ -410,7 +463,7 @@ class groupdav_propfind_iterator implements Iterator
*/ */
public function key() public function key()
{ {
$current = $this->current(); $current = current($this->files);
if ($this->debug) error_log(__METHOD__."() returning ".array2string($current['path'])); if ($this->debug) error_log(__METHOD__."() returning ".array2string($current['path']));
return $current['path']; // we return path as key return $current['path']; // we return path as key
@ -424,21 +477,20 @@ class groupdav_propfind_iterator implements Iterator
if (next($this->files) !== false) if (next($this->files) !== false)
{ {
if ($this->debug) error_log(__METHOD__."() returning TRUE"); if ($this->debug) error_log(__METHOD__."() returning TRUE");
return true; return true;
} }
if (is_array($this->files) && count($this->files) < self::CHUNK_SIZE) // less entries then asked --> no further available if (is_array($this->files) && count($this->files) < self::CHUNK_SIZE) // less entries then asked --> no further available
{ {
if ($this->debug) error_log(__METHOD__."() returning FALSE (no more entries)"); if ($this->debug) error_log(__METHOD__."() returning FALSE (no more entries)");
return false; // no further entries return false; // no further entries
} }
// try query further files via propfind callback of handler and store result in $this->files // 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->files = $this->handler->propfind_callback($this->path,$this->filter,array($this->start,self::CHUNK_SIZE));
$this->start += self::CHUNK_SIZE; $this->start += self::CHUNK_SIZE;
reset($this->files); reset($this->files);
if ($this->debug) error_log(__METHOD__."() returning ".array2string(current($this->files) !== false)); if ($this->debug) error_log(__METHOD__."() returning ".array2string(current($this->files) !== false));
return current($this->files) !== false; return current($this->files) !== false;
} }
@ -451,7 +503,8 @@ class groupdav_propfind_iterator implements Iterator
// query first set of files via propfind callback of handler and store result in $this->files // query first set of files via propfind callback of handler and store result in $this->files
$this->start = 0; $this->start = 0;
$this->files = $this->handler->propfind_callback($this->filter,array($this->start,self::CHUNK_SIZE)); $files = $this->handler->propfind_callback($this->path,$this->filter,array($this->start,self::CHUNK_SIZE));
$this->files = $this->common_files + $files;
$this->start += self::CHUNK_SIZE; $this->start += self::CHUNK_SIZE;
reset($this->files); reset($this->files);
} }

View File

@ -7,7 +7,7 @@
* @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) 2008 by Ralf Becker <RalfBecker-AT-outdoor-training.de> * @copyright (c) 2008-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$ * @version $Id$
*/ */
@ -16,12 +16,6 @@
*/ */
class groupdav_principals extends groupdav_handler class groupdav_principals extends groupdav_handler
{ {
/**
* Reference to the accounts class
*
* @var accounts
*/
var $accounts;
/** /**
* Constructor * Constructor
@ -29,12 +23,11 @@ class groupdav_principals extends groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog' * @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set * @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler * @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
*/ */
function __construct($app,$debug=null,$base_uri=null) function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
{ {
parent::__construct($app,$debug,$base_uri); parent::__construct($app,$debug,$base_uri,$principalURL);
$this->accounts = $GLOBALS['egw']->accounts;
} }
/** /**
@ -48,6 +41,40 @@ class groupdav_principals extends groupdav_handler
*/ */
function propfind($path,$options,&$files,$user) function propfind($path,$options,&$files,$user)
{ {
list(,$principals,$type,$name,$rest) = explode('/',$path,5);
// /principals/users/$name/
// /users/$name/calendar-proxy-read/
// /users/$name/calendar-proxy-write/
// /groups/$name/
// /resources/$resource/
// /__uids__/$uid/.../
switch($type)
{
case 'users':
$files['files'] = $this->propfind_users($name,$rest,$options);
break;
case 'groups':
$files['files'] = $this->propfind_groups($name,$rest,$options);
break;
case 'resources':
$files['files'] = $this->propfind_resources($name,$rest,$options);
break;
case '__uids__':
$files['files'] = $this->propfind_uids($name,$rest,$options);
break;
case '':
$files['files'] = $this->propfind_principals($options);
break;
default:
return '404 Not Found';
}
if (!is_array($files['files']))
{
return $files['files'];
}
return true;
list(,,$id) = explode('/',$path); list(,,$id) = explode('/',$path);
if ($id && !($id = $this->accounts->id2name($id))) if ($id && !($id = $this->accounts->id2name($id)))
{ {
@ -55,18 +82,20 @@ class groupdav_principals extends groupdav_handler
} }
foreach($id ? array($this->accounts->read($id)) : $this->accounts->search(array('type' => 'accounts')) as $account) foreach($id ? array($this->accounts->read($id)) : $this->accounts->search(array('type' => 'accounts')) as $account)
{ {
$props = array( $displayname = $this->translation->convert($account['account_fullname'],
HTTP_WebDAV_Server::mkprop('displayname',trim($account['account_firstname'].' '.$account['account_lastname'])), $this->translation->charset(),'utf-8');
$props = array(
HTTP_WebDAV_Server::mkprop('displayname',$displayname),
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)), HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)),
HTTP_WebDAV_Server::mkprop('resourcetype','principal'), HTTP_WebDAV_Server::mkprop('resourcetype','principal'),
HTTP_WebDAV_Server::mkprop('alternate-URI-set',''), HTTP_WebDAV_Server::mkprop('alternate-URI-set',''),
HTTP_WebDAV_Server::mkprop('principal-URL',$_SERVER['SCRIPT_NAME'].'/principals/'.$account['account_lid']), HTTP_WebDAV_Server::mkprop('principal-URL',$this->base_uri.'/principals/'.$account['account_lid']),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$_SERVER['SCRIPT_NAME'].'/'), HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$this->base_uri.$account['account_lid'].'/calendar/'),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$account['account_email']), HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$account['account_email']),
); );
foreach($this->accounts->memberships($account['account_id']) as $gid => $group) foreach($this->accounts->memberships($account['account_id']) as $gid => $group)
{ {
$props[] = HTTP_WebDAV_Server::mkprop('group-membership',$_SERVER['SCRIPT_NAME'].'/groups/'.$group); $props[] = HTTP_WebDAV_Server::mkprop('group-membership',$this->base_uri.'/groups/'.$group);
} }
$files['files'][] = array( $files['files'][] = array(
'path' => '/principals/'.$account['account_lid'], 'path' => '/principals/'.$account['account_lid'],
@ -74,7 +103,251 @@ class groupdav_principals extends groupdav_handler
); );
if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/".$account['account_lid'].', props='.array2string($props)); if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/".$account['account_lid'].', props='.array2string($props));
} }
return true; return files;
}
/**
* Do propfind in /pricipals/users
*
* @param string $name name of account or empty
* @param string $rest rest of path behind account-name
* @param array $options
* @return array|string array with files or HTTP error code
*/
protected function propfind_users($name,$rest,array $options)
{
//echo "<p>".__METHOD__."($name,$rest,".array2string($options).")</p>\n";
if (empty($name))
{
$files = array();
// add /pricipals/users/ entry
$files[] = $this->add_collection('/principals/users/',array(
HTTP_WebDAV_Server::mkprop('current-user-principal',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))),
));
if ($options['depth'])
{
// add all users
foreach($this->accounts->search(array('type' => 'accounts')) as $account)
{
$files[] = $this->add_account($account);
}
}
}
else
{
if (!($id = $this->accounts->name2id($name,'account_lid','u')) ||
!($account = $this->accounts->read($id)))
{
return '404 Not Found';
}
switch((string)$rest)
{
case '':
$files[] = $this->add_account($account);
$files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/calendar-proxy-read');
$files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/calendar-proxy-write');
break;
case 'calendar-proxy-read':
case 'calendar-proxy-write':
$files = array();
$files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/'.$rest);
// add proxys
break;
default:
return '404 Not Found';
}
}
return $files;
}
/**
* Do propfind in /pricipals/groups
*
* @param string $name name of group or empty
* @param string $rest rest of path behind account-name
* @param array $options
* @return array|string array with files or HTTP error code
*/
protected function propfind_groups($name,$rest,array $options)
{
//echo "<p>".__METHOD__."($name,$rest,".array2string($options).")</p>\n";
if (empty($name))
{
$files = array();
// add /pricipals/users/ entry
$files[] = $this->add_collection('/principals/groups/',array(
HTTP_WebDAV_Server::mkprop('current-user-principal',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))),
));
if ($options['depth'])
{
// add all users
foreach($this->accounts->search(array('type' => 'groups')) as $account)
{
$files[] = $this->add_group($account);
}
}
}
else
{
if (!($id = $this->accounts->name2id($name,'account_lid','g')) ||
!($account = $this->accounts->read($id)))
{
return '404 Not Found';
}
switch((string)$rest)
{
case '':
$files[] = $this->add_group($account);
$files[] = $this->add_collection('/principals/groups/'.$account['account_lid'].'/calendar-proxy-read');
$files[] = $this->add_collection('/principals/groups/'.$account['account_lid'].'/calendar-proxy-write');
break;
case 'calendar-proxy-read':
case 'calendar-proxy-write':
$files = array();
$files[] = $this->add_collection('/principals/groups/'.$account['account_lid'].'/'.$rest);
// add proxys
break;
default:
return '404 Not Found';
}
}
return $files;
}
/**
* Add collection of a single account to a collection
*
* @param array $account
* @return array with values for keys 'path' and 'props'
*/
protected function add_account(array $account)
{
//echo "<p>".__METHOD__."(".array2string($account).")</p>\n";
$displayname = $this->translation->convert($account['account_fullname'],
$this->translation->charset(),'utf-8');
$memberships = array();
foreach($this->accounts->memberships($account['account_id']) as $gid => $group)
{
if ($group)
{
$memberships[] = HTTP_WebDAV_Server::mkprop('href',
$this->base_uri.'/principals/groups/'.$group);
}
}
$props = array(
HTTP_WebDAV_Server::mkprop('displayname',$displayname),
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)),
HTTP_WebDAV_Server::mkprop('resourcetype','principal'),
HTTP_WebDAV_Server::mkprop('alternate-URI-set',array(
HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$account['account_email']))),
HTTP_WebDAV_Server::mkprop('principal-URL',$this->base_uri.'/principals/users/'.$account['account_lid'].'/'),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/calendar/'))),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set',array(
HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$account['account_email']))),
HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-home-set',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))),
HTTP_WebDAV_Server::mkprop('group-member-ship', $memberships),
HTTP_WebDAV_Server::mkprop('principal-URL',array(HTTP_WebDAV_Server::mkprop('href',$this->principalURL))),
);
if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/users/".$account['account_lid'].', props='.array2string($props));
return array(
'path' => '/principals/users/'.$account['account_lid'].'/',
'props' => $props,
);
}
/**
* Add collection of a single group to a collection
*
* @param array $account
* @return array with values for keys 'path' and 'props'
*/
protected function add_group(array $account)
{
$displayname = $this->translation->convert(lang('Group').' '.$account['account_lid'],
$this->translation->charset(),'utf-8');
$members = array();
foreach($this->accounts->members($account['account_id']) as $gid => $user)
{
if ($user)
{
$members[] = HTTP_WebDAV_Server::mkprop('href',
$this->base_uri.'/principals/users/'.$user);
}
}
$props = array(
HTTP_WebDAV_Server::mkprop('displayname',$displayname),
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(groupdav::CALDAV,'calendar-home-set',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/calendar/'))),
HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-home-set',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))),
HTTP_WebDAV_Server::mkprop('group-member-set', $members),
//HTTP_WebDAV_Server::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))),
);
$files['files'][] = array(
'path' => '/principals/groups/'.$account['account_lid'].'/',
'props' => $props,
);
if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/groups/".$account['account_lid'].', props='.array2string($props));
return array(
'path' => '/principals/groups/'.$account['account_lid'].'/',
'props' => $props,
);
}
/**
* Add a collection
*
* @param string $path
* @param array $props=array() extra properties 'resourcetype' is added anyway
* @return array
*/
protected function add_collection($path,$props=array())
{
//echo "<p>".__METHOD__."($path,".array($props).")</p>\n";
$props[] = HTTP_WebDAV_Server::mkprop('resourcetype',array(
HTTP_WebDAV_Server::mkprop('collection',''),
HTTP_WebDAV_Server::mkprop('resourcetype','principal'),
));
return array(
'path' => $path,
'props' => $props,
);
}
/**
* Do propfind of /pricipals
*
* @param string $name name of group or empty
* @param string $rest name of rest of path behind group-name
* @param array $options
* @return array|string array with files or HTTP error code
*/
protected function propfind_principals(array $options)
{
//echo "<p>".__METHOD__."(".array($options).")</p>\n";
$files = array();
$files[] = $this->add_collection('/principals/',array(
HTTP_WebDAV_Server::mkprop('current-user-principal',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/principals/users/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))),
));
if ($options['depth'])
{
$options['depth'] = 0;
$files = array_merge($files,$this->propfind_users('','',$options));
$files = array_merge($files,$this->propfind_groups('','',$options));
//$files = array_merge($this->propfind_resources('','',$options));
//$files = array_merge($this->propfind_uids('','',$options));
}
return $files;
} }
/** /**
@ -90,9 +363,12 @@ class groupdav_principals extends groupdav_handler
{ {
return $account; return $account;
} }
$name = $GLOBALS['egw']->translation->convert(
trim($account['account_firstname'].' '.$account['account_lastname']),
$GLOBALS['egw']->translation->charset(),'utf-8');
$options['data'] = 'Principal: '.$account['account_lid']. $options['data'] = 'Principal: '.$account['account_lid'].
"\nURL: ".$_SERVER['SCRIPT_NAME'].$options['path']. "\nURL: ".$this->base_uri.$options['path'].
"\nName: ".$account['account_firstname'].' '.$account['account_lastname']. "\nName: ".$name.
"\nEmail: ".$account['account_email']. "\nEmail: ".$account['account_email'].
"\nMemberships: ".implode(', ',$this->accounts->memberships($id))."\n"; "\nMemberships: ".implode(', ',$this->accounts->memberships($id))."\n";
$options['mimetype'] = 'text/plain; charset=utf-8'; $options['mimetype'] = 'text/plain; charset=utf-8';
@ -134,7 +410,8 @@ class groupdav_principals extends groupdav_handler
*/ */
function read($id) function read($id)
{ {
return $this->accounts->read($id); return false;
//return $this->accounts->read($id);
} }
/** /**
@ -169,6 +446,6 @@ class groupdav_principals extends groupdav_handler
{ {
$account = $this->read($account); $account = $this->read($account);
} }
return '"'.$account['account_id'].':'.md5(serialize($account)).'"'; return 'EGw-'.$account['account_id'].':'.md5(serialize($account)).'-wGE';
} }
} }

View File

@ -263,7 +263,8 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
// type and size (caller already made sure that path exists) // type and size (caller already made sure that path exists)
if (is_dir($fspath)) { if (is_dir($fspath)) {
// directory (WebDAV collection) // directory (WebDAV collection)
$info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', HTTP_WebDAV_Server::mkprop('collection','')); $info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', array(
HTTP_WebDAV_Server::mkprop('collection', '')));
$info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', 'httpd/unix-directory'); $info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', 'httpd/unix-directory');
} else { } else {
// plain file (WebDAV resource) // plain file (WebDAV resource)
@ -471,4 +472,4 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
{ {
return egw_vfs::checkLock($path); return egw_vfs::checkLock($path);
} }
} }