implement calendarserver-principal-search
authorLars Kneschke <l.kneschke@metaways.de>
Tue, 11 Feb 2014 10:18:48 +0000 (11:18 +0100)
committerPhilipp Schüle <p.schuele@metaways.de>
Thu, 4 Sep 2014 09:25:28 +0000 (11:25 +0200)
Change-Id: I6f052bdbd27944a69ddb8d77ddd689ac15e99d08
Reviewed-on: http://gerrit.tine20.com/customers/326
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
tests/tine20/Tinebase/WebDav/AllTests.php
tests/tine20/Tinebase/WebDav/Plugin/PrincipalSearchTest.php [new file with mode: 0644]
tests/tine20/Tinebase/WebDav/PrincipalBackendTest.php
tine20/Tinebase/Server/WebDAV.php
tine20/Tinebase/WebDav/Plugin/PrincipalSearch.php [new file with mode: 0644]
tine20/Tinebase/WebDav/Principal.php [new file with mode: 0644]
tine20/Tinebase/WebDav/PrincipalBackend.php
tine20/Tinebase/WebDav/PrincipalCollection.php [new file with mode: 0644]
tine20/Tinebase/WebDav/Root.php

index 6ace597..88a7315 100644 (file)
@@ -29,6 +29,7 @@ class Tinebase_WebDav_AllTests
         $suite->addTestSuite('Tinebase_WebDav_PrincipalBackendTest');
         $suite->addTestSuite('Tinebase_WebDav_Plugin_InverseTest');
         $suite->addTestSuite('Tinebase_WebDav_Plugin_OwnCloudTest');
+        $suite->addTestSuite('Tinebase_WebDav_Plugin_PrincipalSearchTest');
         $suite->addTestSuite('Tinebase_WebDav_RootTest');
         
         return $suite;
