From c1bb682e1a3cb707dd6792062a58df0780092cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 15 Nov 2016 22:17:52 +0100 Subject: [PATCH] Enhance CardDAV query report to return a selective list of vcard properties - fixes #889 --- lib/CardDAV/Plugin.php | 17 ++++++- lib/CardDAV/Xml/Filter/AddressData.php | 5 ++ .../Xml/Request/AddressBookQueryReport.php | 7 +++ tests/Sabre/CardDAV/AddressBookHomeTest.php | 4 +- tests/Sabre/CardDAV/AddressBookQueryTest.php | 49 ++++++++++++++++++- tests/Sabre/CardDAV/AddressBookTest.php | 2 +- tests/Sabre/CardDAV/Backend/Mock.php | 9 ++++ .../Request/AddressBookQueryReportTest.php | 47 ++++++++++++++++++ 8 files changed, 133 insertions(+), 7 deletions(-) diff --git a/lib/CardDAV/Plugin.php b/lib/CardDAV/Plugin.php index 0d28d5e5e0..3b3780fc40 100644 --- a/lib/CardDAV/Plugin.php +++ b/lib/CardDAV/Plugin.php @@ -491,7 +491,8 @@ protected function addressbookQueryReport($report) { $props[200]['{' . self::NS_CARDDAV . '}address-data'] = $this->convertVCard( $props[200]['{' . self::NS_CARDDAV . '}address-data'], - $vcardType + $vcardType, + $report->addressDataProperties ); } @@ -846,14 +847,26 @@ protected function negotiateVCard($input, &$mimeType = null) { * * @param string|resource $data * @param string $target + * @param array $propertiesFilter * @return string */ - protected function convertVCard($data, $target) { + protected function convertVCard($data, $target, array $propertiesFilter = null) { if (is_resource($data)) { $data = stream_get_contents($data); } $input = VObject\Reader::read($data); + if (!empty($propertiesFilter)) { + $propertiesFilter = array_merge(['UID', 'VERSION', 'FN'], $propertiesFilter); + $keys = array_unique(array_map(function($child) { + return $child->name; + }, $input->children())); + $keys = array_diff($keys, $propertiesFilter); + foreach ($keys as $key) { + unset($input->$key); + } + $data = $input->serialize(); + } $output = null; try { diff --git a/lib/CardDAV/Xml/Filter/AddressData.php b/lib/CardDAV/Xml/Filter/AddressData.php index 1e7d6d046d..4476e68bfe 100644 --- a/lib/CardDAV/Xml/Filter/AddressData.php +++ b/lib/CardDAV/Xml/Filter/AddressData.php @@ -51,6 +51,11 @@ static function xmlDeserialize(Reader $reader) { 'version' => $reader->getAttribute('version') ?: '3.0', ]; + $elems = (array)$reader->parseInnerTree(); + $result['addressDataProperties'] = array_map(function($element) { + return $element['attributes']['name']; + }, $elems); + $reader->next(); return $result; diff --git a/lib/CardDAV/Xml/Request/AddressBookQueryReport.php b/lib/CardDAV/Xml/Request/AddressBookQueryReport.php index 2818cfc342..9f6e26bb37 100644 --- a/lib/CardDAV/Xml/Request/AddressBookQueryReport.php +++ b/lib/CardDAV/Xml/Request/AddressBookQueryReport.php @@ -28,6 +28,13 @@ class AddressBookQueryReport implements XmlDeserializable { */ public $properties; + /** + * An array with requested vcard properties. + * + * @var array + */ + public $addressDataProperties = []; + /** * List of property/component filters. * diff --git a/tests/Sabre/CardDAV/AddressBookHomeTest.php b/tests/Sabre/CardDAV/AddressBookHomeTest.php index febfd842ec..c10695dd38 100644 --- a/tests/Sabre/CardDAV/AddressBookHomeTest.php +++ b/tests/Sabre/CardDAV/AddressBookHomeTest.php @@ -90,7 +90,7 @@ function testGetChild404() { function testGetChildren() { $children = $this->s->getChildren(); - $this->assertEquals(1, count($children)); + $this->assertEquals(2, count($children)); $this->assertInstanceOf('Sabre\\CardDAV\\AddressBook', $children[0]); $this->assertEquals('book1', $children[0]->getName()); @@ -109,7 +109,7 @@ function testCreateExtendedCollection() { 'uri' => 'book2', '{DAV:}displayname' => 'a-book 2', 'principaluri' => 'principals/user1', - ], $this->backend->addressBooks[1]); + ], $this->backend->addressBooks[2]); } diff --git a/tests/Sabre/CardDAV/AddressBookQueryTest.php b/tests/Sabre/CardDAV/AddressBookQueryTest.php index 65bb3f61ff..ad09073c10 100644 --- a/tests/Sabre/CardDAV/AddressBookQueryTest.php +++ b/tests/Sabre/CardDAV/AddressBookQueryTest.php @@ -288,14 +288,14 @@ function testAddressBookDepth0() { ); $request->setBody( -' + ' ' - ); + ); $response = new HTTP\ResponseMock(); @@ -305,6 +305,51 @@ function testAddressBookDepth0() { $this->server->exec(); $this->assertEquals(415, $response->status, 'Incorrect status code. Full response body:' . $response->body); + } + + function testAddressBookProperties() { + + $request = new HTTP\Request( + 'REPORT', + '/addressbooks/user1/book3', + ['Depth' => '1'] + ); + + $request->setBody( + ' + + + + + + + + +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new DAV\Client(['baseUri' => '/']); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals([ + '/addressbooks/user1/book3/card3' => [ + 200 => [ + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nFN:Test-Card\nEMAIL;TYPE=home:bar@example.org\nEND:VCARD") . '"', + '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\r\nVERSION:3.0\r\nUID:12345\r\nFN:Test-Card\r\nEND:VCARD\r\n", + ], + ], + ], $result); } } diff --git a/tests/Sabre/CardDAV/AddressBookTest.php b/tests/Sabre/CardDAV/AddressBookTest.php index 590c647ed2..1fe5182853 100644 --- a/tests/Sabre/CardDAV/AddressBookTest.php +++ b/tests/Sabre/CardDAV/AddressBookTest.php @@ -85,7 +85,7 @@ function testCreateFile() { function testDelete() { $this->ab->delete(); - $this->assertEquals([], $this->backend->addressBooks); + $this->assertEquals(1, count($this->backend->addressBooks)); } diff --git a/tests/Sabre/CardDAV/Backend/Mock.php b/tests/Sabre/CardDAV/Backend/Mock.php index 9c7ee45c31..053b52e24b 100644 --- a/tests/Sabre/CardDAV/Backend/Mock.php +++ b/tests/Sabre/CardDAV/Backend/Mock.php @@ -20,6 +20,12 @@ function __construct($addressBooks = null, $cards = null) { 'principaluri' => 'principals/user1', '{DAV:}displayname' => 'd-name', ], + [ + 'id' => 'bar', + 'uri' => 'book3', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'd-name', + ], ]; $card2 = fopen('php://memory', 'r+'); @@ -30,6 +36,9 @@ function __construct($addressBooks = null, $cards = null) { 'card1' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", 'card2' => $card2, ], + 'bar' => [ + 'card3' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nFN:Test-Card\nEMAIL;TYPE=home:bar@example.org\nEND:VCARD", + ], ]; } diff --git a/tests/Sabre/CardDAV/Xml/Request/AddressBookQueryReportTest.php b/tests/Sabre/CardDAV/Xml/Request/AddressBookQueryReportTest.php index ecd894dd31..a3f234c3e9 100644 --- a/tests/Sabre/CardDAV/Xml/Request/AddressBookQueryReportTest.php +++ b/tests/Sabre/CardDAV/Xml/Request/AddressBookQueryReportTest.php @@ -300,4 +300,51 @@ function testDeserializeDoubleFilter() { $this->parse($xml); } + + function testDeserializeAddressbookElements() { + + $xml = << + + + + + + + + + + + + + +XML; + + $result = $this->parse($xml); + $addressBookQueryReport = new AddressBookQueryReport(); + $addressBookQueryReport->properties = [ + '{DAV:}getetag', + '{urn:ietf:params:xml:ns:carddav}address-data' + ]; + $addressBookQueryReport->filters = []; + $addressBookQueryReport->test = 'anyof'; + $addressBookQueryReport->contentType = 'text/vcard'; + $addressBookQueryReport->version = '3.0'; + $addressBookQueryReport->addressDataProperties = [ + 'VERSION', + 'UID', + 'NICKNAME', + 'EMAIL', + 'FN', + 'TEL', + ]; + + $this->assertEquals( + $addressBookQueryReport, + $result['value'] + ); + + } + + }