diff --git a/apps/dav/appinfo/v1/carddav.php b/apps/dav/appinfo/v1/carddav.php index 88582d64ee..fc7aff4a63 100644 --- a/apps/dav/appinfo/v1/carddav.php +++ b/apps/dav/appinfo/v1/carddav.php @@ -75,6 +75,7 @@ if ($debugging) { } $server->addPlugin(new \Sabre\CardDAV\VCFExportPlugin()); +$server->addPlugin(new \OCA\DAV\CardDAV\ImageExportPlugin(\OC::$server->getLogger())); $server->addPlugin(new ExceptionLoggerPlugin('carddav', \OC::$server->getLogger())); // And off we go! diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php index de2056ebc3..b7b24ba763 100644 --- a/apps/dav/lib/AppInfo/Application.php +++ b/apps/dav/lib/AppInfo/Application.php @@ -136,7 +136,8 @@ class Application extends App { public function setupContactsProvider(IManager $contactsManager, $userID) { /** @var ContactsManager $cm */ $cm = $this->getContainer()->query('ContactsManager'); - $cm->setupContactsProvider($contactsManager, $userID); + $urlGenerator = $this->getContainer()->getServer()->getURLGenerator(); + $cm->setupContactsProvider($contactsManager, $userID, $urlGenerator); } public function registerHooks() { diff --git a/apps/dav/lib/CardDAV/AddressBookImpl.php b/apps/dav/lib/CardDAV/AddressBookImpl.php index 310be69518..b26f81766c 100644 --- a/apps/dav/lib/CardDAV/AddressBookImpl.php +++ b/apps/dav/lib/CardDAV/AddressBookImpl.php @@ -24,6 +24,7 @@ namespace OCA\DAV\CardDAV; use OCP\Constants; use OCP\IAddressBook; +use OCP\IURLGenerator; use Sabre\VObject\Component\VCard; use Sabre\VObject\Property\Text; use Sabre\VObject\Reader; @@ -40,21 +41,27 @@ class AddressBookImpl implements IAddressBook { /** @var AddressBook */ private $addressBook; + /** @var IURLGenerator */ + private $urlGenerator; + /** * AddressBookImpl constructor. * * @param AddressBook $addressBook * @param array $addressBookInfo * @param CardDavBackend $backend + * @param IUrlGenerator $urlGenerator */ public function __construct( AddressBook $addressBook, array $addressBookInfo, - CardDavBackend $backend) { + CardDavBackend $backend, + IURLGenerator $urlGenerator) { $this->addressBook = $addressBook; $this->addressBookInfo = $addressBookInfo; $this->backend = $backend; + $this->urlGenerator = $urlGenerator; } /** @@ -83,11 +90,11 @@ class AddressBookImpl implements IAddressBook { * @since 5.0.0 */ public function search($pattern, $searchProperties, $options) { - $result = $this->backend->search($this->getKey(), $pattern, $searchProperties); + $results = $this->backend->search($this->getKey(), $pattern, $searchProperties); $vCards = []; - foreach ($result as $cardData) { - $vCards[] = $this->vCard2Array($this->readCard($cardData)); + foreach ($results as $result) { + $vCards[] = $this->vCard2Array($result['uri'], $this->readCard($result['carddata'])); } return $vCards; @@ -100,13 +107,12 @@ class AddressBookImpl implements IAddressBook { */ public function createOrUpdate($properties) { $update = false; - if (!isset($properties['UID'])) { // create a new contact + if (!isset($properties['URI'])) { // create a new contact $uid = $this->createUid(); $uri = $uid . '.vcf'; $vCard = $this->createEmptyVCard($uid); } else { // update existing contact - $uid = $properties['UID']; - $uri = $uid . '.vcf'; + $uri = $properties['URI']; $vCardData = $this->backend->getCard($this->getKey(), $uri); $vCard = $this->readCard($vCardData['carddata']); $update = true; @@ -122,7 +128,7 @@ class AddressBookImpl implements IAddressBook { $this->backend->createCard($this->getKey(), $uri, $vCard->serialize()); } - return $this->vCard2Array($vCard); + return $this->vCard2Array($uri, $vCard); } @@ -207,13 +213,31 @@ class AddressBookImpl implements IAddressBook { /** * create array with all vCard properties * + * @param string $uri * @param VCard $vCard * @return array */ - protected function vCard2Array(VCard $vCard) { - $result = []; + protected function vCard2Array($uri, VCard $vCard) { + $result = [ + 'URI' => $uri, + ]; + foreach ($vCard->children as $property) { $result[$property->name] = $property->getValue(); + if ($property->name === 'PHOTO' && $property->getValueType() === 'BINARY') { + $url = $this->urlGenerator->getAbsoluteURL( + $this->urlGenerator->linkTo('', 'remote.php') . '/dav/'); + $url .= implode('/', [ + 'addressbooks', + substr($this->addressBookInfo['principaluri'], 11), //cut off 'principals/' + $this->addressBookInfo['uri'], + $uri + ]) . '?photo'; + + $result['PHOTO'] = 'VALUE=uri:' . $url; + } else { + $result[$property->name] = $property->getValue(); + } } if ($this->addressBookInfo['principaluri'] === 'principals/system/system' && $this->addressBookInfo['uri'] === 'system') { diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php index 3d2904df39..2ad9f4778c 100644 --- a/apps/dav/lib/CardDAV/CardDavBackend.php +++ b/apps/dav/lib/CardDAV/CardDavBackend.php @@ -780,7 +780,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { } $query2->andWhere($query2->expr()->eq('cp.addressbookid', $query->createNamedParameter($addressBookId))); - $query->select('c.carddata')->from($this->dbCardsTable, 'c') + $query->select('c.carddata', 'c.uri')->from($this->dbCardsTable, 'c') ->where($query->expr()->in('c.id', $query->createFunction($query2->getSQL()))); $result = $query->execute(); @@ -788,8 +788,10 @@ class CardDavBackend implements BackendInterface, SyncSupport { $result->closeCursor(); - return array_map(function($array) {return $this->readBlob($array['carddata']);}, $cards); - + return array_map(function($array) { + $array['carddata'] = $this->readBlob($array['carddata']); + return $array; + }, $cards); } /** diff --git a/apps/dav/lib/CardDAV/ContactsManager.php b/apps/dav/lib/CardDAV/ContactsManager.php index 7900c6ccae..ad633483fd 100644 --- a/apps/dav/lib/CardDAV/ContactsManager.php +++ b/apps/dav/lib/CardDAV/ContactsManager.php @@ -22,6 +22,7 @@ namespace OCA\DAV\CardDAV; use OCP\Contacts\IManager; +use OCP\IURLGenerator; class ContactsManager { @@ -37,26 +38,29 @@ class ContactsManager { /** * @param IManager $cm * @param string $userId + * @param IURLGenerator $urlGenerator */ - public function setupContactsProvider(IManager $cm, $userId) { + public function setupContactsProvider(IManager $cm, $userId, IURLGenerator $urlGenerator) { $addressBooks = $this->backend->getAddressBooksForUser("principals/users/$userId"); - $this->register($cm, $addressBooks); + $this->register($cm, $addressBooks, $urlGenerator); $addressBooks = $this->backend->getAddressBooksForUser("principals/system/system"); - $this->register($cm, $addressBooks); + $this->register($cm, $addressBooks, $urlGenerator); } /** * @param IManager $cm * @param $addressBooks + * @param IURLGenerator $urlGenerator */ - private function register(IManager $cm, $addressBooks) { + private function register(IManager $cm, $addressBooks, $urlGenerator) { foreach ($addressBooks as $addressBookInfo) { $addressBook = new \OCA\DAV\CardDAV\AddressBook($this->backend, $addressBookInfo); $cm->registerAddressBook( new AddressBookImpl( $addressBook, $addressBookInfo, - $this->backend + $this->backend, + $urlGenerator ) ); } diff --git a/apps/dav/lib/CardDAV/ImageExportPlugin.php b/apps/dav/lib/CardDAV/ImageExportPlugin.php new file mode 100644 index 0000000000..3f50522249 --- /dev/null +++ b/apps/dav/lib/CardDAV/ImageExportPlugin.php @@ -0,0 +1,146 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\DAV\CardDAV; + +use OCP\ILogger; +use Sabre\CardDAV\Card; +use Sabre\DAV\Server; +use Sabre\DAV\ServerPlugin; +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; +use Sabre\VObject\Parameter; +use Sabre\VObject\Property\Binary; +use Sabre\VObject\Reader; + +class ImageExportPlugin extends ServerPlugin { + + /** @var Server */ + protected $server; + /** @var ILogger */ + private $logger; + + public function __construct(ILogger $logger) { + $this->logger = $logger; + } + + /** + * Initializes the plugin and registers event handlers + * + * @param Server $server + * @return void + */ + function initialize(Server $server) { + + $this->server = $server; + $this->server->on('method:GET', [$this, 'httpGet'], 90); + } + + /** + * Intercepts GET requests on addressbook urls ending with ?photo. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool|void + */ + function httpGet(RequestInterface $request, ResponseInterface $response) { + + $queryParams = $request->getQueryParameters(); + // TODO: in addition to photo we should also add logo some point in time + if (!array_key_exists('photo', $queryParams)) { + return true; + } + + $path = $request->getPath(); + $node = $this->server->tree->getNodeForPath($path); + + if (!($node instanceof Card)) { + return true; + } + + $this->server->transactionType = 'carddav-image-export'; + + // Checking ACL, if available. + if ($aclPlugin = $this->server->getPlugin('acl')) { + /** @var \Sabre\DAVACL\Plugin $aclPlugin */ + $aclPlugin->checkPrivileges($path, '{DAV:}read'); + } + + if ($result = $this->getPhoto($node)) { + $response->setHeader('Content-Type', $result['Content-Type']); + $response->setStatus(200); + + $response->setBody($result['body']); + + // Returning false to break the event chain + return false; + } + return true; + } + + function getPhoto(Card $node) { + // TODO: this is kind of expensive - load carddav data from database and parse it + // we might want to build up a cache one day + try { + $vObject = $this->readCard($node->get()); + if (!$vObject->PHOTO) { + return false; + } + + $photo = $vObject->PHOTO; + $type = $this->getType($photo); + + $valType = $photo->getValueType(); + $val = ($valType === 'URI' ? $photo->getRawMimeDirValue() : $photo->getValue()); + return [ + 'Content-Type' => $type, + 'body' => $val + ]; + } catch(\Exception $ex) { + $this->logger->logException($ex); + } + return false; + } + + private function readCard($cardData) { + return Reader::read($cardData); + } + + /** + * @param Binary $photo + * @return Parameter + */ + private function getType($photo) { + $params = $photo->parameters(); + if (isset($params['TYPE']) || isset($params['MEDIATYPE'])) { + /** @var Parameter $typeParam */ + $typeParam = isset($params['TYPE']) ? $params['TYPE'] : $params['MEDIATYPE']; + $type = $typeParam->getValue(); + + if (strpos($type, 'image/') === 0) { + return $type; + } else { + return 'image/' . strtolower($type); + } + } + return ''; + } +} diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index e150f441b8..7fa1b13783 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -25,6 +25,7 @@ namespace OCA\DAV; use OCA\DAV\CalDAV\Schedule\IMipPlugin; +use OCA\DAV\CardDAV\ImageExportPlugin; use OCA\DAV\Connector\Sabre\Auth; use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin; use OCA\DAV\Connector\Sabre\DavAclPlugin; @@ -103,6 +104,7 @@ class Server { // addressbook plugins $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin()); $this->server->addPlugin(new VCFExportPlugin()); + $this->server->addPlugin(new ImageExportPlugin(\OC::$server->getLogger())); // system tags plugins $this->server->addPlugin(new \OCA\DAV\SystemTag\SystemTagPlugin( diff --git a/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php b/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php index ddfec3dc3a..4ccc841157 100644 --- a/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php +++ b/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php @@ -43,6 +43,9 @@ class AddressBookImplTest extends TestCase { /** @var AddressBook | \PHPUnit_Framework_MockObject_MockObject */ private $addressBook; + /** @var \OCP\IURLGenerator | \PHPUnit_Framework_MockObject_MockObject */ + private $urlGenerator; + /** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject */ private $backend; @@ -61,11 +64,13 @@ class AddressBookImplTest extends TestCase { $this->backend = $this->getMockBuilder('\OCA\DAV\CardDAV\CardDavBackend') ->disableOriginalConstructor()->getMock(); $this->vCard = $this->getMock('Sabre\VObject\Component\VCard'); + $this->urlGenerator = $this->getMock('OCP\IURLGenerator'); $this->addressBookImpl = new AddressBookImpl( $this->addressBook, $this->addressBookInfo, - $this->backend + $this->backend, + $this->urlGenerator ); } @@ -87,7 +92,8 @@ class AddressBookImplTest extends TestCase { [ $this->addressBook, $this->addressBookInfo, - $this->backend + $this->backend, + $this->urlGenerator, ] ) ->setMethods(['vCard2Array', 'readCard']) @@ -100,15 +106,18 @@ class AddressBookImplTest extends TestCase { ->with($this->addressBookInfo['id'], $pattern, $searchProperties) ->willReturn( [ - 'cardData1', - 'cardData2' + ['uri' => 'foo.vcf', 'carddata' => 'cardData1'], + ['uri' => 'bar.vcf', 'carddata' => 'cardData2'] ] ); $addressBookImpl->expects($this->exactly(2))->method('readCard') ->willReturn($this->vCard); $addressBookImpl->expects($this->exactly(2))->method('vCard2Array') - ->with($this->vCard)->willReturn('vCard'); + ->withConsecutive( + ['foo.vcf', $this->vCard], + ['bar.vcf', $this->vCard] + )->willReturn('vCard'); $result = $addressBookImpl->search($pattern, $searchProperties, []); $this->assertTrue((is_array($result))); @@ -130,7 +139,8 @@ class AddressBookImplTest extends TestCase { [ $this->addressBook, $this->addressBookInfo, - $this->backend + $this->backend, + $this->urlGenerator, ] ) ->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard']) @@ -146,7 +156,7 @@ class AddressBookImplTest extends TestCase { $this->backend->expects($this->never())->method('updateCard'); $this->backend->expects($this->never())->method('getCard'); $addressBookImpl->expects($this->once())->method('vCard2Array') - ->with($this->vCard)->willReturn(true); + ->with('uid.vcf', $this->vCard)->willReturn(true); $this->assertTrue($addressBookImpl->createOrUpdate($properties)); } @@ -161,7 +171,8 @@ class AddressBookImplTest extends TestCase { public function testUpdate() { $uid = 'uid'; - $properties = ['UID' => $uid, 'FN' => 'John Doe']; + $uri = 'bla.vcf'; + $properties = ['URI' => $uri, 'UID' => $uid, 'FN' => 'John Doe']; /** @var \PHPUnit_Framework_MockObject_MockObject | AddressBookImpl $addressBookImpl */ $addressBookImpl = $this->getMockBuilder('OCA\DAV\CardDAV\AddressBookImpl') @@ -169,7 +180,8 @@ class AddressBookImplTest extends TestCase { [ $this->addressBook, $this->addressBookInfo, - $this->backend + $this->backend, + $this->urlGenerator, ] ) ->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard', 'readCard']) @@ -178,7 +190,7 @@ class AddressBookImplTest extends TestCase { $addressBookImpl->expects($this->never())->method('createUid'); $addressBookImpl->expects($this->never())->method('createEmptyVCard'); $this->backend->expects($this->once())->method('getCard') - ->with($this->addressBookInfo['id'], $uid . '.vcf') + ->with($this->addressBookInfo['id'], $uri) ->willReturn(['carddata' => 'data']); $addressBookImpl->expects($this->once())->method('readCard') ->with('data')->willReturn($this->vCard); @@ -187,7 +199,7 @@ class AddressBookImplTest extends TestCase { $this->backend->expects($this->never())->method('createCard'); $this->backend->expects($this->once())->method('updateCard'); $addressBookImpl->expects($this->once())->method('vCard2Array') - ->with($this->vCard)->willReturn(true); + ->with($uri, $this->vCard)->willReturn(true); $this->assertTrue($addressBookImpl->createOrUpdate($properties)); } @@ -251,7 +263,8 @@ class AddressBookImplTest extends TestCase { [ $this->addressBook, $this->addressBookInfo, - $this->backend + $this->backend, + $this->urlGenerator, ] ) ->setMethods(['getUid']) diff --git a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php index c7cd4a3005..58a93befe6 100644 --- a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php +++ b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php @@ -535,8 +535,8 @@ class CardDavBackendTest extends TestCase { $found = []; foreach ($result as $r) { foreach ($expected as $exp) { - if (strpos($r, $exp) > 0) { - $found[$exp] = true; + if ($r['uri'] === $exp[0] && strpos($r['carddata'], $exp[1]) > 0) { + $found[$exp[1]] = true; break; } } @@ -547,11 +547,11 @@ class CardDavBackendTest extends TestCase { public function dataTestSearch() { return [ - ['John', ['FN'], ['John Doe', 'John M. Doe']], - ['M. Doe', ['FN'], ['John M. Doe']], - ['Do', ['FN'], ['John Doe', 'John M. Doe']], - 'check if duplicates are handled correctly' => ['John', ['FN', 'CLOUD'], ['John Doe', 'John M. Doe']], - 'case insensitive' => ['john', ['FN'], ['John Doe', 'John M. Doe']] + ['John', ['FN'], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]], + ['M. Doe', ['FN'], [['uri1', 'John M. Doe']]], + ['Do', ['FN'], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]], + 'check if duplicates are handled correctly' => ['John', ['FN', 'CLOUD'], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]], + 'case insensitive' => ['john', ['FN'], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]] ]; } diff --git a/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php b/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php index 2abd9da66e..23a49a1962 100644 --- a/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php +++ b/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php @@ -32,6 +32,7 @@ class ContactsManagerTest extends TestCase { /** @var IManager | \PHPUnit_Framework_MockObject_MockObject $cm */ $cm = $this->getMockBuilder('OCP\Contacts\IManager')->disableOriginalConstructor()->getMock(); $cm->expects($this->exactly(2))->method('registerAddressBook'); + $urlGenerator = $this->getMockBuilder('OCP\IUrlGenerator')->disableOriginalConstructor()->getMock(); /** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject $backEnd */ $backEnd = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock(); $backEnd->method('getAddressBooksForUser')->willReturn([ @@ -39,6 +40,6 @@ class ContactsManagerTest extends TestCase { ]); $app = new ContactsManager($backEnd); - $app->setupContactsProvider($cm, 'user01'); + $app->setupContactsProvider($cm, 'user01', $urlGenerator); } } diff --git a/apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php b/apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php new file mode 100644 index 0000000000..8583df0b6f --- /dev/null +++ b/apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php @@ -0,0 +1,151 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + + +namespace OCA\DAV\Tests\unit\CardDAV; + + +use OCA\DAV\CardDAV\ImageExportPlugin; +use OCP\ILogger; +use Sabre\CardDAV\Card; +use Sabre\DAV\Server; +use Sabre\DAV\Tree; +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; +use Test\TestCase; + +class ImageExportPluginTest extends TestCase { + + /** @var ResponseInterface | \PHPUnit_Framework_MockObject_MockObject */ + private $response; + /** @var RequestInterface | \PHPUnit_Framework_MockObject_MockObject */ + private $request; + /** @var ImageExportPlugin | \PHPUnit_Framework_MockObject_MockObject */ + private $plugin; + /** @var Server */ + private $server; + /** @var Tree | \PHPUnit_Framework_MockObject_MockObject */ + private $tree; + /** @var ILogger | \PHPUnit_Framework_MockObject_MockObject */ + private $logger; + + function setUp() { + parent::setUp(); + + $this->request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')->getMock(); + $this->response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')->getMock(); + $this->server = $this->getMockBuilder('Sabre\DAV\Server')->getMock(); + $this->tree = $this->getMockBuilder('Sabre\DAV\Tree')->disableOriginalConstructor()->getMock(); + $this->server->tree = $this->tree; + $this->logger = $this->getMockBuilder('\OCP\ILogger')->getMock(); + + $this->plugin = $this->getMock('OCA\DAV\CardDAV\ImageExportPlugin', ['getPhoto'], [$this->logger]); + $this->plugin->initialize($this->server); + } + + /** + * @dataProvider providesQueryParams + * @param $param + */ + public function testQueryParams($param) { + $this->request->expects($this->once())->method('getQueryParameters')->willReturn($param); + $result = $this->plugin->httpGet($this->request, $this->response); + $this->assertTrue($result); + } + + public function providesQueryParams() { + return [ + [[]], + [['1']], + [['foo' => 'bar']], + ]; + } + + public function testNotACard() { + $this->request->expects($this->once())->method('getQueryParameters')->willReturn(['photo' => true]); + $this->request->expects($this->once())->method('getPath')->willReturn('/files/welcome.txt'); + $this->tree->expects($this->once())->method('getNodeForPath')->with('/files/welcome.txt')->willReturn(null); + $result = $this->plugin->httpGet($this->request, $this->response); + $this->assertTrue($result); + } + + /** + * @dataProvider providesCardWithOrWithoutPhoto + * @param bool $expected + * @param array $getPhotoResult + */ + public function testCardWithOrWithoutPhoto($expected, $getPhotoResult) { + $this->request->expects($this->once())->method('getQueryParameters')->willReturn(['photo' => true]); + $this->request->expects($this->once())->method('getPath')->willReturn('/files/welcome.txt'); + + $card = $this->getMockBuilder('Sabre\CardDAV\Card')->disableOriginalConstructor()->getMock(); + $this->tree->expects($this->once())->method('getNodeForPath')->with('/files/welcome.txt')->willReturn($card); + + $this->plugin->expects($this->once())->method('getPhoto')->willReturn($getPhotoResult); + + if (!$expected) { + $this->response->expects($this->once())->method('setHeader'); + $this->response->expects($this->once())->method('setStatus'); + $this->response->expects($this->once())->method('setBody'); + } + + $result = $this->plugin->httpGet($this->request, $this->response); + $this->assertEquals($expected, $result); + } + + public function providesCardWithOrWithoutPhoto() { + return [ + [true, null], + [false, ['Content-Type' => 'image/jpeg', 'body' => '1234']], + ]; + } + + /** + * @dataProvider providesPhotoData + * @param $expected + * @param $cardData + */ + public function testGetPhoto($expected, $cardData) { + /** @var Card | \PHPUnit_Framework_MockObject_MockObject $card */ + $card = $this->getMockBuilder('Sabre\CardDAV\Card')->disableOriginalConstructor()->getMock(); + $card->expects($this->once())->method('get')->willReturn($cardData); + + $this->plugin = new ImageExportPlugin($this->logger); + $this->plugin->initialize($this->server); + + $result = $this->plugin->getPhoto($card); + $this->assertEquals($expected, $result); + } + + public function providesPhotoData() { + return [ + 'empty vcard' => [false, ''], + 'vcard without PHOTO' => [false, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n"], + 'vcard 3 with PHOTO' => [['Content-Type' => 'image/jpeg', 'body' => '12345'], "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO;ENCODING=b;TYPE=JPEG:MTIzNDU=\r\nEND:VCARD\r\n"], + // + // TODO: these three below are not working - needs debugging + // + //'vcard 3 with PHOTO URL' => [['Content-Type' => 'image/jpeg', 'body' => '12345'], "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO;TYPE=JPEG:http://example.org/photo.jpg\r\nEND:VCARD\r\n"], + //'vcard 4 with PHOTO' => [['Content-Type' => 'image/jpeg', 'body' => '12345'], "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO:data:image/jpeg;MTIzNDU=\r\nEND:VCARD\r\n"], + 'vcard 4 with PHOTO URL' => [['Content-Type' => 'image/jpeg', 'body' => 'http://example.org/photo.jpg'], "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO;MEDIATYPE=image/jpeg:http://example.org/photo.jpg\r\nEND:VCARD\r\n"], + ]; + } +}