diff --git a/tests/tine20/Tinebase/WebDav/Plugin/PrincipalSearchTest.php b/tests/tine20/Tinebase/WebDav/Plugin/PrincipalSearchTest.php
new file mode 100644 (file)
index 0000000..de7656c
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Tinebase
+ * @subpackage  Frontend
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2013-2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Lars Kneschke <l.kneschke@metaways.de>
+ */
+
+/**
+ * Test helper
+ */
+require_once 'vendor/sabre/dav/tests/Sabre/HTTP/ResponseMock.php';
+require_once 'vendor/sabre/dav/tests/Sabre/DAV/Auth/Backend/Mock.php';
+
+/**
+ * Test class for Tinebase_WebDav_Plugin_OwnCloud
+ */
+class Tinebase_WebDav_Plugin_PrincipalSearchTest extends TestCase
+{
+    /**
+     * 
+     * @var Sabre\DAV\Server
+     */
+    protected $server;
+    
+    /**
+     * Sets up the fixture.
+     * This method is called before a test is executed.
+     *
+     * @access protected
+     */
+    protected function setUp()
+    {
+        parent::setUp();
+        
+        $this->server = new Sabre\DAV\Server(new Tinebase_WebDav_Root());
+        
+        $mockBackend = new Sabre\DAV\Auth\Backend\Mock();
+        $mockBackend->defaultUser = Tinebase_Core::getUser()->contact_id;
+        
+        $plugin = new Sabre\DAV\Auth\Plugin($mockBackend,'realm');
+        $this->server->addPlugin($plugin);
+        
+        $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);
+        $this->server->addPlugin($aclPlugin);
+        
+        $this->server->addPlugin(new \Sabre\CalDAV\Plugin());
+        $this->server->addPlugin(new \Sabre\CalDAV\SharingPlugin());
+        
+        $this->plugin = new Tinebase_WebDav_Plugin_PrincipalSearch();
+        $this->server->addPlugin($this->plugin);
+        
+        $this->response = new Sabre\HTTP\ResponseMock();
+        $this->server->httpResponse = $this->response;
+    }
+
+    /**
+     * test getPluginName method
+     */
+    public function testGetPluginName()
+    {
+        $pluginName = $this->plugin->getPluginName();
+        
+        $this->assertEquals('calendarserverPrincipalSearch', $pluginName);
+    }
+    
+    /**
+     * test testGetProperties method
+     */
+    public function testGetProperties()
+    {
+        $body = '<?xml version="1.0" encoding="utf-8"?>
+                 <A:calendarserver-principal-search xmlns:A="http://calendarserver.org/ns/" context="attendee">
+                    <A:search-token>Administrators</A:search-token>
+                    <A:limit>
+                        <A:nresults>50</A:nresults>
+                    </A:limit>
+                    <B:prop xmlns:B="DAV:">
+                        <C:calendar-user-address-set xmlns:C="urn:ietf:params:xml:ns:caldav"/>
+                        <C:calendar-user-type xmlns:C="urn:ietf:params:xml:ns:caldav"/>
+                        <A:record-type/>
+                        <A:first-name/>
+                        <A:last-name/>
+                    </B:prop>
+                </A:calendarserver-principal-search>';
+
+        $request = new Sabre\HTTP\Request(array(
+            'REQUEST_METHOD' => 'REPORT',
+            'REQUEST_URI'    => '/principals'
+        ));
+        $request->setBody($body);
+
+        $this->server->httpRequest = $request;
+        $this->server->exec();
+        //var_dump($this->response->body);
+        $this->assertEquals('HTTP/1.1 207 Multi-Status', $this->response->status);
+        
+        $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');
+        
+        $nodes = $xpath->query('//d:multistatus/d:response/d:propstat/d:prop/cal:calendar-user-address-set');
+        $this->assertEquals(1, $nodes->length, $responseDoc->saveXML());
+        $this->assertNotEmpty($nodes->item(0)->nodeValue, $responseDoc->saveXML());
+        
+        $nodes = $xpath->query('//d:multistatus/d:response/d:propstat/d:prop/cal:calendar-user-type');
+        $this->assertEquals(1, $nodes->length, $responseDoc->saveXML());
+        $this->assertNotEmpty($nodes->item(0)->nodeValue, $responseDoc->saveXML());
+        
+        #$nodes = $xpath->query('//d:multistatus/d:response/d:propstat/d:prop/cal:default-alarm-vtodo-datetime');
+        #$this->assertEquals(1, $nodes->length, $responseDoc->saveXML());
+        #$this->assertNotEmpty($nodes->item(0)->nodeValue, $responseDoc->saveXML());
+        
+        #$nodes = $xpath->query('//d:multistatus/d:response/d:propstat/d:prop/cal:default-alarm-vtodo-date');
+        #$this->assertEquals(1, $nodes->length, $responseDoc->saveXML());
+        #$this->assertNotEmpty($nodes->item(0)->nodeValue, $responseDoc->saveXML());
+    }
+}
index 401f3f2..194191b 100644 (file)
@@ -68,12 +68,14 @@ class Tinebase_WebDav_PrincipalBackendTest extends TestCase
      */
     public function testGetPrincipalByGroupPath()
     {
-        $principal = $this->_backend->getPrincipalByPath(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS . '/' . Tinebase_Core::getUser()->accountPrimaryGroup);
+        $list = Tinebase_Group::getInstance()->getGroupById(Tinebase_Core::getUser()->accountPrimaryGroup);
+        
+        $principal = $this->_backend->getPrincipalByPath(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS . '/' . $list->list_id);
         
         //var_dump($principal);
         
-        $this->assertEquals(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS . '/' . Tinebase_Core::getUser()->accountPrimaryGroup, $principal['uri']);
-        $this->assertEquals('Tine 2.0 group ' . Tinebase_Core::getUser()->accountPrimaryGroup, $principal['{DAV:}displayname']);
+        $this->assertEquals(Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS . '/' . $list->list_id, $principal['uri']);
+        $this->assertEquals($list->name . ' (Group)', $principal['{DAV:}displayname']);
     }
     
     public function testGetPrincipalByUserPath()
index 2e8d2d4..a47ffbd 100644 (file)
@@ -111,6 +111,7 @@ class Tinebase_Server_WebDAV extends Tinebase_Server_Abstract implements Tinebas
         self::$_server->addPlugin(new Calendar_Frontend_CalDAV_PluginDefaultAlarms());
         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 DAV\Sync\Plugin());
         self::$_server->addPlugin(new \Sabre\DAV\Browser\Plugin());
         
