diff options
author | Joas Schilling <213943+nickvergessen@users.noreply.github.com> | 2021-10-20 16:25:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-20 16:25:07 +0200 |
commit | 0e951eb9dca2f4fb5e0fc875d0daf92ae208c467 (patch) | |
tree | 79954c236ac22053ae7ca71824d08719a32dbaca | |
parent | cf6bac8d6eac64d9db0b3f2650fb5f19f9db7442 (diff) | |
parent | 7f1dc52a66088a4478aa52708e62fbc2ffa7cb57 (diff) | |
download | nextcloud-server-0e951eb9dca2f4fb5e0fc875d0daf92ae208c467.tar.gz nextcloud-server-0e951eb9dca2f4fb5e0fc875d0daf92ae208c467.zip |
Merge pull request #29269 from nextcloud/feature/28751/provide-contactsmenu-as-ocs-simple
Add an OCS endpoint for the hovercard contact actions
-rw-r--r-- | core/Controller/HoverCardController.php | 84 | ||||
-rw-r--r-- | core/routes.php | 2 | ||||
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 1 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 1 | ||||
-rw-r--r-- | lib/private/Contacts/ContactsMenu/ActionFactory.php | 17 | ||||
-rw-r--r-- | lib/private/Contacts/ContactsMenu/Actions/LinkAction.php | 20 | ||||
-rw-r--r-- | lib/private/Contacts/ContactsMenu/ContactsStore.php | 28 | ||||
-rw-r--r-- | lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php | 2 | ||||
-rw-r--r-- | lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php | 2 | ||||
-rw-r--r-- | lib/public/Contacts/ContactsMenu/IAction.php | 12 | ||||
-rw-r--r-- | lib/public/Contacts/ContactsMenu/IActionFactory.php | 6 | ||||
-rw-r--r-- | tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php | 19 | ||||
-rw-r--r-- | tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php | 27 |
13 files changed, 178 insertions, 43 deletions
diff --git a/core/Controller/HoverCardController.php b/core/Controller/HoverCardController.php new file mode 100644 index 00000000000..cb85da112a4 --- /dev/null +++ b/core/Controller/HoverCardController.php @@ -0,0 +1,84 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2021 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OC\Core\Controller; + +use OC\Contacts\ContactsMenu\Manager; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\Contacts\ContactsMenu\IEntry; +use OCP\IRequest; +use OCP\IUserSession; +use OCP\Share\IShare; + +class HoverCardController extends \OCP\AppFramework\OCSController { + + /** @var Manager */ + private $manager; + + /** @var IUserSession */ + private $userSession; + + /** + * @param IRequest $request + * @param IUserSession $userSession + * @param Manager $manager + */ + public function __construct(IRequest $request, IUserSession $userSession, Manager $manager) { + parent::__construct('core', $request); + $this->userSession = $userSession; + $this->manager = $manager; + } + + /** + * @NoAdminRequired + * + * @param string $userId + * @return DataResponse + */ + public function getUser(string $userId): DataResponse { + $contact = $this->manager->findOne($this->userSession->getUser(), IShare::TYPE_USER, $userId); + + if (!$contact) { + return new DataResponse([], Http::STATUS_NOT_FOUND); + } + + $data = $this->entryToArray($contact); + + $actions = $data['actions']; + if ($data['topAction']) { + array_unshift($actions, $data['topAction']); + } + + return new DataResponse([ + 'userId' => $userId, + 'displayName' => $contact->getFullName(), + 'actions' => $actions, + ]); + } + + protected function entryToArray(IEntry $entry): array { + return json_decode(json_encode($entry), true); + } +} diff --git a/core/routes.php b/core/routes.php index 59988404cd4..5750dac2ad1 100644 --- a/core/routes.php +++ b/core/routes.php @@ -110,6 +110,8 @@ $application->registerRoutes($this, [ ['root' => '/core', 'name' => 'AppPassword#rotateAppPassword', 'url' => '/apppassword/rotate', 'verb' => 'POST'], ['root' => '/core', 'name' => 'AppPassword#deleteAppPassword', 'url' => '/apppassword', 'verb' => 'DELETE'], + ['root' => '/hovercard', 'name' => 'HoverCard#getUser', 'url' => '/v1/{userId}', 'verb' => 'GET'], + ['root' => '/collaboration', 'name' => 'CollaborationResources#searchCollections', 'url' => '/resources/collections/search/{filter}', 'verb' => 'GET'], ['root' => '/collaboration', 'name' => 'CollaborationResources#listCollection', 'url' => '/resources/collections/{collectionId}', 'verb' => 'GET'], ['root' => '/collaboration', 'name' => 'CollaborationResources#renameCollection', 'url' => '/resources/collections/{collectionId}', 'verb' => 'PUT'], diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 53f75ebf27b..72e3ff8a8e4 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -920,6 +920,7 @@ return array( 'OC\\Core\\Controller\\ContactsMenuController' => $baseDir . '/core/Controller/ContactsMenuController.php', 'OC\\Core\\Controller\\CssController' => $baseDir . '/core/Controller/CssController.php', 'OC\\Core\\Controller\\GuestAvatarController' => $baseDir . '/core/Controller/GuestAvatarController.php', + 'OC\\Core\\Controller\\HoverCardController' => $baseDir . '/core/Controller/HoverCardController.php', 'OC\\Core\\Controller\\JsController' => $baseDir . '/core/Controller/JsController.php', 'OC\\Core\\Controller\\LoginController' => $baseDir . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LostController' => $baseDir . '/core/Controller/LostController.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 3388cc945af..c689a1c011e 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -949,6 +949,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Controller\\ContactsMenuController' => __DIR__ . '/../../..' . '/core/Controller/ContactsMenuController.php', 'OC\\Core\\Controller\\CssController' => __DIR__ . '/../../..' . '/core/Controller/CssController.php', 'OC\\Core\\Controller\\GuestAvatarController' => __DIR__ . '/../../..' . '/core/Controller/GuestAvatarController.php', + 'OC\\Core\\Controller\\HoverCardController' => __DIR__ . '/../../..' . '/core/Controller/HoverCardController.php', 'OC\\Core\\Controller\\JsController' => __DIR__ . '/../../..' . '/core/Controller/JsController.php', 'OC\\Core\\Controller\\LoginController' => __DIR__ . '/../../..' . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LostController' => __DIR__ . '/../../..' . '/core/Controller/LostController.php', diff --git a/lib/private/Contacts/ContactsMenu/ActionFactory.php b/lib/private/Contacts/ContactsMenu/ActionFactory.php index 8e139f6486c..891951a88e5 100644 --- a/lib/private/Contacts/ContactsMenu/ActionFactory.php +++ b/lib/private/Contacts/ContactsMenu/ActionFactory.php @@ -29,26 +29,21 @@ use OCP\Contacts\ContactsMenu\ILinkAction; class ActionFactory implements IActionFactory { /** - * @param string $icon - * @param string $name - * @param string $href - * @return ILinkAction + * {@inheritDoc} */ - public function newLinkAction($icon, $name, $href) { + public function newLinkAction(string $icon, string $name, string $href, string $appId = ''): ILinkAction { $action = new LinkAction(); $action->setName($name); $action->setIcon($icon); $action->setHref($href); + $action->setAppId($appId); return $action; } /** - * @param string $icon - * @param string $name - * @param string $email - * @return ILinkAction + * {@inheritDoc} */ - public function newEMailAction($icon, $name, $email) { - return $this->newLinkAction($icon, $name, 'mailto:' . $email); + public function newEMailAction(string $icon, string $name, string $email, string $appId = ''): ILinkAction { + return $this->newLinkAction($icon, $name, 'mailto:' . $email, $appId); } } diff --git a/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php b/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php index a50318903ac..5acafed2fda 100644 --- a/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php +++ b/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php @@ -38,6 +38,9 @@ class LinkAction implements ILinkAction { /** @var int */ private $priority = 10; + /** @var string */ + private $appId; + /** * @param string $icon absolute URI to an icon */ @@ -88,6 +91,22 @@ class LinkAction implements ILinkAction { } /** + * @param string $appId + * @since 23.0.0 + */ + public function setAppId(string $appId) { + $this->appId = $appId; + } + + /** + * @return string + * @since 23.0.0 + */ + public function getAppId(): string { + return $this->appId; + } + + /** * @return array */ public function jsonSerialize() { @@ -95,6 +114,7 @@ class LinkAction implements ILinkAction { 'title' => $this->name, 'icon' => $this->icon, 'hyperlink' => $this->href, + 'appId' => $this->appId, ]; } } diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php index a4a53bf8774..eb7e752a87a 100644 --- a/lib/private/Contacts/ContactsMenu/ContactsStore.php +++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php @@ -113,9 +113,18 @@ class ContactsStore implements IContactsStore { $options ); + $userId = $user->getUID(); + $contacts = array_filter($allContacts, function ($contact) use ($userId) { + // When searching for multiple results, we strip out the current user + if (array_key_exists('UID', $contact)) { + return $contact['UID'] !== $userId; + } + return true; + }); + $entries = array_map(function (array $contact) { return $this->contactArrayToEntry($contact); - }, $allContacts); + }, $contacts); return $this->filterContacts( $user, $entries, @@ -125,12 +134,11 @@ class ContactsStore implements IContactsStore { /** * Filters the contacts. Applied filters: - * 1. filter the current user - * 2. if the `shareapi_allow_share_dialog_user_enumeration` config option is + * 1. if the `shareapi_allow_share_dialog_user_enumeration` config option is * enabled it will filter all local users - * 3. if the `shareapi_exclude_groups` config option is enabled and the + * 2. if the `shareapi_exclude_groups` config option is enabled and the * current user is in an excluded group it will filter all local users. - * 4. if the `shareapi_only_share_with_group_members` config option is + * 3. if the `shareapi_only_share_with_group_members` config option is * enabled it will filter all users which doens't have a common group * with the current user. * @@ -171,10 +179,6 @@ class ContactsStore implements IContactsStore { $selfUID = $self->getUID(); return array_values(array_filter($entries, function (IEntry $entry) use ($skipLocal, $ownGroupsOnly, $selfGroups, $selfUID, $disallowEnumeration, $restrictEnumerationGroup, $restrictEnumerationPhone, $allowEnumerationFullMatch, $filter) { - if ($entry->getProperty('UID') === $selfUID) { - return false; - } - if ($entry->getProperty('isLocalSystemBook')) { if ($skipLocal) { return false; @@ -266,11 +270,7 @@ class ContactsStore implements IContactsStore { return null; } - $userId = $user->getUID(); - $allContacts = $this->contactsManager->search($shareWith, $filter); - $contacts = array_filter($allContacts, function ($contact) use ($userId) { - return $contact['UID'] !== $userId; - }); + $contacts = $this->contactsManager->search($shareWith, $filter); $match = null; foreach ($contacts as $contact) { diff --git a/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php b/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php index 9ae323c18c3..d69f219e84c 100644 --- a/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php +++ b/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php @@ -54,7 +54,7 @@ class EMailProvider implements IProvider { // Skip continue; } - $action = $this->actionFactory->newEMailAction($iconUrl, $address, $address); + $action = $this->actionFactory->newEMailAction($iconUrl, $address, $address, 'email'); $entry->addAction($action); } } diff --git a/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php b/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php index 4882c0ac883..88370f193a1 100644 --- a/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php +++ b/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php @@ -83,7 +83,7 @@ class ProfileProvider implements IProvider { $iconUrl = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'actions/profile.svg')); $profileActionText = $this->l10nFactory->get('core')->t('View profile'); $profileUrl = $this->urlGenerator->linkToRouteAbsolute('core.ProfilePage.index', ['targetUserId' => $targetUserId]); - $action = $this->actionFactory->newLinkAction($iconUrl, $profileActionText, $profileUrl); + $action = $this->actionFactory->newLinkAction($iconUrl, $profileActionText, $profileUrl, 'profile'); // Set highest priority (by descending order), other actions have the default priority 10 as defined in lib/private/Contacts/ContactsMenu/Actions/LinkAction.php $action->setPriority(20); $entry->addAction($action); diff --git a/lib/public/Contacts/ContactsMenu/IAction.php b/lib/public/Contacts/ContactsMenu/IAction.php index 1a2bc9b33b5..9b08bbbf04b 100644 --- a/lib/public/Contacts/ContactsMenu/IAction.php +++ b/lib/public/Contacts/ContactsMenu/IAction.php @@ -60,4 +60,16 @@ interface IAction extends JsonSerializable { * @since 12.0 */ public function getPriority(); + + /** + * @param string $appId + * @since 23.0.0 + */ + public function setAppId(string $appId); + + /** + * @return string + * @since 23.0.0 + */ + public function getAppId(): string; } diff --git a/lib/public/Contacts/ContactsMenu/IActionFactory.php b/lib/public/Contacts/ContactsMenu/IActionFactory.php index f454ea117d2..b4e4eb96819 100644 --- a/lib/public/Contacts/ContactsMenu/IActionFactory.php +++ b/lib/public/Contacts/ContactsMenu/IActionFactory.php @@ -35,9 +35,10 @@ interface IActionFactory { * @param string $icon full path to the action's icon * @param string $name localized name of the action * @param string $href target URL + * @param string $appId the app ID registering the action * @return ILinkAction */ - public function newLinkAction($icon, $name, $href); + public function newLinkAction(string $icon, string $name, string $href, string $appId = ''): ILinkAction; /** * Construct and return a new email action for the contacts menu @@ -47,7 +48,8 @@ interface IActionFactory { * @param string $icon full path to the action's icon * @param string $name localized name of the action * @param string $email target e-mail address + * @param string $appId the appName registering the action * @return ILinkAction */ - public function newEMailAction($icon, $name, $email); + public function newEMailAction(string $icon, string $name, string $email, string $appId = ''): ILinkAction; } diff --git a/tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php b/tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php index ed6e9ace403..1f5d37e7483 100644 --- a/tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php +++ b/tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php @@ -75,10 +75,29 @@ class LinkActionTest extends TestCase { $this->action->setName('Nickie Works'); $this->action->setPriority(33); $this->action->setHref('example.com'); + $this->action->setAppId('contacts'); $expected = [ 'title' => 'Nickie Works', 'icon' => 'icon-contacts', 'hyperlink' => 'example.com', + 'appId' => 'contacts', + ]; + + $json = $this->action->jsonSerialize(); + + $this->assertEquals($expected, $json); + } + + public function testJsonSerializeNoAppName() { + $this->action->setIcon('icon-contacts'); + $this->action->setName('Nickie Works'); + $this->action->setPriority(33); + $this->action->setHref('example.com'); + $expected = [ + 'title' => 'Nickie Works', + 'icon' => 'icon-contacts', + 'hyperlink' => 'example.com', + 'appId' => '', ]; $json = $this->action->jsonSerialize(); diff --git a/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php b/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php index 13cc7575f43..98a31254c3e 100644 --- a/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php +++ b/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php @@ -99,7 +99,7 @@ class ContactsStoreTest extends TestCase { ], ], ]); - $user->expects($this->once()) + $user->expects($this->exactly(2)) ->method('getUID') ->willReturn('user123'); @@ -129,7 +129,7 @@ class ContactsStoreTest extends TestCase { ], ], ]); - $user->expects($this->once()) + $user->expects($this->exactly(2)) ->method('getUID') ->willReturn('user123'); @@ -157,7 +157,7 @@ class ContactsStoreTest extends TestCase { 'PHOTO' => base64_encode('photophotophoto'), ], ]); - $user->expects($this->once()) + $user->expects($this->exactly(2)) ->method('getUID') ->willReturn('user123'); @@ -186,7 +186,7 @@ class ContactsStoreTest extends TestCase { 'PHOTO' => 'VALUE=uri:https://photo', ], ]); - $user->expects($this->once()) + $user->expects($this->exactly(2)) ->method('getUID') ->willReturn('user123'); @@ -210,7 +210,7 @@ class ContactsStoreTest extends TestCase { /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */ $currentUser = $this->createMock(IUser::class); - $currentUser->expects($this->once()) + $currentUser->expects($this->exactly(2)) ->method('getUID') ->willReturn('user001'); @@ -253,7 +253,7 @@ class ContactsStoreTest extends TestCase { /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */ $currentUser = $this->createMock(IUser::class); - $currentUser->expects($this->once()) + $currentUser->expects($this->exactly(2)) ->method('getUID') ->willReturn('user001'); @@ -332,7 +332,7 @@ class ContactsStoreTest extends TestCase { /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */ $currentUser = $this->createMock(IUser::class); - $currentUser->expects($this->once()) + $currentUser->expects($this->exactly(2)) ->method('getUID') ->willReturn('user001'); @@ -411,7 +411,7 @@ class ContactsStoreTest extends TestCase { /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */ $currentUser = $this->createMock(IUser::class); - $currentUser->expects($this->once()) + $currentUser->expects($this->exactly(2)) ->method('getUID') ->willReturn('user001'); @@ -469,7 +469,7 @@ class ContactsStoreTest extends TestCase { /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */ $currentUser = $this->createMock(IUser::class); - $currentUser->expects($this->once()) + $currentUser->expects($this->exactly(2)) ->method('getUID') ->willReturn('user001'); @@ -555,7 +555,7 @@ class ContactsStoreTest extends TestCase { /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */ $currentUser = $this->createMock(IUser::class); - $currentUser->expects($this->once()) + $currentUser->expects($this->exactly(2)) ->method('getUID') ->willReturn('user001'); @@ -624,7 +624,7 @@ class ContactsStoreTest extends TestCase { /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */ $currentUser = $this->createMock(IUser::class); - $currentUser->expects($this->once()) + $currentUser->expects($this->exactly(2)) ->method('getUID') ->willReturn('user001'); @@ -963,9 +963,8 @@ class ContactsStoreTest extends TestCase { 'isLocalSystemBook' => false ], ]); - $user->expects($this->once()) - ->method('getUID') - ->willReturn('user123'); + $user->expects($this->never()) + ->method('getUID'); $entry = $this->contactsStore->findOne($user, 0, 'a567'); |