$suite->addTestSuite('Tinebase_WebDav_Plugin_OwnCloudTest');
$suite->addTestSuite('Tinebase_WebDav_Plugin_PrincipalSearchTest');
$suite->addTestSuite('Tinebase_WebDav_Plugin_SyncTokenTest');
+ $suite->addTestSuite('Tinebase_WebDav_Plugin_ExpandedPropertiesReportTest');
$suite->addTestSuite('Tinebase_WebDav_RootTest');
return $suite;
parent::setUp();
$this->server = new Sabre\DAV\Server(new Tinebase_WebDav_Root());
+ $this->server->debugExceptions = true;
$this->response = new Sabre\HTTP\ResponseMock();
$this->server->httpResponse = $this->response;
--- /dev/null
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ *
+ * @license http://www.gnu.org/licenses/agpl.html
+ * @copyright Copyright (c) 2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author Cornelius Weiß <c.weiss@metaways.de>
+ */
+
+
+/**
+ * Test class for Tinebase_WebDav_Plugin_OwnCloud
+ */
+class Tinebase_WebDav_Plugin_ExpandedPropertiesReportTest extends Tinebase_WebDav_Plugin_PrincipalSearchTest
+{
+ protected function setUp()
+ {
+ parent::setUp();
+ $this->server->addPlugin(new Tinebase_WebDav_Plugin_ExpandedPropertiesReport());
+ }
+
+ public function testExpandProperty()
+ {
+ $list = Tinebase_Group::getInstance()->getGroupById(Tinebase_Core::getUser()->accountPrimaryGroup);
+
+ $body = '<?xml version="1.0" encoding="UTF-8"?>
+ <A:expand-property xmlns:A="DAV:">
+ <A:property name="expanded-group-member-set" namespace="http://calendarserver.org/ns/">
+ <A:property name="last-name" namespace="http://calendarserver.org/ns/"/>
+ <A:property name="principal-URL" namespace="DAV:"/>
+ <A:property name="calendar-user-type" namespace="urn:ietf:params:xml:ns:caldav"/>
+ <A:property name="calendar-user-address-set" namespace="urn:ietf:params:xml:ns:caldav"/>
+ <A:property name="first-name" namespace="http://calendarserver.org/ns/"/>
+ <A:property name="record-type" namespace="http://calendarserver.org/ns/"/>
+ <A:property name="displayname" namespace="DAV:"/>
+ </A:property>
+ </A:expand-property>';
+
+ $request = new Sabre\HTTP\Request(array(
+ 'REQUEST_METHOD' => 'REPORT',
+ 'REQUEST_URI' => '/principals/groups/' . $list->list_id . '/'
+ ));
+ $request->setBody($body);
+
+ $this->server->httpRequest = $request;
+ $this->server->exec();
+
+ $responseDoc = new DOMDocument();
+ $responseDoc->loadXML($this->response->body);
+ #$responseDoc->formatOutput = true; echo $responseDoc->saveXML();
+ $xpath = new DomXPath($responseDoc);
+ $xpath->registerNamespace('cal', 'urn:ietf:params:xml:ns:caldav');
+ $xpath->registerNamespace('cs', 'http://calendarserver.org/ns/');
+
+ $nodes = $xpath->query('///cs:expanded-group-member-set/d:response/d:href[text()="/principals/groups/' . $list->list_id . '/"]');
+ $this->assertEquals(1, $nodes->length, 'group itself (not shown by client) is missing');
+
+ $nodes = $xpath->query('///cs:expanded-group-member-set/d:response/d:href[text()="/principals/intelligroups/' . $list->list_id . '/"]');
+ $this->assertEquals(1, $nodes->length, 'intelligroup (to keep group itself) is missing');
+
+ $nodes = $xpath->query('///cs:expanded-group-member-set/d:response/d:href[text()="/principals/users/' . Tinebase_Core::getUser()->contact_id . '/"]');
+ $this->assertEquals(1, $nodes->length, 'user is missing');
+ }
+
+ public function testConvert()
+ {
+ $emailArrayFromClient = array(array(
+ 'userType' => 'user',
+ 'firstName' => 'Users',
+ 'lastName' => '(Group)',
+ 'partStat' => 'NEEDS-ACTION',
+ 'role' => 'REQ',
+ 'email' => 'urn:uuid:principals/intelligroups/cc74c2880f8c5c0eaacc57ea95f4d2571fb8a4b1',
+ ));
+
+ $event = new Calendar_Model_Event();
+ Calendar_Model_Attender::emailsToAttendee($event, $emailArrayFromClient);
+
+ $this->assertEquals('cc74c2880f8c5c0eaacc57ea95f4d2571fb8a4b1', $event->attendee->getFirstRecord()->user_id);
+ $this->assertEquals('group', $event->attendee->getFirstRecord()->user_type);
+ }
+}
\ No newline at end of file
//var_dump($principal);
$this->assertEquals(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS . '/' . $list->list_id, $principal['uri']);
- $this->assertEquals($list->name . ' (Group)', $principal['{DAV:}displayname']);
+ $this->assertEquals($list->name . ' ('. Tinebase_Translation::getTranslation('Calendar')->_('Group') . ')', $principal['{DAV:}displayname']);
}
public function testGetPrincipalByUserPath()
break;
case self::USERTYPE_GROUP:
case self::USERTYPE_RESOURCE:
- return $resolvedUser->name ?: $resolvedUser->n_fileas;
+ $translation = Tinebase_Translation::getTranslation('Calendar');
+ $name = $resolvedUser->name ?: $resolvedUser->n_fileas;
+ if ($this->user_type == self::USERTYPE_GROUP) {
+ $name . ' (' . $translation->_('Group') . ')';
+ }
+ return $name;
break;
default:
throw new Exception("type " . $this->user_type . " not yet supported");
*/
public static function emailsToAttendee(Calendar_Model_Event $_event, $_emails, $_implicitAddMissingContacts = TRUE)
{
- if (Tinebase_Core::isLogLevel(Zend_Log::TRACE))
- Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " list of new attendees " . print_r($_emails, true));
+ if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
+ Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " list of new attendees " . print_r($_emails, true));
if (! $_event->attendee instanceof Tinebase_Record_RecordSet) {
$_event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender');
$attendeeId = NULL;
if ($newAttendee['userType'] == Calendar_Model_Attender::USERTYPE_USER) {
+ // list from groupmember expand
+ if ( ! $attendeeId &&
+ preg_match('#^urn:uuid:principals/intelligroups/([a-z0-9]+)#', $newAttendee['email'], $matches)
+ ) {
+ $newAttendee['userType'] = Calendar_Model_Attender::USERTYPE_GROUP;
+ $attendeeId = $matches[1];
+ }
+
// does a contact with this email address exist?
- if ($contact = self::resolveEmailToContact($newAttendee, false)) {
+ if (! $attendeeId && $contact = self::resolveEmailToContact($newAttendee, false)) {
$attendeeId = $contact->getId();
}
// does a resouce with this email address exist?
- if ( ! $attendeeId) {
+ if (! $attendeeId) {
$resources = Calendar_Controller_Resource::getInstance()->search(new Calendar_Model_ResourceFilter(array(
array('field' => 'email', 'operator' => 'equals', 'value' => $newAttendee['email']),
)));
$newAttendee['userType'] = Calendar_Model_Attender::USERTYPE_GROUP;
$attendeeId = $lists->getFirstRecord()->group_id;
}
- }
-
+ }
+
if (! $attendeeId) {
// autocreate a contact if allowed
$contact = self::resolveEmailToContact($newAttendee, $_implicitAddMissingContacts);
$attendeeId = $contact->getId();
}
}
+
} else if($newAttendee['userType'] == Calendar_Model_Attender::USERTYPE_GROUP) {
$lists = Addressbook_Controller_List::getInstance()->search(new Addressbook_Model_ListFilter(array(
array('field' => 'name', 'operator' => 'equals', 'value' => $newAttendee['displayName']),
$attendeeId = $lists->getFirstRecord()->group_id;
}
}
-
+
if ($attendeeId !== NULL) {
// finally add to attendee
$_event->attendee->addRecord(new Calendar_Model_Attender(array(
$aclPlugin = new \Sabre\DAVACL\Plugin();
$aclPlugin->defaultUsernamePath = Tinebase_WebDav_PrincipalBackend::PREFIX_USERS;
- $aclPlugin->principalCollectionSet = array (Tinebase_WebDav_PrincipalBackend::PREFIX_USERS, Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS);
+ $aclPlugin->principalCollectionSet = array (Tinebase_WebDav_PrincipalBackend::PREFIX_USERS, Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS, Tinebase_WebDav_PrincipalBackend::PREFIX_INTELLIGROUPS);
$aclPlugin->principalSearchPropertySet = array(
'{DAV:}displayname' => 'Display name',
self::$_server->addPlugin(new Tinebase_WebDav_Plugin_Inverse());
self::$_server->addPlugin(new Tinebase_WebDav_Plugin_OwnCloud());
self::$_server->addPlugin(new Tinebase_WebDav_Plugin_PrincipalSearch());
+ self::$_server->addPlugin(new Tinebase_WebDav_Plugin_ExpandedPropertiesReport());
self::$_server->addPlugin(new \Sabre\DAV\Browser\Plugin());
self::$_server->addPlugin(new Tinebase_WebDav_Plugin_SyncToken());
--- /dev/null
+<?php
+/**
+ * CalDAV plugin for expanded-group-member-set
+ *
+ * NOTE: for expand-property reports some properties seem to be prefixed with 'expanded-':
+ * - expanded-group-member-set
+ * - expanded-group-membership
+ *
+ * It's not clear if this is according to the standards, but iCal sends this requests and
+ * Sabre can't cope with it yet
+ *
+ * @copyright Copyright (c) 2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author Cornelius Weiß <c.weiss@metaways.de>
+ * @license http://www.gnu.org/licenses/agpl.html
+ */
+class Tinebase_WebDav_Plugin_ExpandedPropertiesReport extends \Sabre\DAV\ServerPlugin {
+
+ /**
+ * Reference to server object
+ *
+ * @var \Sabre\DAV\Server
+ */
+ protected $server;
+
+ /**
+ * Returns a list of features for the DAV: HTTP header.
+ *
+ * @return array
+ */
+ public function getFeatures()
+ {
+ return array();
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @see \Sabre\DAV\ServerPlugin::getPluginName()
+ */
+ public function getPluginName()
+ {
+ return 'expandPropertiesReport';
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @see \Sabre\DAV\ServerPlugin::getSupportedReportSet()
+ */
+ public function getSupportedReportSet($uri)
+ {
+ return array(
+ '{DAV:}expand-property',
+ );
+
+ }
+ /**
+ * Initializes the plugin
+ *
+ * @param \Sabre\DAV\Server $server
+ * @return void
+ */
+ public function initialize(\Sabre\DAV\Server $server)
+ {
+ $this->server = $server;
+
+ $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties'));
+ }
+
+ /**
+ * beforeGetProperties
+ *
+ * This method handler is invoked before any after properties for a
+ * resource are fetched. This allows us to add in any CalDAV specific
+ * properties.
+ *
+ * @param string $path
+ * @param DAV\INode $node
+ * @param array $requestedProperties
+ * @param array $returnedProperties
+ * @return void
+ */
+ public function beforeGetProperties($path, \Sabre\DAV\INode $node, &$requestedProperties, &$returnedProperties)
+ {
+ if (in_array('{http://calendarserver.org/ns/}expanded-group-member-set', $requestedProperties)) {
+ $parentNode = $this->server->tree->getNodeForPath($path);
+ $groupMemberSet = $parentNode->getGroupMemberSet();
+
+ // iCal want's to have the group itself in the response set
+ $groupMemberSet[] = $path;
+
+ // have record for group itself
+ $groupMemberSet[] = str_replace(
+ Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS,
+ Tinebase_WebDav_PrincipalBackend::PREFIX_INTELLIGROUPS,
+ $path
+ );
+
+ $returnedProperties[200]['{http://calendarserver.org/ns/}expanded-group-member-set'] = new Sabre\DAV\Property\HrefList($groupMemberSet);
+ }
+ }
+
+}
{
const PREFIX_USERS = 'principals/users';
const PREFIX_GROUPS = 'principals/groups';
+ const PREFIX_INTELLIGROUPS = 'principals/intelligroups';
const SHARED = 'shared';
/**
switch ($prefixPath) {
case self::PREFIX_GROUPS:
+ case self::PREFIX_INTELLIGROUPS:
$filter = new Addressbook_Model_ListFilter(array(
array(
'field' => 'type',
$lists = Addressbook_Controller_List::getInstance()->search($filter);
foreach ($lists as $list) {
- $principals[] = $this->_listToPrincipal($list);
+ $principals[] = $this->_listToPrincipal($list, $prefixPath);
}
break;
break;
case self::PREFIX_GROUPS:
+ case self::PREFIX_INTELLIGROUPS:
$filter = new Addressbook_Model_ListFilter(array(
array(
'field' => 'type',
return null;
}
- $principal = $this->_listToPrincipal($list);
+ $principal = $this->_listToPrincipal($list, $prefix);
break;
break;
case self::PREFIX_GROUPS:
+ case self::PREFIX_INTELLIGROUPS:
$filter = new Addressbook_Model_ListFilter(array(
array(
'field' => 'type',
switch ($prefixPath) {
case self::PREFIX_GROUPS:
+ case self::PREFIX_INTELLIGROUPS:
$filter = new Addressbook_Model_ListFilter(array(
array(
'field' => 'type',
* convert list model to principal array
*
* @param Addressbook_Model_List $list
+ * @param string $prefix
* @return array
*/
- protected function _listToPrincipal(Addressbook_Model_List $list)
+ protected function _listToPrincipal(Addressbook_Model_List $list, $prefix)
{
+ $calUserType = $prefix == self::PREFIX_INTELLIGROUPS ? 'INTELLIGROUP' : 'GROUP';
+
$principal = array(
- 'uri' => self::PREFIX_GROUPS . '/' . $list->getId(),
- '{DAV:}displayname' => $list->name . ' (Group)',
- '{DAV:}alternate-URI-set' => array('urn:uuid:' . $list->getId()),
+ 'uri' => $prefix . '/' . $list->getId(),
+ '{DAV:}displayname' => $list->name . ' (' . $translation = Tinebase_Translation::getTranslation('Calendar')->_('Group') . ')',
+ '{DAV:}alternate-URI-set' => array('urn:uuid:' . $prefix . '/' . $list->getId()),
- '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar-user-type' => 'GROUP',
+ '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar-user-type' => $calUserType,
'{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}record-type' => 'groups',
- '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}first-name' => 'Group',
+ '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}first-name' => Tinebase_Translation::getTranslation('Calendar')->_('Group'),
'{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}last-name' => $list->name,
);
-
+
+ if ($calUserType == 'INTELLIGROUP') {
+ // OSX needs an email adress to send the attendee
+ $principal['{http://sabredav.org/ns}email-address'] = 'urn:uuid:' . $prefix . '/' . $list->getId();
+ }
+
return $principal;
}
}
parent::__construct('root', array(
new \Sabre\DAV\SimpleCollection('principals', array(
new Tinebase_WebDav_PrincipalCollection(new Tinebase_WebDav_PrincipalBackend(), Tinebase_WebDav_PrincipalBackend::PREFIX_USERS),
- new Tinebase_WebDav_PrincipalCollection(new Tinebase_WebDav_PrincipalBackend(), Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS)
+ new Tinebase_WebDav_PrincipalCollection(new Tinebase_WebDav_PrincipalBackend(), Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS),
+ new Tinebase_WebDav_PrincipalCollection(new Tinebase_WebDav_PrincipalBackend(), Tinebase_WebDav_PrincipalBackend::PREFIX_INTELLIGROUPS)
))
));