Skip to content

Commit

Permalink
Merge pull request #29269 from nextcloud/feature/28751/provide-contac…
Browse files Browse the repository at this point in the history
…tsmenu-as-ocs-simple

Add an OCS endpoint for the hovercard contact actions
  • Loading branch information
nickvergessen authored Oct 20, 2021
2 parents cf6bac8 + 7f1dc52 commit 0e951eb
Show file tree
Hide file tree
Showing 13 changed files with 178 additions and 43 deletions.
84 changes: 84 additions & 0 deletions core/Controller/HoverCardController.php
Original file line number Diff line number Diff line change
@@ -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);
}
}
2 changes: 2 additions & 0 deletions core/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@
['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'],
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,7 @@
'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',
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
17 changes: 6 additions & 11 deletions lib/private/Contacts/ContactsMenu/ActionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,21 @@
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);
}
}
20 changes: 20 additions & 0 deletions lib/private/Contacts/ContactsMenu/Actions/LinkAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -87,6 +90,22 @@ public function getHref() {
return $this->href;
}

/**
* @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
*/
Expand All @@ -95,6 +114,7 @@ public function jsonSerialize() {
'title' => $this->name,
'icon' => $this->icon,
'hyperlink' => $this->href,
'appId' => $this->appId,
];
}
}
28 changes: 14 additions & 14 deletions lib/private/Contacts/ContactsMenu/ContactsStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,18 @@ public function getContacts(IUser $user, $filter, ?int $limit = null, ?int $offs
$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,
Expand All @@ -125,12 +134,11 @@ public function getContacts(IUser $user, $filter, ?int $limit = null, ?int $offs

/**
* 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.
*
Expand Down Expand Up @@ -171,10 +179,6 @@ private function filterContacts(
$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;
Expand Down Expand Up @@ -266,11 +270,7 @@ public function findOne(IUser $user, $shareType, $shareWith) {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function process(IEntry $entry) {
// Skip
continue;
}
$action = $this->actionFactory->newEMailAction($iconUrl, $address, $address);
$action = $this->actionFactory->newEMailAction($iconUrl, $address, $address, 'email');
$entry->addAction($action);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public function process(IEntry $entry) {
$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);
Expand Down
12 changes: 12 additions & 0 deletions lib/public/Contacts/ContactsMenu/IAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,16 @@ public function setPriority($priority);
* @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;
}
6 changes: 4 additions & 2 deletions lib/public/Contacts/ContactsMenu/IActionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -47,7 +48,8 @@ public function newLinkAction($icon, $name, $href);
* @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;
}
19 changes: 19 additions & 0 deletions tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,29 @@ public function testJsonSerialize() {
$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();
Expand Down
Loading

0 comments on commit 0e951eb

Please sign in to comment.