diff options
author | Lukas Reschke <lukas@statuscode.ch> | 2017-04-26 18:31:17 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-26 18:31:17 +0200 |
commit | d89c76049fbc7061bfa45e87c46f08802242d5f2 (patch) | |
tree | 0aa8bdbad51349e6e6081fd3fe24945950433e03 | |
parent | edd9444209ec8f0737821870fa9a25b7fe5dc0e6 (diff) | |
parent | 6bbc682c4b24212d36ef595d3692653dca1c67b1 (diff) | |
download | nextcloud-server-d89c76049fbc7061bfa45e87c46f08802242d5f2.tar.gz nextcloud-server-d89c76049fbc7061bfa45e87c46f08802242d5f2.zip |
Merge pull request #4374 from nextcloud/contactsmenu_popover
add contacts popover
-rw-r--r-- | apps/comments/css/comments.css | 6 | ||||
-rw-r--r-- | apps/comments/js/commentstabview.js | 20 | ||||
-rw-r--r-- | apps/comments/tests/js/commentstabviewSpec.js | 2 | ||||
-rw-r--r-- | core/Controller/ContactsMenuController.php | 17 | ||||
-rw-r--r-- | core/css/share.scss | 19 | ||||
-rw-r--r-- | core/js/core.json | 1 | ||||
-rw-r--r-- | core/js/jquery.contactsmenu.js | 117 | ||||
-rw-r--r-- | core/js/merged-template-prepend.json | 3 | ||||
-rw-r--r-- | core/js/sharedialogresharerinfoview.js | 5 | ||||
-rw-r--r-- | core/js/sharedialogshareelistview.js | 15 | ||||
-rw-r--r-- | core/js/tests/specs/jquery.contactsmenuSpec.js | 213 | ||||
-rw-r--r-- | core/routes.php | 1 | ||||
-rw-r--r-- | lib/private/Contacts/ContactsMenu/ContactsStore.php | 44 | ||||
-rw-r--r-- | lib/private/Contacts/ContactsMenu/Manager.php | 17 | ||||
-rw-r--r-- | tests/Core/Controller/ContactsMenuControllerTest.php | 31 | ||||
-rw-r--r-- | tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php | 95 | ||||
-rw-r--r-- | tests/lib/Contacts/ContactsMenu/ManagerTest.php | 45 |
17 files changed, 643 insertions, 8 deletions
diff --git a/apps/comments/css/comments.css b/apps/comments/css/comments.css index 796a550227b..2d794d52708 100644 --- a/apps/comments/css/comments.css +++ b/apps/comments/css/comments.css @@ -54,7 +54,6 @@ #commentsTabView .comment { position: relative; - z-index: 1; margin-bottom: 30px; } @@ -108,6 +107,11 @@ vertical-align: middle; } +#commentsTabView .authorRow>div.hidden { + display: none !important; +} + +#commentsTabView .comments li .message .avatar-name-wrapper, #commentsTabView .comment .authorRow { position: relative; } diff --git a/apps/comments/js/commentstabview.js b/apps/comments/js/commentstabview.js index 2256bea943e..ace0862ad2e 100644 --- a/apps/comments/js/commentstabview.js +++ b/apps/comments/js/commentstabview.js @@ -232,6 +232,21 @@ var $this = $(this); $this.avatar($this.attr('data-username'), 32); }); + + var username = $el.find('.avatar').data('username'); + if (username !== oc_current_user) { + $el.find('.authorRow .avatar, .authorRow .author').contactsMenu( + username, 0, $el.find('.authorRow')); + } + + var message = $el.find('.message'); + message.find('.avatar').each(function() { + var avatar = $(this); + var strong = $(this).next(); + var appendTo = $(this).parent(); + + $.merge(avatar, strong).contactsMenu(avatar.data('user'), 0, appendTo); + }); }, /** @@ -251,7 +266,10 @@ // escape possible regex characters in the name mention = mention.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - var displayName = avatar + ' <strong>'+ _.escape(mentions[i].mentionDisplayName)+'</strong>'; + var displayName = '' + + '<span class="avatar-name-wrapper">' + + avatar + ' <strong>'+ _.escape(mentions[i].mentionDisplayName)+'</strong>' + + '</span>'; // replace every mention either at the start of the input or after a whitespace // followed by a non-word character. diff --git a/apps/comments/tests/js/commentstabviewSpec.js b/apps/comments/tests/js/commentstabviewSpec.js index 0bbfaa1f295..c961548d806 100644 --- a/apps/comments/tests/js/commentstabviewSpec.js +++ b/apps/comments/tests/js/commentstabviewSpec.js @@ -153,7 +153,7 @@ describe('OCA.Comments.CommentsTabView tests', function() { expect($comment.find('strong:first').text()).toEqual('Thane of Cawdor'); expect($comment.find('.avatar[data-user=banquo]').length).toEqual(1); - expect($comment.find('strong:last-child').text()).toEqual('Lord Banquo'); + expect($comment.find('.avatar-name-wrapper:last-child strong').text()).toEqual('Lord Banquo'); }); }); diff --git a/core/Controller/ContactsMenuController.php b/core/Controller/ContactsMenuController.php index b0e0e0c6a77..bbb990f1a4f 100644 --- a/core/Controller/ContactsMenuController.php +++ b/core/Controller/ContactsMenuController.php @@ -26,6 +26,7 @@ namespace OC\Core\Controller; use OC\Contacts\ContactsMenu\Manager; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCP\IRequest; use OCP\IUserSession; @@ -59,4 +60,20 @@ class ContactsMenuController extends Controller { return $this->manager->getEntries($this->userSession->getUser(), $filter); } + /** + * @NoAdminRequired + * + * @param integer $shareType + * @param string $shareWith + * @return JSONResponse + */ + public function findOne($shareType, $shareWith) { + $contact = $this->manager->findOne($this->userSession->getUser(), $shareType, $shareWith); + + if ($contact) { + return $contact; + } else { + return new JSONResponse([], Http::STATUS_NOT_FOUND); + } + } } diff --git a/core/css/share.scss b/core/css/share.scss index 552e20c80cc..2e1c99b6f41 100644 --- a/core/css/share.scss +++ b/core/css/share.scss @@ -87,6 +87,7 @@ list-style-type: none; padding: 8px; > li { + position: relative; padding-top: 10px; padding-bottom: 10px; font-weight: bold; @@ -103,6 +104,7 @@ padding: 3px 6px; } } + .shareOption { white-space: nowrap; display: inline-block; @@ -159,6 +161,10 @@ a { padding: 6px 4px; } +.resharerInfoView.subView { + position: relative; +} + #defaultExpireMessage, .reshare { /* fix shared by text going out of box */ white-space: normal; @@ -185,6 +191,19 @@ a { color: rgba($color-main-text, .4); } +.contactsmenu-popover { + left: -8px; + right: auto; + padding: 3px 6px; + li.hidden { + display: none !important; + } + &:after { + left: 8px; + right: auto; + } +} + .popovermenu .datepicker { margin-left: 35px; } diff --git a/core/js/core.json b/core/js/core.json index aadd66a0558..15e406bf2d2 100644 --- a/core/js/core.json +++ b/core/js/core.json @@ -20,6 +20,7 @@ "libraries": [ "jquery-showpassword.js", "jquery.avatar.js", + "jquery.contactsmenu.js", "placeholder.js" ], "modules": [ diff --git a/core/js/jquery.contactsmenu.js b/core/js/jquery.contactsmenu.js new file mode 100644 index 00000000000..1ea9f732f79 --- /dev/null +++ b/core/js/jquery.contactsmenu.js @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +(function ($) { + var ENTRY = '' + + '<li>' + + ' <a href="{{hyperlink}}">' + + ' {{#if icon}}<img src="{{icon}}">{{/if}}' + + ' <span>{{title}}</span>' + + ' </a>' + + '</li>'; + + var LIST = '' + + '<div class="menu popovermenu bubble hidden contactsmenu-popover">' + + ' <ul>' + + ' <li>' + + ' <a>' + + ' <span class="icon-loading-small"></span>' + + ' </a>' + + ' </li>' + + ' </ul>' + + '</div>'; + + $.fn.contactsMenu = function(shareWith, shareType, appendTo) { + // 0 - user, 4 - email, 6 - remote + var allowedTypes = [0, 4, 6]; + if (allowedTypes.indexOf(shareType) === -1) { + return; + } + + var $div = this; + appendTo.append(LIST); + var $list = appendTo.find('div.contactsmenu-popover'); + + $div.click(function() { + if (!$list.hasClass('hidden')) { + $list.addClass('hidden'); + $list.hide(); + return; + } + + $list.removeClass('hidden'); + $list.show(); + + if ($list.hasClass('loaded')) { + return; + } + + $list.addClass('loaded'); + $.ajax(OC.generateUrl('/contactsmenu/findOne'), { + method: 'POST', + data: { + shareType: shareType, + shareWith: shareWith + } + }).then(function(data) { + $list.find('ul').find('li').addClass('hidden'); + + var actions; + if (!data.topAction) { + actions = [{ + hyperlink: '#', + title: t('core', 'No action available') + }]; + } else { + actions = [data.topAction].concat(data.actions); + } + + actions.forEach(function(action) { + var template = Handlebars.compile(ENTRY); + $list.find('ul').append(template(action)); + }); + + if (actions.length === 0) { + + } + }, function(jqXHR) { + $list.find('ul').find('li').addClass('hidden'); + + var title; + if (jqXHR.status === 404) { + title = t('core', 'No action available'); + } else { + title = t('core', 'Error fetching contact actions'); + } + + var template = Handlebars.compile(ENTRY); + $list.find('ul').append(template({ + hyperlink: '#', + title: title + })); + }); + }); + + $(document).click(function(event) { + var clickedList = ($list.has(event.target).length > 0); + var clickedTarget = ($div.has(event.target).length > 0); + + $div.each(function() { + if ($(this).is(event.target)) { + clickedTarget = true; + } + }); + + if (clickedList || clickedTarget) { + return; + } + + $list.addClass('hidden'); + $list.hide(); + }); + }; +}(jQuery)); diff --git a/core/js/merged-template-prepend.json b/core/js/merged-template-prepend.json index 12b7ca8faa3..0dd6bed5329 100644 --- a/core/js/merged-template-prepend.json +++ b/core/js/merged-template-prepend.json @@ -13,5 +13,6 @@ "mimetypelist.js", "oc-backbone.js", "placeholder.js", - "jquery.avatar.js" + "jquery.avatar.js", + "jquery.contactsmenu.js" ] diff --git a/core/js/sharedialogresharerinfoview.js b/core/js/sharedialogresharerinfoview.js index a82b495bdcc..201484c52a8 100644 --- a/core/js/sharedialogresharerinfoview.js +++ b/core/js/sharedialogresharerinfoview.js @@ -100,6 +100,11 @@ $this.avatar($this.data('username'), 32); }); + this.$el.find('.reshare').contactsMenu( + this.model.getReshareOwner(), + OC.Share.SHARE_TYPE_USER, + this.$el); + return this; }, diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index 3a481e53dde..f513eb75848 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -26,7 +26,7 @@ '{{#each sharees}}' + '<li data-share-id="{{shareId}}" data-share-type="{{shareType}}" data-share-with="{{shareWith}}">' + '<div class="avatar {{#if modSeed}}imageplaceholderseed{{/if}}" data-username="{{shareWith}}" data-displayname="{{shareWithDisplayName}}" {{#if modSeed}}data-seed="{{shareWith}} {{shareType}}"{{/if}}></div>' + - '<span class="has-tooltip username" title="{{shareWithTitle}}">{{shareWithDisplayName}}</span>' + + '<span class="username" title="{{shareWithTitle}}">{{shareWithDisplayName}}</span>' + '<span class="sharingOptionsGroup">' + '{{#if editPermissionPossible}}' + '<span class="shareOption">' + @@ -361,6 +361,15 @@ this.$('.has-tooltip').tooltip({ placement: 'bottom' }); + + this.$('ul.shareWithList > li').each(function() { + var $this = $(this); + + var shareWith = $this.data('share-with'); + var shareType = $this.data('share-type'); + + $this.find('div.avatar, span.username').contactsMenu(shareWith, shareType, $this); + }) } else { var permissionChangeShareId = parseInt(this._renderPermissionChange, 10); var shareWithIndex = this.model.findShareWithIndex(permissionChangeShareId); @@ -399,7 +408,7 @@ var shareId = parseInt(this._menuOpen, 10); if(!_.isNaN(shareId)) { var liSelector = 'li[data-share-id=' + shareId + ']'; - OC.showMenu(null, this.$(liSelector + ' .popovermenu')); + OC.showMenu(null, this.$(liSelector + '.sharingOptionsGroup .popovermenu')); } } @@ -476,7 +485,7 @@ event.stopPropagation(); var $element = $(event.target); var $li = $element.closest('li[data-share-id]'); - var $menu = $li.find('.popovermenu'); + var $menu = $li.find('.sharingOptionsGroup .popovermenu'); OC.showMenu(null, $menu); this._menuOpen = $li.data('share-id'); diff --git a/core/js/tests/specs/jquery.contactsmenuSpec.js b/core/js/tests/specs/jquery.contactsmenuSpec.js new file mode 100644 index 00000000000..7287648f5a2 --- /dev/null +++ b/core/js/tests/specs/jquery.contactsmenuSpec.js @@ -0,0 +1,213 @@ +/** + * Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +describe('jquery.contactsMenu tests', function() { + + var $selector1, $selector2, $appendTo; + + beforeEach(function() { + $('#testArea').append($('<div id="selector1">')); + $('#testArea').append($('<div id="selector2">')); + $('#testArea').append($('<div id="appendTo">')); + $selector1 = $('#selector1'); + $selector2 = $('#selector2'); + $appendTo = $('#appendTo'); + }); + + afterEach(function() { + $selector1.remove(); + $selector2.remove(); + $appendTo.remove(); + }); + + describe('shareType', function() { + it('stops if type not supported', function() { + $selector1.contactsMenu('user', 1, $appendTo); + expect($appendTo.children().length).toEqual(0); + + $selector1.contactsMenu('user', 2, $appendTo); + expect($appendTo.children().length).toEqual(0); + + $selector1.contactsMenu('user', 3, $appendTo); + expect($appendTo.children().length).toEqual(0); + + $selector1.contactsMenu('user', 5, $appendTo); + expect($appendTo.children().length).toEqual(0); + }); + + it('append list if shareType supported', function() { + $selector1.contactsMenu('user', 0, $appendTo); + expect($appendTo.children().length).toEqual(1); + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble hidden contactsmenu-popover"> <ul> <li> <a> <span class="icon-loading-small"></span> </a> </li> </ul></div>'); + }); + }); + + describe('open on click', function() { + it('with one selector', function() { + $selector1.contactsMenu('user', 0, $appendTo); + expect($appendTo.children().length).toEqual(1); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true); + $selector1.click(); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false); + }); + + it('with multiple selectors - 1', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + + expect($appendTo.children().length).toEqual(1); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true); + $selector1.click(); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false); + }); + + it('with multiple selectors - 2', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + + expect($appendTo.children().length).toEqual(1); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true); + $selector2.click(); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false); + }); + + it ('should close when clicking the selector again - 1', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + + expect($appendTo.children().length).toEqual(1); + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + $selector1.click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(false); + $selector1.click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + }); + + it ('should close when clicking the selector again - 1', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + + expect($appendTo.children().length).toEqual(1); + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + $selector1.click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(false); + $selector2.click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + }); + }); + + describe('send requests to the server and render', function() { + it('load a topaction only', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + $selector1.click(); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json; charset=utf-8' }, + JSON.stringify({ + "id": null, + "fullName": "Name 123", + "topAction": { + "title": "bar@baz.wtf", + "icon": "foo.svg", + "hyperlink": "mailto:bar%40baz.wtf"}, + "actions": [] + }) + ); + expect(fakeServer.requests[0].method).toEqual('POST'); + expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); + + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="mailto:bar%40baz.wtf"> <img src="foo.svg"> <span>bar@baz.wtf</span> </a></li></ul></div>'); + }); + + it('load topaction and more actions', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + $selector1.click(); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json; charset=utf-8' }, + JSON.stringify({ + "id": null, + "fullName": "Name 123", + "topAction": { + "title": "bar@baz.wtf", + "icon": "foo.svg", + "hyperlink": "mailto:bar%40baz.wtf"}, + "actions": [{ + "title": "Details", + "icon": "details.svg", + "hyperlink": "http:\/\/localhost\/index.php\/apps\/contacts" + }] + }) + ); + expect(fakeServer.requests[0].method).toEqual('POST'); + expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); + + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="mailto:bar%40baz.wtf"> <img src="foo.svg"> <span>bar@baz.wtf</span> </a></li><li> <a href="http://localhost/index.php/apps/contacts"> <img src="details.svg"> <span>Details</span> </a></li></ul></div>'); + }); + + it('load no actions', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + $selector1.click(); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json; charset=utf-8' }, + JSON.stringify({ + "id": null, + "fullName": "Name 123", + "topAction": null, + "actions": [] + }) + ); + expect(fakeServer.requests[0].method).toEqual('POST'); + expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); + + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="#"> <span>No action available</span> </a></li></ul></div>'); + }); + + it('should throw an error', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + $selector1.click(); + + fakeServer.requests[0].respond( + 400, + { 'Content-Type': 'application/json; charset=utf-8' }, + JSON.stringify([]) + ); + expect(fakeServer.requests[0].method).toEqual('POST'); + expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); + + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="#"> <span>Error fetching contact actions</span> </a></li></ul></div>'); + }); + + it('should handle 404', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + $selector1.click(); + + fakeServer.requests[0].respond( + 404, + { 'Content-Type': 'application/json; charset=utf-8' }, + JSON.stringify([]) + ); + expect(fakeServer.requests[0].method).toEqual('POST'); + expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); + + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="#"> <span>No action available</span> </a></li></ul></div>'); + }); + }); + + it('click anywhere else to close the menu', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + $selector1.click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(false); + $(document).click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + }); +}); diff --git a/core/routes.php b/core/routes.php index 37db2642c1b..c167dad2f9f 100644 --- a/core/routes.php +++ b/core/routes.php @@ -61,6 +61,7 @@ $application->registerRoutes($this, [ ['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'], ['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'], ['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'], + ['name' => 'contactsMenu#findOne', 'url' => '/contactsmenu/findOne', 'verb' => 'POST'], ], 'ocs' => [ ['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'], diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php index 1cdb5d6fc5f..40a0bf87031 100644 --- a/lib/private/Contacts/ContactsMenu/ContactsStore.php +++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php @@ -60,6 +60,50 @@ class ContactsStore { } /** + * @param IUser $user + * @param integer $shareType + * @param string $shareWith + * @return IEntry|null + */ + public function findOne(IUser $user, $shareType, $shareWith) { + switch($shareType) { + case 0: + case 6: + $filter = ['UID']; + break; + case 4: + $filter = ['EMAIL']; + break; + default: + return null; + } + + $userId = $user->getUID(); + $allContacts = $this->contactsManager->search($shareWith, $filter); + $contacts = array_filter($allContacts, function($contact) use ($userId) { + return $contact['UID'] !== $userId; + }); + $match = null; + + foreach ($contacts as $contact) { + if ($shareType === 4 && isset($contact['EMAIL'])) { + if (in_array($shareWith, $contact['EMAIL'])) { + $match = $contact; + break; + } + } + if ($shareType === 0 || $shareType === 6) { + if ($contact['UID'] === $shareWith && $contact['isLocalSystemBook'] === true) { + $match = $contact; + break; + } + } + } + + return $match ? $this->contactArrayToEntry($match) : null; + } + + /** * @param array $contact * @return Entry */ diff --git a/lib/private/Contacts/ContactsMenu/Manager.php b/lib/private/Contacts/ContactsMenu/Manager.php index 16d77c2df08..766b4623253 100644 --- a/lib/private/Contacts/ContactsMenu/Manager.php +++ b/lib/private/Contacts/ContactsMenu/Manager.php @@ -51,7 +51,7 @@ class Manager { } /** - * @param string $user + * @param IUser $user * @param string $filter * @return array */ @@ -70,6 +70,21 @@ class Manager { } /** + * @param IUser $user + * @param integer $shareType + * @param string $shareWith + * @return IEntry + */ + public function findOne(IUser $user, $shareType, $shareWith) { + $entry = $this->store->findOne($user, $shareType, $shareWith); + if ($entry) { + $this->processEntries([$entry], $user); + } + + return $entry; + } + + /** * @param IEntry[] $entries * @return IEntry[] */ diff --git a/tests/Core/Controller/ContactsMenuControllerTest.php b/tests/Core/Controller/ContactsMenuControllerTest.php index bf6188e9097..92a185cf2ad 100644 --- a/tests/Core/Controller/ContactsMenuControllerTest.php +++ b/tests/Core/Controller/ContactsMenuControllerTest.php @@ -76,4 +76,35 @@ class ContactsMenuControllerTest extends TestCase { $this->assertEquals($entries, $response); } + public function testFindOne() { + $user = $this->createMock(IUser::class); + $entry = $this->createMock(IEntry::class); + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn($user); + $this->contactsManager->expects($this->once()) + ->method('findOne') + ->with($this->equalTo($user), $this->equalTo(42), $this->equalTo('test-search-phrase')) + ->willReturn($entry); + + $response = $this->controller->findOne(42, 'test-search-phrase'); + + $this->assertEquals($entry, $response); + } + + public function testFindOne404() { + $user = $this->createMock(IUser::class); + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn($user); + $this->contactsManager->expects($this->once()) + ->method('findOne') + ->with($this->equalTo($user), $this->equalTo(42), $this->equalTo('test-search-phrase')) + ->willReturn(null); + + $response = $this->controller->findOne(42, 'test-search-phrase'); + + $this->assertEquals([], $response->getData()); + $this->assertEquals(404, $response->getStatus()); + } } diff --git a/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php b/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php index 80c26a9078e..08da360388f 100644 --- a/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php +++ b/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php @@ -157,4 +157,99 @@ class ContactsStoreTest extends TestCase { $this->assertEquals('https://photo', $entries[1]->getAvatar()); } + public function testFindOneUser() { + $user = $this->createMock(IUser::class); + $this->contactsManager->expects($this->once()) + ->method('search') + ->with($this->equalTo('a567'), $this->equalTo(['UID'])) + ->willReturn([ + [ + 'UID' => 123, + 'isLocalSystemBook' => false + ], + [ + 'UID' => 'a567', + 'FN' => 'Darren Roner', + 'EMAIL' => [ + 'darren@roner.au' + ], + 'isLocalSystemBook' => true + ], + ]); + $user->expects($this->once()) + ->method('getUID') + ->willReturn('user123'); + + $entry = $this->contactsStore->findOne($user, 0, 'a567'); + + $this->assertEquals([ + 'darren@roner.au' + ], $entry->getEMailAddresses()); + } + + public function testFindOneEMail() { + $user = $this->createMock(IUser::class); + $this->contactsManager->expects($this->once()) + ->method('search') + ->with($this->equalTo('darren@roner.au'), $this->equalTo(['EMAIL'])) + ->willReturn([ + [ + 'UID' => 123, + 'isLocalSystemBook' => false + ], + [ + 'UID' => 'a567', + 'FN' => 'Darren Roner', + 'EMAIL' => [ + 'darren@roner.au' + ], + 'isLocalSystemBook' => false + ], + ]); + $user->expects($this->once()) + ->method('getUID') + ->willReturn('user123'); + + $entry = $this->contactsStore->findOne($user, 4, 'darren@roner.au'); + + $this->assertEquals([ + 'darren@roner.au' + ], $entry->getEMailAddresses()); + } + + public function testFindOneNotSupportedType() { + $user = $this->createMock(IUser::class); + + $entry = $this->contactsStore->findOne($user, 42, 'darren@roner.au'); + + $this->assertEquals(null, $entry); + } + + public function testFindOneNoMatches() { + $user = $this->createMock(IUser::class); + $this->contactsManager->expects($this->once()) + ->method('search') + ->with($this->equalTo('a567'), $this->equalTo(['UID'])) + ->willReturn([ + [ + 'UID' => 123, + 'isLocalSystemBook' => false + ], + [ + 'UID' => 'a567', + 'FN' => 'Darren Roner', + 'EMAIL' => [ + 'darren@roner.au123' + ], + 'isLocalSystemBook' => false + ], + ]); + $user->expects($this->once()) + ->method('getUID') + ->willReturn('user123'); + + $entry = $this->contactsStore->findOne($user, 0, 'a567'); + + $this->assertEquals(null, $entry); + } } diff --git a/tests/lib/Contacts/ContactsMenu/ManagerTest.php b/tests/lib/Contacts/ContactsMenu/ManagerTest.php index 9c92ec54b9f..783e5590a29 100644 --- a/tests/lib/Contacts/ContactsMenu/ManagerTest.php +++ b/tests/lib/Contacts/ContactsMenu/ManagerTest.php @@ -99,4 +99,49 @@ class ManagerTest extends TestCase { $this->assertEquals($expected, $data); } + public function testFindOne() { + $shareTypeFilter = 42; + $shareWithFilter = 'foobar'; + + $user = $this->createMock(IUser::class); + $entry = current($this->generateTestEntries()); + $provider = $this->createMock(IProvider::class); + $this->contactsStore->expects($this->once()) + ->method('findOne') + ->with($user, $shareTypeFilter, $shareWithFilter) + ->willReturn($entry); + $this->actionProviderStore->expects($this->once()) + ->method('getProviders') + ->with($user) + ->willReturn([$provider]); + $provider->expects($this->once()) + ->method('process'); + + $data = $this->manager->findOne($user, $shareTypeFilter, $shareWithFilter); + + $this->assertEquals($entry, $data); + } + + public function testFindOne404() { + $shareTypeFilter = 42; + $shareWithFilter = 'foobar'; + + $user = $this->createMock(IUser::class); + $provider = $this->createMock(IProvider::class); + $this->contactsStore->expects($this->once()) + ->method('findOne') + ->with($user, $shareTypeFilter, $shareWithFilter) + ->willReturn(null); + $this->actionProviderStore->expects($this->never()) + ->method('getProviders') + ->with($user) + ->willReturn([$provider]); + $provider->expects($this->never()) + ->method('process'); + + $data = $this->manager->findOne($user, $shareTypeFilter, $shareWithFilter); + + $this->assertEquals(null, $data); + } + } |