diff --git a/appinfo/routes.php b/appinfo/routes.php index b05194360..93fa5662a 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -39,6 +39,8 @@ ['name' => 'contact#searchPhoto', 'url' => '/v1/autocompletion/photo', 'verb' => 'POST'], // Circles ['name' => 'contact#getCircleMembers', 'url' => '/v1/circles/getmembers', 'verb' => 'GET'], + // Contact Groups + ['name' => 'contact#getContactGroupMembers', 'url' => '/v1/autocompletion/groupmembers', 'verb' => 'POST'], // Settings ['name' => 'settings#setConfig', 'url' => '/v1/config/{key}', 'verb' => 'POST'], // Tools diff --git a/lib/Controller/ContactController.php b/lib/Controller/ContactController.php index af1ee7518..702258696 100644 --- a/lib/Controller/ContactController.php +++ b/lib/Controller/ContactController.php @@ -7,11 +7,14 @@ */ namespace OCA\Calendar\Controller; +use Exception; +use OCA\Calendar\Service\ContactsService; use OCA\Calendar\Service\ServiceException; use OCA\Circles\Exceptions\CircleNotFoundException; use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\QueryException; use OCP\Contacts\IManager; @@ -24,31 +27,21 @@ * @package OCA\Calendar\Controller */ class ContactController extends Controller { - /** @var IManager */ - private $contactsManager; - - /** @var IAppManager */ - private $appManager; - - /** @var IUserManager */ - private $userManager; - /** * ContactController constructor. * * @param string $appName * @param IRequest $request - * @param IManager $contacts */ - public function __construct(string $appName, + public function __construct( + string $appName, IRequest $request, - IManager $contacts, - IAppManager $appManager, - IUserManager $userManager) { + private IManager $contactsManager, + private IAppManager $appManager, + private IUserManager $userManager, + private ContactsService $contactsService, + ) { parent::__construct($appName, $request); - $this->contactsManager = $contacts; - $this->appManager = $appManager; - $this->userManager = $userManager; } /** @@ -59,7 +52,7 @@ public function __construct(string $appName, * * @NoAdminRequired */ - public function searchLocation(string $search):JSONResponse { + public function searchLocation(string $search): JSONResponse { if (!$this->contactsManager->isEnabled()) { return new JSONResponse(); } @@ -69,7 +62,7 @@ public function searchLocation(string $search):JSONResponse { $contacts = []; foreach ($result as $r) { // Information about system users is fetched via DAV nowadays - if (isset($r['isLocalSystemBook']) && $r['isLocalSystemBook']) { + if ($this->contactsService->isSystemBook($r)) { continue; } @@ -77,19 +70,9 @@ public function searchLocation(string $search):JSONResponse { continue; } - $name = $this->getNameFromContact($r); - if (\is_string($r['ADR'])) { - $r['ADR'] = [$r['ADR']]; - } - - $photo = isset($r['PHOTO']) - ? $this->getPhotoUri($r['PHOTO']) - : null; - - $addresses = []; - foreach ($r['ADR'] as $address) { - $addresses[] = trim(preg_replace("/\n+/", "\n", str_replace(';', "\n", $address))); - } + $name = $this->contactsService->getNameFromContact($r); + $photo = $this->contactsService->getPhotoUri($r); + $addresses = $this->contactsService->getAddress($r); $contacts[] = [ 'name' => $name, @@ -119,51 +102,78 @@ public function searchAttendee(string $search):JSONResponse { $contacts = []; foreach ($result as $r) { - // Information about system users is fetched via DAV nowadays - if (isset($r['isLocalSystemBook']) && $r['isLocalSystemBook']) { + if ($this->contactsService->isSystemBook($r) || !$this->contactsService->hasEmail($r)) { continue; } - - if (!isset($r['EMAIL'])) { + $name = $this->contactsService->getNameFromContact($r); + $email = $this->contactsService->getEmail($r); + $photo = $this->contactsService->getPhotoUri($r); + $timezoneId = $this->contactsService->getTimezoneId($r); + $lang = $this->contactsService->getLanguageId($r); + $contacts[] = [ + 'name' => $name, + 'emails' => $email, + 'lang' => $lang, + 'tzid' => $timezoneId, + 'photo' => $photo, + 'type' => 'individual' + ]; + } + + $groups = $this->contactsManager->search($search, ['CATEGORIES']); + $groups = array_filter($groups, function ($group) { + return $this->contactsService->hasEmail($group); + }); + $filtered = $this->contactsService->filterGroupsWithCount($groups, $search); + foreach ($filtered as $groupName => $count) { + if ($count === 0) { continue; } + $contacts[] = [ + 'name' => $groupName, + 'emails' => ['mailto:group+' . urlencode($groupName) . '@group'], + 'lang' => '', + 'tzid' => '', + 'photo' => '', + 'type' => 'contactsgroup', + 'members' => $count, + ]; + } - $name = $this->getNameFromContact($r); - if (\is_string($r['EMAIL'])) { - $r['EMAIL'] = [$r['EMAIL']]; - } + return new JSONResponse($contacts); + } - $photo = isset($r['PHOTO']) - ? $this->getPhotoUri($r['PHOTO']) - : null; + #[NoAdminRequired] + public function getContactGroupMembers(string $groupName): JSONResponse { + if (!$this->contactsManager->isEnabled()) { + return new JSONResponse(); + } - $lang = null; - if (isset($r['LANG'])) { - if (\is_array($r['LANG'])) { - $lang = $r['LANG'][0]; - } else { - $lang = $r['LANG']; - } + $groupmembers = $this->contactsManager->search($groupName, ['CATEGORIES']); + $contacts = []; + foreach ($groupmembers as $r) { + if (!in_array($groupName, explode(',', $r['CATEGORIES']), true)) { + continue; } - - $timezoneId = null; - if (isset($r['TZ'])) { - if (\is_array($r['TZ'])) { - $timezoneId = $r['TZ'][0]; - } else { - $timezoneId = $r['TZ']; - } + if (!$this->contactsService->hasEmail($r) || $this->contactsService->isSystemBook($r)) { + continue; } - + $name = $this->contactsService->getNameFromContact($r); + $email = $this->contactsService->getEmail($r); + $photo = $this->contactsService->getPhotoUri($r); + $timezoneId = $this->contactsService->getTimezoneId($r); + $lang = $this->contactsService->getLanguageId($r); $contacts[] = [ - 'name' => $name, - 'emails' => $r['EMAIL'], - 'lang' => $lang, - 'tzid' => $timezoneId, - 'photo' => $photo, + 'commonName' => $name, + 'email' => $email[0], + 'calendarUserType' => 'INDIVIDUAL', + 'language' => $lang, + 'timezoneId' => $timezoneId, + 'avatar' => $photo, + 'isUser' => false, + 'member' => 'mailto:group+' . urlencode($groupName) . '@group', ]; } - return new JSONResponse($contacts); } @@ -243,17 +253,14 @@ public function searchPhoto(string $search):JSONResponse { $result = $this->contactsManager->search($search, ['EMAIL']); foreach ($result as $r) { - if (!isset($r['EMAIL'])) { + if (!$this->contactsService->hasEmail($r) || $this->contactsService->isSystemBook($r)) { continue; } - - if (\is_string($r['EMAIL'])) { - $r['EMAIL'] = [$r['EMAIL']]; - } + $email = $this->contactsService->getEmail($r); $match = false; - foreach ($r['EMAIL'] as $email) { - if ($email === $search) { + foreach ($email as $e) { + if ($e === $search) { $match = true; } } @@ -262,15 +269,12 @@ public function searchPhoto(string $search):JSONResponse { continue; } - if (!isset($r['PHOTO'])) { + $photo = $this->contactsService->getPhotoUri($r); + if ($photo === null) { continue; } - $name = $this->getNameFromContact($r); - $photo = $this->getPhotoUri($r['PHOTO']); - if (!$photo) { - continue; - } + $name = $this->contactsService->getNameFromContact($r); return new JSONResponse([ 'name' => $name, @@ -281,28 +285,4 @@ public function searchPhoto(string $search):JSONResponse { return new JSONResponse([], Http::STATUS_NOT_FOUND); } - /** - * Extract name from an array containing a contact's information - * - * @param array $r - * @return string - */ - private function getNameFromContact(array $r):string { - return $r['FN'] ?? ''; - } - - /** - * Get photo uri from contact - * - * @param string $raw - * @return string|null - */ - private function getPhotoUri(string $raw):?string { - $uriPrefix = 'VALUE=uri:'; - if (substr($raw, 0, strlen($uriPrefix)) === $uriPrefix) { - return substr($raw, strpos($raw, 'http')); - } - - return null; - } } diff --git a/lib/Service/ContactsService.php b/lib/Service/ContactsService.php new file mode 100644 index 000000000..2d35e3b54 --- /dev/null +++ b/lib/Service/ContactsService.php @@ -0,0 +1,144 @@ + {{ option.commonName }} -
+
{{ option.email }}
-
+
{{ option.subtitle }}
@@ -161,6 +161,21 @@ export default { showInfo(this.$t('calendar', 'Note that members of circles get invited but are not synced yet.')) this.resolveCircleMembers(selectedValue.id, selectedValue.email) } + if (selectedValue.type === 'contactsgroup') { + showInfo(this.$t('calendar', 'Note that members of contact groups get invited but are not synced yet.')) + this.getContactGroupMembers(selectedValue.commonName) + let group = { + calendarUserType: 'GROUP', + commonName: selectedValue.commonName, + dropdownName: selectedValue.dropdownName, + email: selectedValue.email, + isUser: false, + subtitle: selectedValue.subtitle, + type: 'contactsgroup', + } + this.$emit('add-attendee', group) + return + } this.$emit('add-attendee', selectedValue) }, async resolveCircleMembers(circleId, groupId) { @@ -180,6 +195,23 @@ export default { } }) }, + async getContactGroupMembers(groupName) { + let results + try { + results = await HttpClient.post(linkTo('calendar', 'index.php') + '/v1/autocompletion/groupmembers', { + groupName, + }) + } catch (error) { + console.debug(error) + return [] + } + + results.data.forEach((member) => { + if (!this.organizer || member.email !== this.organizer.uri) { + this.$emit('add-attendee', member) + } + }) + }, async findAttendeesFromContactsAPI(query) { let response @@ -210,6 +242,24 @@ export default { return } + if(result.type === 'contactsgroup') { + arr.push({ + calendarUserType: 'GROUP', + commonName: result.name, + subtitle: this.$n('calendar', '%n member', '%n members', result.members), + members: {length: result.members}, + email, + isUser: false, + avatar: result.photo, + language: result.lang, + timezoneId: result.tzid, + hasMultipleEMails: false, + dropdownName: name, + type: 'contactsgroup', + }) + return + } + arr.push({ calendarUserType: 'INDIVIDUAL', commonName: result.name, @@ -246,7 +296,7 @@ export default { // We do not support GROUPS for now if (principal.calendarUserType === 'GROUP') { - return false + console.debug(principal) } // Do not include resources and rooms diff --git a/src/views/EditSidebar.vue b/src/views/EditSidebar.vue index ffec6df23..c0a1cb843 100644 --- a/src/views/EditSidebar.vue +++ b/src/views/EditSidebar.vue @@ -522,7 +522,6 @@ export default { this.showPreloader = true if (!this.isPrivate()) { this.showModalNewAttachments.map(async (attachment, i) => { - // console.log('Add share', attachment) this.sharedProgress = Math.ceil(100 * (i + 1) / total) // add share + change attachment diff --git a/tests/php/unit/Controller/ContactControllerTest.php b/tests/php/unit/Controller/ContactControllerTest.php index 3b176ee3c..afba3dfb7 100644 --- a/tests/php/unit/Controller/ContactControllerTest.php +++ b/tests/php/unit/Controller/ContactControllerTest.php @@ -8,6 +8,7 @@ namespace OCA\Calendar\Controller; use ChristophWurst\Nextcloud\Testing\TestCase; +use OCA\Calendar\Service\ContactsService; use OCP\App\IAppManager; use OCP\AppFramework\Http\JSONResponse; use OCP\Contacts\IManager; @@ -30,6 +31,7 @@ class ContactControllerTest extends TestCase { /** @var IUserManager|MockObject */ private $userManager; + private ContactsService|MockObject $service; /** @var ContactController */ protected $controller; @@ -42,17 +44,22 @@ protected function setUp():void { $this->manager = $this->createMock(IManager::class); $this->appManager = $this->createMock(IAppManager::class); $this->userManager = $this->createMock(IUserManager::class); + $this->service = $this->createMock(ContactsService::class); $this->controller = new ContactController($this->appName, - $this->request, $this->manager, $this->appManager, $this->userManager); + $this->request, + $this->manager, + $this->appManager, + $this->userManager, + $this->service + ); } public function testSearchLocationDisabled():void { - $this->manager->expects($this->once()) + $this->manager->expects(self::once()) ->method('isEnabled') - ->with() ->willReturn(false); - $this->manager->expects($this->never()) + $this->manager->expects(self::never()) ->method('search'); $response = $this->controller->searchLocation('search 123'); @@ -63,57 +70,89 @@ public function testSearchLocationDisabled():void { } public function testSearchLocation():void { + $user1 = [ + 'FN' => 'Person 1', + 'ADR' => [ + '33 42nd Street;Random Town;Some State;;United States', + ';;5 Random Ave;12782 Some big city;Yet another state;United States', + ], + 'EMAIL' => [ + 'foo1@example.com', + 'foo2@example.com', + ], + 'LANG' => [ + 'de', + 'en' + ], + 'TZ' => [ + 'Europe/Berlin', + 'UTC' + ], + 'PHOTO' => 'VALUE=uri:http://foo.bar' + ]; + $user2 = [ + 'FN' => 'Person 2', + 'EMAIL' => 'foo3@example.com', + ]; + $user3 = [ + 'ADR' => [ + 'ABC Street 2;01337 Village;;Germany', + ], + 'LANG' => 'en_us', + 'TZ' => 'Australia/Adelaide', + 'PHOTO' => 'VALUE:BINARY:4242424242' + ]; + $user4 = [ + 'isLocalSystemBook' => true, + 'FN' => 'Person 3', + 'ADR' => [ + 'ABC Street 2;01337 Village;;Germany', + ], + 'LANG' => 'en_us', + 'TZ' => 'Australia/Adelaide', + 'PHOTO' => 'VALUE:BINARY:4242424242' + ]; + $this->manager->expects(self::once()) ->method('isEnabled') - ->with() ->willReturn(true); - + $this->service + ->method('isSystemBook') + ->willReturnMap([ + [$user1, false], + [$user2, false], + [$user3, false], + [$user4, true], + ]); + $this->service + ->method('getNameFromContact') + ->willReturnMap([ + [$user1, 'Person 1'], + [$user3, ''], + ]); + $this->service->method('getPhotoUri') + ->willReturnMap([ + [$user1, 'http://foo.bar'], + [$user3, null] + ]); + $this->service->method('getAddress') + ->willReturnMap([ + [$user1, [ + "33 42nd Street\nRandom Town\nSome State\nUnited States", + "5 Random Ave\n12782 Some big city\nYet another state\nUnited States", + ]], + [$user3, [ + "ABC Street 2\n01337 Village\nGermany", + ]], + ]); $this->manager->expects(self::once()) ->method('search') ->with('search 123', ['FN', 'ADR']) ->willReturn([ - [ - 'FN' => 'Person 1', - 'ADR' => [ - '33 42nd Street;Random Town;Some State;;United States', - ';;5 Random Ave;12782 Some big city;Yet another state;United States', - ], - 'EMAIL' => [ - 'foo1@example.com', - 'foo2@example.com', - ], - 'LANG' => [ - 'de', - 'en' - ], - 'TZ' => [ - 'Europe/Berlin', - 'UTC' - ], - 'PHOTO' => 'VALUE=uri:http://foo.bar' - ], - [ - 'FN' => 'Person 2', - 'EMAIL' => 'foo3@example.com', - ], - [ - 'ADR' => [ - 'ABC Street 2;01337 Village;;Germany', - ], - 'LANG' => 'en_us', - 'TZ' => 'Australia/Adelaide', - 'PHOTO' => 'VALUE:BINARY:4242424242' - ], - [ - 'isLocalSystemBook' => true, - 'FN' => 'Person 3', - 'ADR' => [ - 'ABC Street 2;01337 Village;;Germany', - ], - 'LANG' => 'en_us', - 'TZ' => 'Australia/Adelaide', - 'PHOTO' => 'VALUE:BINARY:4242424242' - ], + $user1, + $user2, + $user3, + $user4, ]); $response = $this->controller->searchLocation('search 123'); @@ -138,31 +177,44 @@ public function testSearchLocation():void { $this->assertEquals(200, $response->getStatus()); } - public function testSearchAttendeeDisabled():void { - $this->manager->expects($this->once()) + public function testGetGroupMembersNoResults() { + $this->manager->expects(self::once()) ->method('isEnabled') - ->with() - ->willReturn(false); - - $this->manager->expects($this->never()) - ->method('search'); + ->willReturn(true); - $response = $this->controller->searchAttendee('search 123'); + $groupname = 'groupname'; + $this->manager->expects(self::once()) + ->method('search') + ->with($groupname, ['CATEGORIES']) + ->willReturn([]); - $this->assertInstanceOf(JSONResponse::class, $response); - $this->assertEquals([], $response->getData()); - $this->assertEquals(200, $response->getStatus()); + $this->controller->getContactGroupMembers($groupname); } - public function testSearchAttendee():void { + public function testGetGroupMembers() { $this->manager->expects(self::once()) ->method('isEnabled') - ->with() ->willReturn(true); - + $this->service->expects(self::once()) + ->method('hasEmail') + ->willReturn(true); + $this->service->expects(self::once()) + ->method('getNameFromContact') + ->willReturn('Person 1'); + $this->service->expects(self::once()) + ->method('getLanguageId') + ->willReturn('en_us'); + $this->service->expects(self::once()) + ->method('getTimezoneId') + ->willReturn('Australia/Adelaide'); + $this->service->expects(self::once()) + ->method('getEmail') + ->willReturn(['foo1@example.com']); + + $groupname = 'group'; $this->manager->expects(self::once()) ->method('search') - ->with('search 123', ['FN', 'EMAIL']) + ->with($groupname, ['CATEGORIES']) ->willReturn([ [ 'FN' => 'Person 1', @@ -182,11 +234,13 @@ public function testSearchAttendee():void { 'Europe/Berlin', 'UTC' ], - 'PHOTO' => 'VALUE=uri:http://foo.bar' + 'PHOTO' => 'VALUE=uri:http://foo.bar', + 'CATEGORIES' => 'groupname,group', ], [ 'FN' => 'Person 2', 'EMAIL' => 'foo3@example.com', + 'CATEGORIES' => 'groups,asecondgroup', ], [ 'ADR' => [ @@ -194,7 +248,8 @@ public function testSearchAttendee():void { ], 'LANG' => 'en_us', 'TZ' => 'Australia/Adelaide', - 'PHOTO' => 'VALUE:BINARY:4242424242' + 'PHOTO' => 'VALUE:BINARY:4242424242', + 'CATEGORIES' => 'agroupthatswrong,asecondgroup', ], [ 'isLocalSystemBook' => true, @@ -202,12 +257,141 @@ public function testSearchAttendee():void { 'ADR' => [ 'ABC Street 2;01337 Village;;Germany', ], + 'EMAIL' => 'foo4@example.com', 'LANG' => 'en_us', 'TZ' => 'Australia/Adelaide', - 'PHOTO' => 'VALUE:BINARY:4242424242' + 'PHOTO' => 'VALUE:BINARY:4242424242', + 'CATEGORIES' => 'groupppppp', ], ]); + $groupmembers = $this->controller->getContactGroupMembers($groupname); + $this->assertCount(1, $groupmembers->getData()); + } + + public function testSearchAttendeeDisabled():void { + $this->manager->expects(self::once()) + ->method('isEnabled') + ->willReturn(false); + + $this->manager->expects(self::never()) + ->method('search'); + + $response = $this->controller->searchAttendee('search 123'); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals([], $response->getData()); + $this->assertEquals(200, $response->getStatus()); + } + + public function testSearchAttendee():void { + $user1 = [ + 'FN' => 'Person 1', + 'ADR' => [ + '33 42nd Street;Random Town;Some State;;United States', + ';;5 Random Ave;12782 Some big city;Yet another state;United States', + ], + 'EMAIL' => [ + 'foo1@example.com', + 'foo2@example.com', + ], + 'LANG' => [ + 'de', + 'en' + ], + 'TZ' => [ + 'Europe/Berlin', + 'UTC' + ], + 'PHOTO' => 'VALUE=uri:http://foo.bar' + ]; + $user2 = [ + 'FN' => 'Person 2', + 'EMAIL' => 'foo3@example.com', + ]; + $user3 = [ + 'ADR' => [ + 'ABC Street 2;01337 Village;;Germany', + ], + 'LANG' => 'en_us', + 'TZ' => 'Australia/Adelaide', + 'PHOTO' => 'VALUE:BINARY:4242424242' + ]; + $user4 = [ + 'isLocalSystemBook' => true, + 'FN' => 'Person 3', + 'ADR' => [ + 'ABC Street 2;01337 Village;;Germany', + ], + 'LANG' => 'en_us', + 'TZ' => 'Australia/Adelaide', + 'PHOTO' => 'VALUE:BINARY:4242424242', + 'CATEGORIES' => 'search 123' + ]; + + $this->manager->expects(self::once()) + ->method('isEnabled') + ->willReturn(true); + $this->service + ->method('hasEmail') + ->willReturnMap([ + [$user1, true], + [$user2, true], + [$user3, false], + [$user4, true], + ]); + $this->service + ->method('isSystemBook') + ->willReturnMap([ + [$user1, false], + [$user2, false], + [$user3, false], + [$user4, true], + ]); + $this->service + ->method('getNameFromContact') + ->willReturnMap([ + [$user1, 'Person 1'], + [$user2, 'Person 2'], + [$user3, ''], + ]); + $this->service->expects(self::exactly(2)) + ->method('getLanguageId') + ->willReturnMap([ + [$user1, 'de'], + [$user3, 'en_us'], + ]); + $this->service->expects(self::exactly(2)) + ->method('getTimezoneId') + ->willReturnMap([ + [$user1, 'Europe/Berlin'], + [$user3, 'Australia/Adelaide'], + ]); + $this->service->expects(self::exactly(2)) + ->method('getEmail') + ->willReturnMap([ + [$user1, [ + 'foo1@example.com', + 'foo2@example.com', + ] + ], + [$user2, ['foo3@example.com']], + [$user3, ['foo5@example.com']], + ]); + $this->service->method('getPhotoUri') + ->willReturnMap([ + [$user1, 'http://foo.bar'], + [$user2, null], + [$user3, null], + [$user4, null], + ]); + $this->manager->expects(self::exactly(2)) + ->method('search') + ->willReturnMap([ + ['search 123', ['FN', 'EMAIL'], [], [$user1, $user2, $user3, $user4]], + ['search 123', ['CATEGORIES'], [], [$user4]] + ]); + $response = $this->controller->searchAttendee('search 123'); $this->assertInstanceOf(JSONResponse::class, $response); @@ -221,6 +405,7 @@ public function testSearchAttendee():void { 'lang' => 'de', 'tzid' => 'Europe/Berlin', 'photo' => 'http://foo.bar', + 'type' => 'individual' ], [ 'name' => 'Person 2', 'emails' => [ @@ -229,80 +414,102 @@ public function testSearchAttendee():void { 'lang' => null, 'tzid' => null, 'photo' => null, + 'type' => 'individual' ] ], $response->getData()); $this->assertEquals(200, $response->getStatus()); } public function testSearchPhotoDisabled():void { - $this->manager->expects($this->once()) + $this->manager->expects(self::once()) ->method('isEnabled') - ->with() ->willReturn(false); - $this->manager->expects($this->never()) + $this->manager->expects(self::never()) ->method('search'); $response = $this->controller->searchAttendee('search 123'); - $this->assertInstanceOf(JSONResponse::class, $response); $this->assertEquals([], $response->getData()); $this->assertEquals(200, $response->getStatus()); } public function testSearchPhoto():void { + $user1 = [ + 'FN' => 'Person 1', + 'ADR' => [ + '33 42nd Street;Random Town;Some State;;United States', + ';;5 Random Ave;12782 Some big city;Yet another state;United States', + ], + 'EMAIL' => [ + 'foo1@example.com', + 'foo2@example.com', + ], + 'LANG' => [ + 'de', + 'en' + ], + 'TZ' => [ + 'Europe/Berlin', + 'UTC' + ], + 'PHOTO' => 'VALUE=uri:http://foo123.bar' + ]; + $user2 = [ + 'FN' => 'Person 2', + 'EMAIL' => 'foo3@example.com', + 'PHOTO' => 'VALUE=uri:http://foo.bar' + ]; + $user3 = [ + 'ADR' => [ + 'ABC Street 2;01337 Village;;Germany', + ], + 'LANG' => 'en_us', + 'TZ' => 'Australia/Adelaide', + 'PHOTO' => 'VALUE:BINARY:4242424242' + ]; + $user4 = [ + 'isLocalSystemBook' => true, + 'FN' => 'Person 3', + 'ADR' => [ + 'ABC Street 2;01337 Village;;Germany', + ], + 'EMAIL' => 'foo5@example.com', + 'LANG' => 'en_us', + 'TZ' => 'Australia/Adelaide', + 'PHOTO' => 'VALUE=uri:http://foo456.bar' + ]; + $this->manager->expects(self::once()) ->method('isEnabled') - ->with() ->willReturn(true); - + $this->service->method('hasEmail')->willReturnMap([ + [$user1, true], + [$user2, true], + [$user3, false], + [$user3, true], + ]); + $this->service->method('isSystemBook'); + $this->service->method('getEmail') + ->willReturnMap([ + [$user1, [ + 'foo1@example.com', + 'foo2@example.com', + ] + ], + [$user2, ['foo3@example.com']], + [$user3, ['foo5@example.com']], + ]); + $this->service->method('getNameFromContact')->willReturn('Person 2'); + $this->service->method('getPhotoUri')->willReturn('http://foo.bar'); $this->manager->expects(self::once()) ->method('search') ->with('foo3@example.com', ['EMAIL']) ->willReturn([ - [ - 'FN' => 'Person 1', - 'ADR' => [ - '33 42nd Street;Random Town;Some State;;United States', - ';;5 Random Ave;12782 Some big city;Yet another state;United States', - ], - 'EMAIL' => [ - 'foo1@example.com', - 'foo2@example.com', - ], - 'LANG' => [ - 'de', - 'en' - ], - 'TZ' => [ - 'Europe/Berlin', - 'UTC' - ], - 'PHOTO' => 'VALUE=uri:http://foo123.bar' - ], - [ - 'FN' => 'Person 2', - 'EMAIL' => 'foo3@example.com', - 'PHOTO' => 'VALUE=uri:http://foo.bar' - ], - [ - 'ADR' => [ - 'ABC Street 2;01337 Village;;Germany', - ], - 'LANG' => 'en_us', - 'TZ' => 'Australia/Adelaide', - 'PHOTO' => 'VALUE:BINARY:4242424242' - ], - [ - 'isLocalSystemBook' => true, - 'FN' => 'Person 3', - 'ADR' => [ - 'ABC Street 2;01337 Village;;Germany', - ], - 'LANG' => 'en_us', - 'TZ' => 'Australia/Adelaide', - 'PHOTO' => 'VALUE=uri:http://foo456.bar' - ], + $user1, + $user2, + $user3, + $user4, ]); $response = $this->controller->searchPhoto('foo3@example.com'); diff --git a/tests/php/unit/Service/ContactsServiceTest.php b/tests/php/unit/Service/ContactsServiceTest.php new file mode 100644 index 000000000..986573f92 --- /dev/null +++ b/tests/php/unit/Service/ContactsServiceTest.php @@ -0,0 +1,96 @@ +service = new ContactsService(); + } + + public function testGetEmail(): void { + $contact = ['EMAIL' => 'test@test.com']; + $this->assertEquals(['test@test.com'], $this->service->getEmail($contact)); + } + + public function testIsSystemBook(): void { + $contact = ['isLocalSystemBook' => true]; + $this->assertTrue($this->service->isSystemBook($contact)); + } + + public function testIsNotSystemBook(): void { + $contact = ['isLocalSystemBook' => false]; + $this->assertFalse($this->service->isSystemBook($contact)); + } + + public function testNotSetSystemBook(): void { + $this->assertFalse($this->service->isSystemBook([])); + } + + public function testHasEmail(): void { + $contact = ['EMAIL' => 'test@test.com']; + $this->assertTrue($this->service->hasEmail($contact)); + } + + public function testHasNoEmail(): void { + $this->assertFalse($this->service->hasEmail([])); + } + + public function testGetPhotoUri(): void { + $contact = ['PHOTO' => 'VALUE=uri:http://test']; + $this->assertEquals('http://test', $this->service->getPhotoUri($contact)); + } + + public function testGetPhotoInvalidUri(): void { + $contact = ['PHOTO' => 'VALUE=uri:thisisnotit']; + $this->assertNull($this->service->getPhotoUri($contact)); + } + + public function testGetPhotoUriNoPhoto(): void { + $this->assertNull($this->service->getPhotoUri([])); + } + + public function testFilterGroupsWithCount(): void { + $contact = [ + ['CATEGORIES' => 'The Proclaimers,I\'m gonna be,When I go out,I would walk 500 Miles,I would walk 500 more'], + ['CATEGORIES' => 'The Proclaimers,When I\'m lonely,I would walk 500 Miles,I would walk 500 more'], + ]; + + $searchterm = 'walk'; + + $expected = [ + 'I would walk 500 Miles' => 2, + 'I would walk 500 more' => 2, + ]; + + $this->assertEqualsCanonicalizing($expected, $this->service->filterGroupsWithCount($contact, $searchterm)); + } + + public function testGetTimezoneId(): void { + $contact = ['TZ' => ['UTC']]; + $this->assertEquals('UTC', $this->service->getTimezoneId($contact)); + } + + public function testGetLanguageId(): void { + $contact = ['LANG' => ['de_de']]; + $this->assertEquals('de_de', $this->service->getLanguageId($contact)); + } + + public function testGetNameFromContact(): void { + $contact = ['FN' => 'test']; + $this->assertEquals('test', $this->service->getNameFromContact($contact)); + } + + public function testGetNameFromContactNoName(): void { + $this->assertEquals('', $this->service->getNameFromContact([])); + } +}