diff --git a/tine20/Tinebase/WebDav/Plugin/PrincipalSearch.php b/tine20/Tinebase/WebDav/Plugin/PrincipalSearch.php
new file mode 100644 (file)
index 0000000..c878036
--- /dev/null
@@ -0,0 +1,145 @@
+<?php
+/**
+ * CalDAV plugin for calendar-auto-schedule
+ * 
+ * This plugin provides functionality added by RFC6638
+ * It takes care of additional properties and features
+ * 
+ * see: http://tools.ietf.org/html/rfc6638
+ *
+ * @package    Sabre
+ * @subpackage CalDAV
+ * @copyright  Copyright (c) 2014-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author     Lars Kneschke <l.kneschke@metaways.de>
+ * @license    http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Tinebase_WebDav_Plugin_PrincipalSearch 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('calendarserver-principal-search');
+    }
+
+    /**
+     * (non-PHPdoc)
+     * @see \Sabre\DAV\ServerPlugin::getPluginName()
+     */
+    public function getPluginName() 
+    {
+        return 'calendarserverPrincipalSearch';
+    }
+    
+    /**
+     * (non-PHPdoc)
+     * @see \Sabre\DAV\ServerPlugin::getSupportedReportSet()
+     */
+    public function getSupportedReportSet($uri) 
+    {
+        return array(
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}calendarserver-principal-search'
+        );
+
+    }
+    /**
+     * Initializes the plugin 
+     * 
+     * @param \Sabre\DAV\Server $server 
+     * @return void
+     */
+    public function initialize(\Sabre\DAV\Server $server) 
+    {
+        $this->server = $server;
+
+        $server->xmlNamespaces[\Sabre\CalDAV\Plugin::NS_CALDAV] = 'cal';
+        $server->xmlNamespaces[\Sabre\CalDAV\Plugin::NS_CALENDARSERVER] = 'cs';
+
+        #$server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties'));
+        $server->subscribeEvent('report',array($this,'report'));
+        
+        array_push($server->protectedProperties,
+            // CalendarServer extensions
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}record-type',
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}first-name',
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}last-name'
+        );
+    }
+    
+    /**
+     * 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 ($node instanceof \Sabre\DAVACL\IPrincipal) {var_dump($path);
+    #        // schedule-outbox-URL property
+    #        #'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar-user-type'        => 'GROUP',
+    #        $property = '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar-user-type';
+    #        if (in_array($property,$requestedProperties)) {
+    #            list($prefix, $nodeId) = Sabre\DAV\URLUtil::splitPath($path);
+    #            
+    #            unset($requestedProperties[array_search($property, $requestedProperties)]);
+    #            $returnedProperties[200][$property] = ($prefix == Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS) ? 'GROUP' : 'INDIVIDUAL';
+
+    #        }
+    #    }
+    #}
+    
+    /**
+     * This method handles HTTP REPORT requests
+     *
+     * @param string $reportName
+     * @param \DOMNode $dom
+     * @return bool
+     */
+    public function report($reportName, $dom) 
+    {
+        switch($reportName) {
+            case '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}calendarserver-principal-search':
+                $this->_principalSearchReport($dom);
+                return false;
+        }
+    }
+    
+    protected function _principalSearchReport(\DOMDocument $dom) 
+    {
+        $requestedProperties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($dom->firstChild));
+        
+        $searchTokens = $dom->firstChild->getElementsByTagName('search-token');
+
+        $searchProperties = array();
+        
+        if ($searchTokens->length > 0) {
+            $searchProperties['{http://calendarserver.org/ns/}search-token'] = $searchTokens->item(0)->nodeValue;
+        }
+        
+        $result = $this->server->getPlugin('acl')->principalSearch($searchProperties, $requestedProperties);
+
+        $prefer = $this->server->getHTTPPRefer();
+
+        $this->server->httpResponse->sendStatus(207);
+        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+        $this->server->httpResponse->setHeader('Vary','Brief,Prefer');
+        $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal']));
+    }
+}
diff --git a/tine20/Tinebase/WebDav/Principal.php b/tine20/Tinebase/WebDav/Principal.php
new file mode 100644 (file)
index 0000000..1904966
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Principals Collection
+ *
+ * This collection represents a list of users.
+ * The users are instances of Sabre\DAVACL\Principal
+ *
+ * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Tinebase_WebDav_Principal extends Sabre\DAVACL\Principal 
+{
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to
+     *      be updated.
+     *
+     * @return array
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => '{DAV:}authenticated',
+                'protected' => true,
+            ),
+        );
+    }
+}
index 9662c37..2999c24 100644 (file)
@@ -34,32 +34,37 @@ class Tinebase_WebDav_PrincipalBackend implements DAVACL\PrincipalBackend\Backen
         
         switch ($prefixPath) {
             case self::PREFIX_GROUPS:
-                $groups = Tinebase_Group::getInstance()->getMultiple(Tinebase_Core::getUser()->getGroupMemberships());
-                
-                foreach ($groups as $group) {
-                    if (empty($group->list_id)) {
-                        continue;
-                    }
-                    $principals[] = array(
-                        'uri'               => self::PREFIX_GROUPS . '/' . $group->list_id,
-                        '{DAV:}displayname' => $group->name
-                    );
-                }
+                $filter = new Addressbook_Model_ListFilter(array(
+                    array(
+                        'field'     => 'type',
+                        'operator'  => 'equals',
+                        'value'     => Addressbook_Model_List::LISTTYPE_GROUP
+                    )
+                ));
+                
+                $lists = Addressbook_Controller_List::getInstance()->search($filter);
                 
+                foreach ($lists as $list) {
+                    $principals[] = $this->_listToPrincipal($list);
+                }
                 
                 break;
                 
             case self::PREFIX_USERS:
-                $principal = array(
-                    'uri'               => self::PREFIX_USERS . '/' . Tinebase_Core::getUser()->contact_id,
-                    '{DAV:}displayname' => Tinebase_Core::getUser()->accountDisplayName
-                );
+                $filter = new Addressbook_Model_ContactFilter(array(
+                    array(
+                        'field'     => 'type',
+                        'operator'  => 'equals',
+                        'value'     => Addressbook_Model_Contact::CONTACTTYPE_USER
+                    )
+                ));
                 
-                if (!empty(Tinebase_Core::getUser()->accountEmailAddress)) {
-                    $principal['{http://sabredav.org/ns}email-address'] = Tinebase_Core::getUser()->accountEmailAddress;
+                $contacts = Addressbook_Controller_Contact::getInstance()->search($filter);
+                
+                foreach ($contacts as $contact) {
+                    $principals[] = $this->_contactToPrincipal($contact);
                 }
                 
-                $principals[] = $principal;
                 
                 break;
         }
@@ -70,33 +75,61 @@ class Tinebase_WebDav_PrincipalBackend implements DAVACL\PrincipalBackend\Backen
     /**
      * (non-PHPdoc)
      * @see Sabre\DAVACL\IPrincipalBackend::getPrincipalByPath()
-     * @todo implement search for users and groups
+     * @todo resolve real $path
      */
     public function getPrincipalByPath($path) 
     {
         $principal = null;
         
-        list($prefix, $name) = \Sabre\DAV\URLUtil::splitPath($path);
+        list($prefix, $id) = \Sabre\DAV\URLUtil::splitPath($path);
         
         switch ($prefix) {
             case self::PREFIX_GROUPS:
-                $principal = array(
-                    'uri'               => self::PREFIX_GROUPS . '/' . $name,
-                    '{DAV:}displayname' => 'Tine 2.0 group ' . $name
-                );
+                $filter = new Addressbook_Model_ListFilter(array(
+                    array(
+                        'field'     => 'type',
+                        'operator'  => 'equals',
+                        'value'     => Addressbook_Model_List::LISTTYPE_GROUP
+                    ),
+                    array(
+                        'field'     => 'id',
+                        'operator'  => 'equals',
+                        'value'     => $id
+                    ),
+                ));
+                
+                $list = Addressbook_Controller_List::getInstance()->search($filter)->getFirstRecord();
+                
+                if (!$list) {
+                    return null;
+                }
+                
+                $principal = $this->_listToPrincipal($list);
                 
                 break;
                 
             case self::PREFIX_USERS:
-                $principal = array(
-                    'uri'               => self::PREFIX_USERS . '/' . Tinebase_Core::getUser()->contact_id,
-                    '{DAV:}displayname' => Tinebase_Core::getUser()->accountDisplayName
-                );
+                $filter = new Addressbook_Model_ContactFilter(array(
+                    array(
+                        'field'     => 'type',
+                        'operator'  => 'equals',
+                        'value'     => Addressbook_Model_Contact::CONTACTTYPE_USER
+                    ),
+                    array(
+                        'field'     => 'id',
+                        'operator'  => 'equals',
+                        'value'     => $id
+                    ),
+                ));
+                
+                $contact = Addressbook_Controller_Contact::getInstance()->search($filter)->getFirstRecord();
                 
-                if (!empty(Tinebase_Core::getUser()->accountEmailAddress)) {
-                    $principal['{http://sabredav.org/ns}email-address'] = Tinebase_Core::getUser()->accountEmailAddress;
+                if (!$contact) {
+                    return null;
                 }
                 
+                $principal = $this->_contactToPrincipal($contact);
+                
                 break;
         }
         
@@ -111,6 +144,33 @@ class Tinebase_WebDav_PrincipalBackend implements DAVACL\PrincipalBackend\Backen
     {
         $result = array();
         
+        list($path, $listId) = Sabre\DAV\URLUtil::splitPath($principal);
+        
+        if ($path == self::PREFIX_GROUPS) {
+            $filter = new Addressbook_Model_ListFilter(array(
+                array(
+                    'field'     => 'type',
+                    'operator'  => 'equals',
+                    'value'     => Addressbook_Model_List::LISTTYPE_GROUP
+                ),
+                array(
+                    'field'     => 'id',
+                    'operator'  => 'equals',
+                    'value'     => $listId
+                ),
+            ));
+            
+            $list = Addressbook_Controller_List::getInstance()->search($filter)->getFirstRecord();
+            
+            if (!$list) {
+                return array();
+            }
+            
+            foreach ($list->members as $member) {
+                $result[] = self::PREFIX_USERS . '/' . $member;
+            }
+        }
+        
         return $result;
     }
     
@@ -122,15 +182,17 @@ class Tinebase_WebDav_PrincipalBackend implements DAVACL\PrincipalBackend\Backen
     {
         $result = array();
         
-        list(, $contactId) = Sabre\DAV\URLUtil::splitPath($principal);
-        
-        $user = Tinebase_User::getInstance()->getUserByProperty('contactId', $contactId);
-        
-        $groupIds = Tinebase_Group::getInstance()->getGroupMemberships($user);
-        $groups   = Tinebase_Group::getInstance()->getMultiple($groupIds);
+        list($path, $contactId) = Sabre\DAV\URLUtil::splitPath($principal);
         
-        foreach ($groups as $group) {
-            $result[] = 'principals/groups/' . $group->list_id;
+        if ($path == self::PREFIX_USERS) {
+            $user = Tinebase_User::getInstance()->getUserByProperty('contactId', $contactId);
+            
+            $groupIds = Tinebase_Group::getInstance()->getGroupMemberships($user);
+            $groups   = Tinebase_Group::getInstance()->getMultiple($groupIds);
+            
+            foreach ($groups as $group) {
+                $result[] = self::PREFIX_GROUPS . '/' . $group->list_id;
+            }
         }
         
         return $result;
@@ -182,26 +244,60 @@ class Tinebase_WebDav_PrincipalBackend implements DAVACL\PrincipalBackend\Backen
         $principalUris = array();
         
         switch ($prefixPath) {
+            case self::PREFIX_GROUPS:
+                $filter = new Addressbook_Model_ListFilter(array(
+                    array(
+                        'field'     => 'type',
+                        'operator'  => 'equals',
+                        'value'     => Addressbook_Model_List::LISTTYPE_GROUP
+                    )
+                ));
+                
+                if (!empty($searchProperties['{http://calendarserver.org/ns/}search-token'])) {
+                    $filter->addFilter($filter->createFilter(array(
+                        'field'     => 'query',
+                        'operator'  => 'contains',
+                        'value'     => $searchProperties['{http://calendarserver.org/ns/}search-token']
+                    )));
+                }
+                
+                $result = Addressbook_Controller_List::getInstance()->search($filter, null, false, true);
+                
+                foreach ($result as $listId) {
+                    $principalUris[] = $prefixPath . '/' . $listId;
+                }
+                
+                break;
+                
             case self::PREFIX_USERS:
+                $filter = new Addressbook_Model_ContactFilter(array(
+                    array(
+                        'field'     => 'type',
+                        'operator'  => 'equals',
+                        'value'     => Addressbook_Model_Contact::CONTACTTYPE_USER
+                    )
+                ));
+                
+                if (!empty($searchProperties['{http://calendarserver.org/ns/}search-token'])) {
+                    $filter->addFilter($filter->createFilter(array(
+                        'field'     => 'query',
+                        'operator'  => 'contains',
+                        'value'     => $searchProperties['{http://calendarserver.org/ns/}search-token']
+                    )));
+                }
+                
                 if (!empty($searchProperties['{http://sabredav.org/ns}email-address'])) {
-                    $filter = new Addressbook_Model_ContactFilter(array(
-                        array(
-                            'field'     => 'email_query',
-                            'operator'  => 'equals',
-                            'value'     => $searchProperties['{http://sabredav.org/ns}email-address']
-                        ),
-                        array(
-                            'field'     => 'type',
-                            'operator'  => 'equals',
-                            'value'     => Addressbook_Model_Contact::CONTACTTYPE_USER
-                        )
-                    ));
-                    
-                    $result = Addressbook_Controller_Contact::getInstance()->search($filter, null, false, true);
-                    
-                    if (count($result) > 0) {
-                        $principalUris[] = 'principals/users/' . $result[0];
-                    }
+                    $filter->addFilter($filter->createFilter(array(
+                        'field'     => 'email_query',
+                        'operator'  => 'equals',
+                        'value'     => $searchProperties['{http://sabredav.org/ns}email-address']
+                    )));
+                }
+                
+                $result = Addressbook_Controller_Contact::getInstance()->search($filter, null, false, true);
+                
+                foreach ($result as $contactId) {
+                    $principalUris[] = $prefixPath . '/' . $contactId;
                 }
                 
                 break;
@@ -209,4 +305,54 @@ class Tinebase_WebDav_PrincipalBackend implements DAVACL\PrincipalBackend\Backen
         
         return $principalUris;
     }
+    
+    /**
+     * convert contact model to principal array
+     * 
+     * @param Addressbook_Model_Contact $contact
+     * @return array
+     */
+    protected function _contactToPrincipal(Addressbook_Model_Contact $contact)
+    {
+        $principal = array(
+            'uri'                     => self::PREFIX_USERS . '/' . $contact->getId(),
+            '{DAV:}displayname'       => $contact->n_fileas,
+            '{DAV:}alternate-URI-set' => array('urn:uuid:' . $contact->getId()),
+            
+            '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar-user-type'  => 'INDIVIDUAL',
+            
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}record-type' => 'users',
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}first-name'  => $contact->n_given,
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}last-name'   => $contact->n_family
+        );
+        
+        if (!empty(Tinebase_Core::getUser()->accountEmailAddress)) {
+            $principal['{http://sabredav.org/ns}email-address'] = $contact->email;
+        }
+        
+        return $principal;
+    }
+    
+    /**
+     * convert list model to principal array
+     * 
+     * @param Addressbook_Model_List $list
+     * @return array
+     */
+    protected function _listToPrincipal(Addressbook_Model_List $list)
+    {
+        $principal = array(
+            'uri'                     => self::PREFIX_GROUPS . '/' . $list->getId(),
+            '{DAV:}displayname'       => $list->name . ' (Group)',
+            '{DAV:}alternate-URI-set' => array('urn:uuid:' . $list->getId()),
+            
+            '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar-user-type'  => 'GROUP',
+            
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}record-type' => 'groups',
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}first-name'  => 'Group',
+            '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}last-name'   => $list->name,
+        );
+        
+        return $principal;
+    }
 }
diff --git a/tine20/Tinebase/WebDav/PrincipalCollection.php b/tine20/Tinebase/WebDav/PrincipalCollection.php
new file mode 100644 (file)
index 0000000..4264724
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Principals Collection
+ *
+ * This collection represents a list of users.
+ * The users are instances of Sabre\DAVACL\Principal
+ *
+ * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Tinebase_WebDav_PrincipalCollection extends Sabre\DAVACL\AbstractPrincipalCollection {
+
+    /**
+     * This method returns a node for a principal.
+     *
+     * The passed array contains principal information, and is guaranteed to
+     * at least contain a uri item. Other properties may or may not be
+     * supplied by the authentication backend.
+     *
+     * @param array $principal
+     * @return \Sabre\DAV\INode
+     */
+    public function getChildForPrincipal(array $principal) 
+    {
+        return new Tinebase_WebDav_Principal($this->principalBackend, $principal);
+    }
+}
index 5c41552..15b09a6 100644 (file)
@@ -64,8 +64,8 @@ class Tinebase_WebDav_Root extends DAV\SimpleCollection
             new DAV\SimpleCollection('tasks',                          $caldavTasksChildren),
             new DAV\SimpleCollection('webdav',                         $webdavChildren),
             new DAV\SimpleCollection('principals', array(
-                new DAVACL\PrincipalCollection(new Tinebase_WebDav_PrincipalBackend(), Tinebase_WebDav_PrincipalBackend::PREFIX_USERS),
-                new DAVACL\PrincipalCollection(new Tinebase_WebDav_PrincipalBackend(), Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS)
+                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)
             )),
             // main entry point for ownCloud 
             new DAV\SimpleCollection('remote.php',                     $ownCloudChildren)