summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files_sharing/lib/Controller/ShareesAPIController.php589
-rw-r--r--lib/private/Collaboration/Collaborators/CirclePlugin.php49
-rw-r--r--lib/private/Collaboration/Collaborators/GroupPlugin.php122
-rw-r--r--lib/private/Collaboration/Collaborators/LookupPlugin.php83
-rw-r--r--lib/private/Collaboration/Collaborators/MailPlugin.php162
-rw-r--r--lib/private/Collaboration/Collaborators/RemotePlugin.php135
-rw-r--r--lib/private/Collaboration/Collaborators/Search.php82
-rw-r--r--lib/private/Collaboration/Collaborators/SearchResult.php97
-rw-r--r--lib/private/Collaboration/Collaborators/UserPlugin.php147
-rw-r--r--lib/private/Server.php12
-rw-r--r--lib/public/Collaboration/Collaborators/ISearch.php37
-rw-r--r--lib/public/Collaboration/Collaborators/ISearchPlugin.php37
-rw-r--r--lib/public/Collaboration/Collaborators/ISearchResult.php68
13 files changed, 1062 insertions, 558 deletions
diff --git a/apps/files_sharing/lib/Controller/ShareesAPIController.php b/apps/files_sharing/lib/Controller/ShareesAPIController.php
index 94a4854dbde..0acec150472 100644
--- a/apps/files_sharing/lib/Controller/ShareesAPIController.php
+++ b/apps/files_sharing/lib/Controller/ShareesAPIController.php
@@ -6,6 +6,7 @@
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @license AGPL-3.0
*
@@ -27,52 +28,23 @@ namespace OCA\Files_Sharing\Controller;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCSController;
-use OCP\Contacts\IManager;
-use OCP\Federation\ICloudIdManager;
-use OCP\Http\Client\IClientService;
-use OCP\IGroup;
-use OCP\IGroupManager;
-use OCP\ILogger;
+use OCP\Collaboration\Collaborators\ISearch;
use OCP\IRequest;
-use OCP\IUser;
-use OCP\IUserManager;
use OCP\IConfig;
-use OCP\IUserSession;
use OCP\IURLGenerator;
use OCP\Share;
+use OCP\Share\IManager;
class ShareesAPIController extends OCSController {
-
- /** @var IGroupManager */
- protected $groupManager;
-
- /** @var IUserManager */
- protected $userManager;
-
- /** @var IManager */
- protected $contactsManager;
-
/** @var IConfig */
protected $config;
- /** @var IUserSession */
- protected $userSession;
-
/** @var IURLGenerator */
protected $urlGenerator;
- /** @var ILogger */
- protected $logger;
-
- /** @var \OCP\Share\IManager */
+ /** @var IManager */
protected $shareManager;
- /** @var IClientService */
- protected $clientService;
-
- /** @var ICloudIdManager */
- protected $cloudIdManager;
-
/** @var bool */
protected $shareWithGroupOnly = false;
@@ -103,304 +75,31 @@ class ShareesAPIController extends OCSController {
];
protected $reachedEndFor = [];
+ /** @var ISearch */
+ private $collaboratorSearch;
/**
* @param string $appName
* @param IRequest $request
- * @param IGroupManager $groupManager
- * @param IUserManager $userManager
- * @param IManager $contactsManager
* @param IConfig $config
- * @param IUserSession $userSession
* @param IURLGenerator $urlGenerator
- * @param ILogger $logger
- * @param \OCP\Share\IManager $shareManager
- * @param IClientService $clientService
- * @param ICloudIdManager $cloudIdManager
+ * @param IManager $shareManager
+ * @param ISearch $collaboratorSearch
*/
- public function __construct($appName,
- IRequest $request,
- IGroupManager $groupManager,
- IUserManager $userManager,
- IManager $contactsManager,
- IConfig $config,
- IUserSession $userSession,
- IURLGenerator $urlGenerator,
- ILogger $logger,
- \OCP\Share\IManager $shareManager,
- IClientService $clientService,
- ICloudIdManager $cloudIdManager
+ public function __construct(
+ $appName,
+ IRequest $request,
+ IConfig $config,
+ IURLGenerator $urlGenerator,
+ IManager $shareManager,
+ ISearch $collaboratorSearch
) {
parent::__construct($appName, $request);
- $this->groupManager = $groupManager;
- $this->userManager = $userManager;
- $this->contactsManager = $contactsManager;
$this->config = $config;
- $this->userSession = $userSession;
$this->urlGenerator = $urlGenerator;
- $this->logger = $logger;
$this->shareManager = $shareManager;
- $this->clientService = $clientService;
- $this->cloudIdManager = $cloudIdManager;
- }
-
- /**
- * @param string $search
- */
- protected function getUsers($search) {
- $this->result['users'] = $this->result['exact']['users'] = $users = [];
-
- $userGroups = [];
- if ($this->shareWithGroupOnly) {
- // Search in all the groups this user is part of
- $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
- foreach ($userGroups as $userGroup) {
- $usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $this->limit, $this->offset);
- foreach ($usersTmp as $uid => $userDisplayName) {
- $users[$uid] = $userDisplayName;
- }
- }
- } else {
- // Search in all users
- $usersTmp = $this->userManager->searchDisplayName($search, $this->limit, $this->offset);
-
- foreach ($usersTmp as $user) {
- $users[$user->getUID()] = $user->getDisplayName();
- }
- }
-
- if (!$this->shareeEnumeration || sizeof($users) < $this->limit) {
- $this->reachedEndFor[] = 'users';
- }
-
- $foundUserById = false;
- $lowerSearch = strtolower($search);
- foreach ($users as $uid => $userDisplayName) {
- if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) {
- if (strtolower($uid) === $lowerSearch) {
- $foundUserById = true;
- }
- $this->result['exact']['users'][] = [
- 'label' => $userDisplayName,
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_USER,
- 'shareWith' => $uid,
- ],
- ];
- } else {
- $this->result['users'][] = [
- 'label' => $userDisplayName,
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_USER,
- 'shareWith' => $uid,
- ],
- ];
- }
- }
-
- if ($this->offset === 0 && !$foundUserById) {
- // On page one we try if the search result has a direct hit on the
- // user id and if so, we add that to the exact match list
- $user = $this->userManager->get($search);
- if ($user instanceof IUser) {
- $addUser = true;
-
- if ($this->shareWithGroupOnly) {
- // Only add, if we have a common group
- $commonGroups = array_intersect($userGroups, $this->groupManager->getUserGroupIds($user));
- $addUser = !empty($commonGroups);
- }
-
- if ($addUser) {
- array_push($this->result['exact']['users'], [
- 'label' => $user->getDisplayName(),
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_USER,
- 'shareWith' => $user->getUID(),
- ],
- ]);
- }
- }
- }
-
- if (!$this->shareeEnumeration) {
- $this->result['users'] = [];
- }
- }
-
- /**
- * @param string $search
- */
- protected function getGroups($search) {
- $this->result['groups'] = $this->result['exact']['groups'] = [];
-
- $groups = $this->groupManager->search($search, $this->limit, $this->offset);
- $groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups);
-
- if (!$this->shareeEnumeration || sizeof($groups) < $this->limit) {
- $this->reachedEndFor[] = 'groups';
- }
-
- $userGroups = [];
- if (!empty($groups) && $this->shareWithGroupOnly) {
- // Intersect all the groups that match with the groups this user is a member of
- $userGroups = $this->groupManager->getUserGroups($this->userSession->getUser());
- $userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups);
- $groupIds = array_intersect($groupIds, $userGroups);
- }
-
- $lowerSearch = strtolower($search);
- foreach ($groups as $group) {
- // FIXME: use a more efficient approach
- $gid = $group->getGID();
- if (!in_array($gid, $groupIds)) {
- continue;
- }
- if (strtolower($gid) === $lowerSearch || strtolower($group->getDisplayName()) === $lowerSearch) {
- $this->result['exact']['groups'][] = [
- 'label' => $group->getDisplayName(),
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_GROUP,
- 'shareWith' => $gid,
- ],
- ];
- } else {
- $this->result['groups'][] = [
- 'label' => $group->getDisplayName(),
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_GROUP,
- 'shareWith' => $gid,
- ],
- ];
- }
- }
-
- if ($this->offset === 0 && empty($this->result['exact']['groups'])) {
- // On page one we try if the search result has a direct hit on the
- // user id and if so, we add that to the exact match list
- $group = $this->groupManager->get($search);
- if ($group instanceof IGroup && (!$this->shareWithGroupOnly || in_array($group->getGID(), $userGroups))) {
- array_push($this->result['exact']['groups'], [
- 'label' => $group->getDisplayName(),
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_GROUP,
- 'shareWith' => $group->getGID(),
- ],
- ]);
- }
- }
-
- if (!$this->shareeEnumeration) {
- $this->result['groups'] = [];
- }
- }
-
-
- /**
- * @param string $search
- * @suppress PhanUndeclaredClassMethod
- */
- protected function getCircles($search) {
- $this->result['circles'] = $this->result['exact']['circles'] = [];
-
- $result = \OCA\Circles\Api\Sharees::search($search, $this->limit, $this->offset);
- if (array_key_exists('circles', $result['exact'])) {
- $this->result['exact']['circles'] = $result['exact']['circles'];
- }
- if (array_key_exists('circles', $result)) {
- $this->result['circles'] = $result['circles'];
- }
- }
-
-
- /**
- * @param string $search
- * @return array
- */
- protected function getRemote($search) {
- $result = ['results' => [], 'exact' => []];
-
- // Search in contacts
- //@todo Pagination missing
- $addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']);
- $result['exactIdMatch'] = false;
- foreach ($addressBookContacts as $contact) {
- if (isset($contact['isLocalSystemBook'])) {
- continue;
- }
- if (isset($contact['CLOUD'])) {
- $cloudIds = $contact['CLOUD'];
- if (!is_array($cloudIds)) {
- $cloudIds = [$cloudIds];
- }
- $lowerSearch = strtolower($search);
- foreach ($cloudIds as $cloudId) {
- try {
- list(, $serverUrl) = $this->splitUserRemote($cloudId);
- } catch (\InvalidArgumentException $e) {
- continue;
- }
-
- if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) {
- if (strtolower($cloudId) === $lowerSearch) {
- $result['exactIdMatch'] = true;
- }
- $result['exact'][] = [
- 'label' => $contact['FN'] . " ($cloudId)",
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_REMOTE,
- 'shareWith' => $cloudId,
- 'server' => $serverUrl,
- ],
- ];
- } else {
- $result['results'][] = [
- 'label' => $contact['FN'] . " ($cloudId)",
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_REMOTE,
- 'shareWith' => $cloudId,
- 'server' => $serverUrl,
- ],
- ];
- }
- }
- }
- }
-
- if (!$this->shareeEnumeration) {
- $result['results'] = [];
- }
-
- if (!$result['exactIdMatch'] && $this->cloudIdManager->isValidCloudId($search) && $this->offset === 0) {
- $result['exact'][] = [
- 'label' => $search,
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_REMOTE,
- 'shareWith' => $search,
- ],
- ];
- }
-
- $this->reachedEndFor[] = 'remotes';
-
- return $result;
- }
-
- /**
- * split user and remote from federated cloud id
- *
- * @param string $address federated share address
- * @return array [user, remoteURL]
- * @throws \InvalidArgumentException
- */
- public function splitUserRemote($address) {
- try {
- $cloudId = $this->cloudIdManager->resolveCloudId($address);
- return [$cloudId->getUser(), $cloudId->getRemote()];
- } catch (\InvalidArgumentException $e) {
- throw new \InvalidArgumentException('Invalid Federated Cloud ID', 0, $e);
- }
+ $this->collaboratorSearch = $collaboratorSearch;
}
/**
@@ -461,7 +160,9 @@ class ShareesAPIController extends OCSController {
Share::SHARE_TYPE_USER,
];
- if ($itemType === 'file' || $itemType === 'folder') {
+ if ($itemType === null) {
+ throw new OCSBadRequestException('Missing itemType');
+ } elseif ($itemType === 'file' || $itemType === 'folder') {
if ($this->shareManager->allowGroupSharing()) {
$shareTypes[] = Share::SHARE_TYPE_GROUP;
}
@@ -495,94 +196,11 @@ class ShareesAPIController extends OCSController {
$this->limit = (int) $perPage;
$this->offset = $perPage * ($page - 1);
- return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage, $lookup);
- }
-
- /**
- * Method to get out the static call for better testing
- *
- * @param string $itemType
- * @return bool
- */
- protected function isRemoteSharingAllowed($itemType) {
- try {
- $backend = \OC\Share\Share::getBackend($itemType);
- return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE);
- } catch (\Exception $e) {
- return false;
- }
- }
-
- /**
- * Testable search function that does not need globals
- *
- * @param string $search
- * @param string $itemType
- * @param array $shareTypes
- * @param int $page
- * @param int $perPage
- * @param bool $lookup
- * @return DataResponse
- * @throws OCSBadRequestException
- */
- protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage, $lookup) {
- // Verify arguments
- if ($itemType === null) {
- throw new OCSBadRequestException('Missing itemType');
- }
-
- // Get users
- if (in_array(Share::SHARE_TYPE_USER, $shareTypes)) {
- $this->getUsers($search);
- }
-
- // Get groups
- if (in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) {
- $this->getGroups($search);
- }
-
- // Get circles
- if (in_array(Share::SHARE_TYPE_CIRCLE, $shareTypes)) {
- $this->getCircles($search);
- }
+ list($result, $hasMoreResults) = $this->collaboratorSearch->search($search, $shareTypes, $lookup, $this->limit, $this->offset);
+ $response = new DataResponse($result);
- // Get remote
- $remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
- if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) {
- $remoteResults = $this->getRemote($search);
- }
-
- // Get emails
- $mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
- if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) {
- $mailResults = $this->getEmail($search);
- }
-
- // Get from lookup server
- if ($lookup) {
- $this->getLookup($search);
- }
-
- // if we have a exact match, either for the federated cloud id or for the
- // email address we only return the exact match. It is highly unlikely
- // that the exact same email address and federated cloud id exists
- if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) {
- $this->result['emails'] = $mailResults['results'];
- $this->result['exact']['emails'] = $mailResults['exact'];
- } else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) {
- $this->result['remotes'] = $remoteResults['results'];
- $this->result['exact']['remotes'] = $remoteResults['exact'];
- } else {
- $this->result['remotes'] = $remoteResults['results'];
- $this->result['exact']['remotes'] = $remoteResults['exact'];
- $this->result['emails'] = $mailResults['results'];
- $this->result['exact']['emails'] = $mailResults['exact'];
- }
-
- $response = new DataResponse($this->result);
-
- if (sizeof($this->reachedEndFor) < 3) {
+ if ($hasMoreResults) {
$response->addHeader('Link', $this->getPaginationLink($page, [
'search' => $search,
'itemType' => $itemType,
@@ -595,166 +213,21 @@ class ShareesAPIController extends OCSController {
}
/**
- * @param string $search
- * @return array
- */
- protected function getEmail($search) {
- $result = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
-
- // Search in contacts
- //@todo Pagination missing
- $addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']);
- $lowerSearch = strtolower($search);
- foreach ($addressBookContacts as $contact) {
- if (isset($contact['EMAIL'])) {
- $emailAddresses = $contact['EMAIL'];
- if (!is_array($emailAddresses)) {
- $emailAddresses = [$emailAddresses];
- }
- foreach ($emailAddresses as $emailAddress) {
- $exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
-
- if (isset($contact['isLocalSystemBook'])) {
- if ($exactEmailMatch) {
- try {
- $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
- } catch (\InvalidArgumentException $e) {
- continue;
- }
-
- if (!$this->hasUserInResult($cloud->getUser())) {
- $this->result['exact']['users'][] = [
- 'label' => $contact['FN'] . " ($emailAddress)",
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_USER,
- 'shareWith' => $cloud->getUser(),
- ],
- ];
- }
- return ['results' => [], 'exact' => [], 'exactIdMatch' => true];
- }
-
- if ($this->shareeEnumeration) {
- try {
- $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
- } catch (\InvalidArgumentException $e) {
- continue;
- }
-
- if (!$this->hasUserInResult($cloud->getUser())) {
- $this->result['users'][] = [
- 'label' => $contact['FN'] . " ($emailAddress)",
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_USER,
- 'shareWith' => $cloud->getUser(),
- ],
- ];
- }
- }
- continue;
- }
-
- if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) {
- if ($exactEmailMatch) {
- $result['exactIdMatch'] = true;
- }
- $result['exact'][] = [
- 'label' => $contact['FN'] . " ($emailAddress)",
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_EMAIL,
- 'shareWith' => $emailAddress,
- ],
- ];
- } else {
- $result['results'][] = [
- 'label' => $contact['FN'] . " ($emailAddress)",
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_EMAIL,
- 'shareWith' => $emailAddress,
- ],
- ];
- }
- }
- }
- }
-
- if (!$this->shareeEnumeration) {
- $result['results'] = [];
- }
-
- if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) {
- $result['exact'][] = [
- 'label' => $search,
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_EMAIL,
- 'shareWith' => $search,
- ],
- ];
- }
-
- $this->reachedEndFor[] = 'emails';
-
- return $result;
- }
-
- protected function getLookup($search) {
- $isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
- $lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
- $lookupServerUrl = rtrim($lookupServerUrl, '/');
- $result = [];
-
- if($isEnabled === 'yes') {
- try {
- $client = $this->clientService->newClient();
- $response = $client->get(
- $lookupServerUrl . '/users?search=' . urlencode($search),
- [
- 'timeout' => 10,
- 'connect_timeout' => 3,
- ]
- );
-
- $body = json_decode($response->getBody(), true);
-
- $result = [];
- foreach ($body as $lookup) {
- $result[] = [
- 'label' => $lookup['federationId'],
- 'value' => [
- 'shareType' => Share::SHARE_TYPE_REMOTE,
- 'shareWith' => $lookup['federationId'],
- ],
- 'extra' => $lookup,
- ];
- }
- } catch (\Exception $e) {}
- }
-
- $this->result['lookup'] = $result;
- }
-
- /**
- * Check if a given user is already part of the result
+ * Method to get out the static call for better testing
*
- * @param string $userId
+ * @param string $itemType
* @return bool
*/
- protected function hasUserInResult($userId) {
- foreach ($this->result['exact']['users'] as $result) {
- if ($result['value']['shareWith'] === $userId) {
- return true;
- }
- }
-
- foreach ($this->result['users'] as $result) {
- if ($result['value']['shareWith'] === $userId) {
- return true;
- }
+ protected function isRemoteSharingAllowed($itemType) {
+ try {
+ $backend = \OC\Share\Share::getBackend($itemType);
+ return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE);
+ } catch (\Exception $e) {
+ return false;
}
-
- return false;
}
+
/**
* Generates a bunch of pagination links for the current page
*
diff --git a/lib/private/Collaboration/Collaborators/CirclePlugin.php b/lib/private/Collaboration/Collaborators/CirclePlugin.php
new file mode 100644
index 00000000000..3d476644ecc
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/CirclePlugin.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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\Collaboration\Collaborators;
+
+
+use OCA\Circles\Api\Sharees;
+use OCP\Collaboration\Collaborators\ISearchPlugin;
+use OCP\Collaboration\Collaborators\ISearchResult;
+
+class CirclePlugin implements ISearchPlugin {
+ public function search($search, $limit, $offset, ISearchResult $searchResult) {
+ $result = ['wide' => [], 'exact' => []];
+
+ if(\OC_App::isEnabled('circles')) {
+ $circles = Sharees::search($search);
+ if (array_key_exists('circles', $circles['exact'])) {
+ $result['exact'] = $circles['exact']['circles'];
+ }
+ if (array_key_exists('circles', $circles)) {
+ $result['wide'] = $circles['circles'];
+ }
+
+ $searchResult->addResultSet('circles', $result['wide'], $result['exact']);
+ }
+
+ return false;
+ }
+}
diff --git a/lib/private/Collaboration/Collaborators/GroupPlugin.php b/lib/private/Collaboration/Collaborators/GroupPlugin.php
new file mode 100644
index 00000000000..d156190eed9
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/GroupPlugin.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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\Collaboration\Collaborators;
+
+use OCP\Collaboration\Collaborators\ISearchPlugin;
+use OCP\Collaboration\Collaborators\ISearchResult;
+use OCP\IConfig;
+use OCP\IGroup;
+use OCP\IGroupManager;
+use OCP\IUserSession;
+use OCP\Share;
+
+class GroupPlugin implements ISearchPlugin {
+ protected $shareeEnumeration;
+ protected $shareWithGroupOnly;
+
+ /** @var IGroupManager */
+ private $groupManager;
+ /** @var IConfig */
+ private $config;
+ /** @var IUserSession */
+ private $userSession;
+
+ public function __construct(IConfig $config, IGroupManager $groupManager, IUserSession $userSession) {
+ $this->groupManager = $groupManager;
+ $this->config = $config;
+ $this->userSession = $userSession;
+
+ $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
+ $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
+ }
+
+ public function search($search, $limit, $offset, ISearchResult $searchResult) {
+ $hasMoreResults = false;
+ $result = ['wide' => [], 'exact' => []];
+
+ $groups = $this->groupManager->search($search, $limit, $offset);
+ $groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups);
+
+ if (!$this->shareeEnumeration || sizeof($groups) < $limit) {
+ $hasMoreResults = true;
+ }
+
+ $userGroups = [];
+ if (!empty($groups) && $this->shareWithGroupOnly) {
+ // Intersect all the groups that match with the groups this user is a member of
+ $userGroups = $this->groupManager->getUserGroups($this->userSession->getUser());
+ $userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups);
+ $groupIds = array_intersect($groupIds, $userGroups);
+ }
+
+ $lowerSearch = strtolower($search);
+ foreach ($groups as $group) {
+ // FIXME: use a more efficient approach
+ $gid = $group->getGID();
+ if (!in_array($gid, $groupIds)) {
+ continue;
+ }
+ if (strtolower($gid) === $lowerSearch || strtolower($group->getDisplayName()) === $lowerSearch) {
+ $result['exact'][] = [
+ 'label' => $group->getDisplayName(),
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_GROUP,
+ 'shareWith' => $gid,
+ ],
+ ];
+ } else {
+ $result['wide'][] = [
+ 'label' => $group->getDisplayName(),
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_GROUP,
+ 'shareWith' => $gid,
+ ],
+ ];
+ }
+ }
+
+ if ($offset === 0 && empty($result['exact'])) {
+ // On page one we try if the search result has a direct hit on the
+ // user id and if so, we add that to the exact match list
+ $group = $this->groupManager->get($search);
+ if ($group instanceof IGroup && (!$this->shareWithGroupOnly || in_array($group->getGID(), $userGroups))) {
+ array_push($result['exact'], [
+ 'label' => $group->getDisplayName(),
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_GROUP,
+ 'shareWith' => $group->getGID(),
+ ],
+ ]);
+ }
+ }
+
+ if (!$this->shareeEnumeration) {
+ $result['wide'] = [];
+ }
+
+ $searchResult->addResultSet('groups', $result['wide'], $result['exact']);
+
+ return [$result, $hasMoreResults];
+ }
+}
diff --git a/lib/private/Collaboration/Collaborators/LookupPlugin.php b/lib/private/Collaboration/Collaborators/LookupPlugin.php
new file mode 100644
index 00000000000..567b41ca76f
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/LookupPlugin.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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\Collaboration\Collaborators;
+
+
+use OCP\Collaboration\Collaborators\ISearchPlugin;
+use OCP\Collaboration\Collaborators\ISearchResult;
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\Share;
+
+class LookupPlugin implements ISearchPlugin {
+
+ /** @var IConfig */
+ private $config;
+ /** @var IClientService */
+ private $clientService;
+
+ public function __construct(IConfig $config, IClientService $clientService) {
+ $this->config = $config;
+ $this->clientService = $clientService;
+ }
+
+ public function search($search, $limit, $offset, ISearchResult $searchResult) {
+ if ($this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no') !== 'yes') {
+ return false;
+ }
+
+ $lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
+ $lookupServerUrl = rtrim($lookupServerUrl, '/');
+ $result = ['wide' => [], 'exact' => []];
+
+ try {
+ $client = $this->clientService->newClient();
+ $response = $client->get(
+ $lookupServerUrl . '/users?search=' . urlencode($search),
+ [
+ 'timeout' => 10,
+ 'connect_timeout' => 3,
+ ]
+ );
+
+ $body = json_decode($response->getBody(), true);
+
+ $result = [];
+ foreach ($body as $lookup) {
+ $result[] = [
+ 'label' => $lookup['federationId'],
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_REMOTE,
+ 'shareWith' => $lookup['federationId'],
+ ],
+ 'extra' => $lookup,
+ ];
+ }
+ } catch (\Exception $e) {
+ }
+
+ $searchResult->addResultSet('lookup', $result, []);
+
+ }
+}
diff --git a/lib/private/Collaboration/Collaborators/MailPlugin.php b/lib/private/Collaboration/Collaborators/MailPlugin.php
new file mode 100644
index 00000000000..bd4d70e41de
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/MailPlugin.php
@@ -0,0 +1,162 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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\Collaboration\Collaborators;
+
+
+use OCP\Collaboration\Collaborators\ISearchPlugin;
+use OCP\Collaboration\Collaborators\ISearchResult;
+use OCP\Contacts\IManager;
+use OCP\Federation\ICloudIdManager;
+use OCP\IConfig;
+use OCP\Share;
+
+class MailPlugin implements ISearchPlugin {
+ protected $shareeEnumeration;
+
+ /** @var IManager */
+ private $contactsManager;
+ /** @var ICloudIdManager */
+ private $cloudIdManager;
+ /** @var IConfig */
+ private $config;
+
+ public function __construct(IManager $contactsManager, ICloudIdManager $cloudIdManager, IConfig $config) {
+ $this->contactsManager = $contactsManager;
+ $this->cloudIdManager = $cloudIdManager;
+ $this->config = $config;
+
+ $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
+ }
+
+ /**
+ * @param $search
+ * @param $limit
+ * @param $offset
+ * @param ISearchResult $searchResult
+ * @return bool
+ * @since 13.0.0
+ */
+ public function search($search, $limit, $offset, ISearchResult $searchResult) {
+ $result = ['wide' => [], 'exact' => []];
+
+ // Search in contacts
+ //@todo Pagination missing
+ $addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']);
+ $lowerSearch = strtolower($search);
+ foreach ($addressBookContacts as $contact) {
+ if (isset($contact['EMAIL'])) {
+ $emailAddresses = $contact['EMAIL'];
+ if (!is_array($emailAddresses)) {
+ $emailAddresses = [$emailAddresses];
+ }
+ foreach ($emailAddresses as $emailAddress) {
+ $exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
+
+ if (isset($contact['isLocalSystemBook'])) {
+ if ($exactEmailMatch) {
+ try {
+ $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
+ } catch (\InvalidArgumentException $e) {
+ continue;
+ }
+
+ if (!$searchResult->hasResult('users', $cloud->getUser())) {
+ $singleResult = [[
+ 'label' => $contact['FN'] . " ($emailAddress)",
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_USER,
+ 'shareWith' => $cloud->getUser(),
+ ],
+ ]];
+ $searchResult->addResultSet('users', [], $singleResult);
+ $searchResult->markExactIdMatch('emails');
+ }
+ return false;
+ }
+
+ if ($this->shareeEnumeration) {
+ try {
+ $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
+ } catch (\InvalidArgumentException $e) {
+ continue;
+ }
+
+ if (!$searchResult->hasResult('users', $cloud->getUser())) {
+ $singleResult = [[
+ 'label' => $contact['FN'] . " ($emailAddress)",
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_USER,
+ 'shareWith' => $cloud->getUser(),
+ ]],
+ ];
+ $searchResult->addResultSet('users', $singleResult, []);
+ $result = [];
+ }
+ }
+ continue;
+ }
+
+ if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) {
+ if ($exactEmailMatch) {
+ $searchResult->markExactIdMatch('emails');
+ }
+ $result['exact'][] = [
+ 'label' => $contact['FN'] . " ($emailAddress)",
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_EMAIL,
+ 'shareWith' => $emailAddress,
+ ],
+ ];
+ } else {
+ $result['wide'][] = [
+ 'label' => $contact['FN'] . " ($emailAddress)",
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_EMAIL,
+ 'shareWith' => $emailAddress,
+ ],
+ ];
+ }
+ }
+ }
+ }
+
+ if (!$this->shareeEnumeration) {
+ $result['wide'] = [];
+ }
+
+ if (!$searchResult->hasExactIdMatch('emails') && filter_var($search, FILTER_VALIDATE_EMAIL)) {
+ $result['exact'][] = [
+ 'label' => $search,
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_EMAIL,
+ 'shareWith' => $search,
+ ],
+ ];
+ }
+
+ $searchResult->addResultSet('emails', $result['wide'], $result['exact']);
+
+ return false;
+ }
+}
diff --git a/lib/private/Collaboration/Collaborators/RemotePlugin.php b/lib/private/Collaboration/Collaborators/RemotePlugin.php
new file mode 100644
index 00000000000..ff075b8e42d
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/RemotePlugin.php
@@ -0,0 +1,135 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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\Collaboration\Collaborators;
+
+
+use OCP\Collaboration\Collaborators\ISearchPlugin;
+use OCP\Collaboration\Collaborators\ISearchResult;
+use OCP\Contacts\IManager;
+use OCP\Federation\ICloudIdManager;
+use OCP\IConfig;
+use OCP\Share;
+
+class RemotePlugin implements ISearchPlugin {
+ protected $shareeEnumeration;
+
+ /** @var IManager */
+ private $contactsManager;
+ /** @var ICloudIdManager */
+ private $cloudIdManager;
+ /** @var IConfig */
+ private $config;
+
+ public function __construct(IManager $contactsManager, ICloudIdManager $cloudIdManager, IConfig $config) {
+ $this->contactsManager = $contactsManager;
+ $this->cloudIdManager = $cloudIdManager;
+ $this->config = $config;
+
+ $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
+ }
+
+ public function search($search, $limit, $offset, ISearchResult $searchResult) {
+ $result = ['wide' => [], 'exact' => []];
+
+ // Search in contacts
+ //@todo Pagination missing
+ $addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']);
+ foreach ($addressBookContacts as $contact) {
+ if (isset($contact['isLocalSystemBook'])) {
+ continue;
+ }
+ if (isset($contact['CLOUD'])) {
+ $cloudIds = $contact['CLOUD'];
+ if (!is_array($cloudIds)) {
+ $cloudIds = [$cloudIds];
+ }
+ $lowerSearch = strtolower($search);
+ foreach ($cloudIds as $cloudId) {
+ try {
+ list(, $serverUrl) = $this->splitUserRemote($cloudId);
+ } catch (\InvalidArgumentException $e) {
+ continue;
+ }
+
+ if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) {
+ if (strtolower($cloudId) === $lowerSearch) {
+ $searchResult->hasExactIdMatch('remotes');
+ }
+ $result['exact'][] = [
+ 'label' => $contact['FN'] . " ($cloudId)",
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_REMOTE,
+ 'shareWith' => $cloudId,
+ 'server' => $serverUrl,
+ ],
+ ];
+ } else {
+ $result['wide'][] = [
+ 'label' => $contact['FN'] . " ($cloudId)",
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_REMOTE,
+ 'shareWith' => $cloudId,
+ 'server' => $serverUrl,
+ ],
+ ];
+ }
+ }
+ }
+ }
+
+ if (!$this->shareeEnumeration) {
+ $result['wide'] = [];
+ }
+
+ if (!$searchResult->hasExactIdMatch('remotes') && $this->cloudIdManager->isValidCloudId($search) && $offset === 0) {
+ $result['exact'][] = [
+ 'label' => $search,
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_REMOTE,
+ 'shareWith' => $search,
+ ],
+ ];
+ }
+
+ $searchResult->addResultSet('remotes', $result['wide'], $result['exact']);
+
+ return false;
+ }
+
+ /**
+ * split user and remote from federated cloud id
+ *
+ * @param string $address federated share address
+ * @return array [user, remoteURL]
+ * @throws \InvalidArgumentException
+ */
+ public function splitUserRemote($address) {
+ try {
+ $cloudId = $this->cloudIdManager->resolveCloudId($address);
+ return [$cloudId->getUser(), $cloudId->getRemote()];
+ } catch (\InvalidArgumentException $e) {
+ throw new \InvalidArgumentException('Invalid Federated Cloud ID', 0, $e);
+ }
+ }
+}
diff --git a/lib/private/Collaboration/Collaborators/Search.php b/lib/private/Collaboration/Collaborators/Search.php
new file mode 100644
index 00000000000..abca13bcfe9
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/Search.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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\Collaboration\Collaborators;
+
+use OCP\Collaboration\Collaborators\ISearch;
+use OCP\Collaboration\Collaborators\ISearchPlugin;
+use OCP\Collaboration\Collaborators\ISearchResult;
+use OCP\IContainer;
+use OCP\Share;
+
+class Search implements ISearch {
+ /** @var IContainer */
+ private $c;
+
+ public function __construct(IContainer $c) {
+ $this->c = $c;
+ }
+
+ public function search($search, array $shareTypes, $lookup, $limit, $offset) {
+ $hasMoreResults = false;
+
+ $pluginList = [
+ Share::SHARE_TYPE_USER => UserPlugin::class,
+ Share::SHARE_TYPE_GROUP => GroupPlugin::class,
+ Share::SHARE_TYPE_CIRCLE => CirclePlugin::class,
+ Share::SHARE_TYPE_EMAIL => MailPlugin::class,
+ Share::SHARE_TYPE_REMOTE => RemotePlugin::class,
+ ];
+
+ /** @var ISearchResult $searchResult */
+ $searchResult = $this->c->resolve(SearchResult::class);
+
+ foreach ($shareTypes as $type) {
+ if(!isset($pluginList[$type])) {
+ continue;
+ }
+ /** @var ISearchPlugin $searchPlugin */
+ $searchPlugin = $this->c->resolve($pluginList[$type]);
+ $hasMoreResults |= $searchPlugin->search($search, $limit, $offset, $searchResult);
+ }
+
+ // Get from lookup server, not a separate share type
+ if ($lookup) {
+ $searchPlugin = $this->c->resolve(LookupPlugin::class);
+ $hasMoreResults |= $searchPlugin->search($search, $limit, $offset, $searchResult);
+ }
+
+ // sanitizing, could go into the plugins as well
+
+ // if we have a exact match, either for the federated cloud id or for the
+ // email address we only return the exact match. It is highly unlikely
+ // that the exact same email address and federated cloud id exists
+ if($searchResult->hasExactIdMatch('emails') && !$searchResult->hasExactIdMatch('remotes')) {
+ $searchResult->unsetResult('remotes');
+ } elseif (!$searchResult->hasExactIdMatch('emails') && $searchResult->hasExactIdMatch('remotes')) {
+ $searchResult->unsetResult('emails');
+ }
+
+ return [$searchResult->asArray(), $hasMoreResults];
+ }
+}
diff --git a/lib/private/Collaboration/Collaborators/SearchResult.php b/lib/private/Collaboration/Collaborators/SearchResult.php
new file mode 100644
index 00000000000..f3559c9327f
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/SearchResult.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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\Collaboration\Collaborators;
+
+
+use OCP\Collaboration\Collaborators\ISearchResult;
+
+class SearchResult implements ISearchResult {
+
+ protected $result = [
+ 'exact' => [
+ 'users' => [],
+ 'groups' => [],
+ 'remotes' => [],
+ 'emails' => [],
+ 'circles' => [],
+ ],
+ 'users' => [],
+ 'groups' => [],
+ 'remotes' => [],
+ 'emails' => [],
+ 'lookup' => [],
+ 'circles' => [],
+ ];
+
+ protected $exactIdMatches = [];
+
+ public function addResultSet($type, array $matches, array $exactMatches = null) {
+ if(!isset($this->result[$type])) {
+ throw new \InvalidArgumentException('Invalid type provided');
+ }
+
+ $this->result[$type] = array_merge($this->result[$type], $matches);
+ if(is_array($exactMatches)) {
+ $this->result['exact'][$type] = array_merge($this->result['exact'][$type], $exactMatches);
+ }
+ }
+
+ public function markExactIdMatch($type) {
+ $this->exactIdMatches[$type] = 1;
+ }
+
+ public function hasExactIdMatch($type) {
+ return isset($this->exactIdMatches[$type]);
+ }
+
+ public function hasResult($type, $collaboratorId) {
+ if(!isset($this->result[$type])) {
+ throw new \InvalidArgumentException('Invalid type provided');
+ }
+
+ $resultArrays = [$this->result['exact'][$type], $this->result[$type]];
+ foreach($resultArrays as $resultArray) {
+ if ($resultArray['value']['shareWith'] === $collaboratorId) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function asArray() {
+ return $this->result;
+ }
+
+ public function unsetResult($type) {
+ if(!isset($this->result[$type])) {
+ throw new \InvalidArgumentException('Invalid type provided');
+ }
+
+ $this->result[$type] = [];
+ if(isset($this->$result['exact'][$type])) {
+ $this->result['exact'][$type] = [];
+ }
+ }
+}
diff --git a/lib/private/Collaboration/Collaborators/UserPlugin.php b/lib/private/Collaboration/Collaborators/UserPlugin.php
new file mode 100644
index 00000000000..815862b8726
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/UserPlugin.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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\Collaboration\Collaborators;
+
+
+use OCP\Collaboration\Collaborators\ISearchPlugin;
+use OCP\Collaboration\Collaborators\ISearchResult;
+use OCP\IConfig;
+use OCP\IGroupManager;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use OCP\Share;
+
+class UserPlugin implements ISearchPlugin {
+ /* @var bool */
+ protected $shareWithGroupOnly;
+ protected $shareeEnumeration;
+
+ /** @var IConfig */
+ private $config;
+ /** @var IGroupManager */
+ private $groupManager;
+ /** @var IUserSession */
+ private $userSession;
+ /** @var IUserManager */
+ private $userManager;
+
+ public function __construct(IConfig $config, IUserManager $userManager, IGroupManager $groupManager, IUserSession $userSession) {
+ $this->config = $config;
+
+ $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
+ $this->groupManager = $groupManager;
+ $this->userSession = $userSession;
+ $this->userManager = $userManager;
+
+ $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
+ }
+
+ public function search($search, $limit, $offset, ISearchResult $searchResult) {
+ $result = ['wide' => [], 'exact' => []];
+ $users = [];
+ $hasMoreResults = false;
+
+ $userGroups = [];
+ if ($this->shareWithGroupOnly) {
+ // Search in all the groups this user is part of
+ $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
+ foreach ($userGroups as $userGroup) {
+ $usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $limit, $offset);
+ foreach ($usersTmp as $uid => $userDisplayName) {
+ $users[$uid] = $userDisplayName;
+ }
+ }
+ } else {
+ // Search in all users
+ $usersTmp = $this->userManager->searchDisplayName($search, $limit, $offset);
+
+ foreach ($usersTmp as $user) {
+ $users[$user->getUID()] = $user->getDisplayName();
+ }
+ }
+
+ if (!$this->shareeEnumeration || sizeof($users) < $limit) {
+ $hasMoreResults = true;
+ }
+
+ $foundUserById = false;
+ $lowerSearch = strtolower($search);
+ foreach ($users as $uid => $userDisplayName) {
+ if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) {
+ if (strtolower($uid) === $lowerSearch) {
+ $foundUserById = true;
+ }
+ $result['exact'][] = [
+ 'label' => $userDisplayName,
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_USER,
+ 'shareWith' => $uid,
+ ],
+ ];
+ } else {
+ $result['wide'][] = [
+ 'label' => $userDisplayName,
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_USER,
+ 'shareWith' => $uid,
+ ],
+ ];
+ }
+ }
+
+ if ($offset === 0 && !$foundUserById) {
+ // On page one we try if the search result has a direct hit on the
+ // user id and if so, we add that to the exact match list
+ $user = $this->userManager->get($search);
+ if ($user instanceof IUser) {
+ $addUser = true;
+
+ if ($this->shareWithGroupOnly) {
+ // Only add, if we have a common group
+ $commonGroups = array_intersect($userGroups, $this->groupManager->getUserGroupIds($user));
+ $addUser = !empty($commonGroups);
+ }
+
+ if ($addUser) {
+ array_push($result['exact'], [
+ 'label' => $user->getDisplayName(),
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_USER,
+ 'shareWith' => $user->getUID(),
+ ],
+ ]);
+ }
+ }
+ }
+
+ if (!$this->shareeEnumeration) {
+ $result['wide'] = [];
+ }
+
+ $searchResult->addResultSet('users', $result['wide'], $result['exact']);
+
+ return $hasMoreResults;
+ }
+}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index a20d9ccfc01..88fc79e472e 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -993,6 +993,11 @@ class Server extends ServerContainer implements IServerContainer {
});
$this->registerAlias('ShareManager', \OCP\Share\IManager::class);
+ $this->registerService(\OCP\Collaboration\Collaborators\ISearch::class, function(Server $c) {
+ return new Collaboration\Collaborators\Search($c);
+ });
+ $this->registerAlias('CollaboratorSearch', \OCP\Collaboration\Collaborators\ISearch::class);
+
$this->registerService('SettingsManager', function (Server $c) {
$manager = new \OC\Settings\Manager(
$c->getLogger(),
@@ -1777,6 +1782,13 @@ class Server extends ServerContainer implements IServerContainer {
}
/**
+ * @return \OCP\Collaboration\Collaborators\ISearch
+ */
+ public function getCollaboratorSearch() {
+ return $this->query('CollaboratorSearch');
+ }
+
+ /**
* Returns the LDAP Provider
*
* @return \OCP\LDAP\ILDAPProvider
diff --git a/lib/public/Collaboration/Collaborators/ISearch.php b/lib/public/Collaboration/Collaborators/ISearch.php
new file mode 100644
index 00000000000..db2e5f7ac05
--- /dev/null
+++ b/lib/public/Collaboration/Collaborators/ISearch.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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 OCP\Collaboration\Collaborators;
+
+interface ISearch {
+ /**
+ * @param string $search
+ * @param array $shareTypes
+ * @param bool $lookup
+ * @param int $limit
+ * @param int $offset
+ * @return array with two elements, 1st ISearchResult as array, 2nd a bool indicating whether more result are available
+ * @since 13.0.0
+ */
+ public function search($search, array $shareTypes, $lookup, $limit, $offset);
+}
diff --git a/lib/public/Collaboration/Collaborators/ISearchPlugin.php b/lib/public/Collaboration/Collaborators/ISearchPlugin.php
new file mode 100644
index 00000000000..cae93bfd731
--- /dev/null
+++ b/lib/public/Collaboration/Collaborators/ISearchPlugin.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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 OCP\Collaboration\Collaborators;
+
+
+interface ISearchPlugin {
+ /**
+ * @param string $search
+ * @param int $limit
+ * @param int $offset
+ * @param ISearchResult $searchResult
+ * @return bool whether the plugin has more results
+ * @since 13.0.0
+ */
+ public function search($search, $limit, $offset, ISearchResult $searchResult);
+}
diff --git a/lib/public/Collaboration/Collaborators/ISearchResult.php b/lib/public/Collaboration/Collaborators/ISearchResult.php
new file mode 100644
index 00000000000..b8d0a32cc0a
--- /dev/null
+++ b/lib/public/Collaboration/Collaborators/ISearchResult.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @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 OCP\Collaboration\Collaborators;
+
+
+interface ISearchResult {
+ /**
+ * @param string $type one of: users, groups, remotes, email, circles, lookup
+ * @param array $matches
+ * @param array|null $exactMatches
+ * @since 13.0.0
+ */
+ public function addResultSet($type, array $matches, array $exactMatches = null);
+
+ /**
+ * @param string $type one of: users, groups, remotes, email, circles, lookup
+ * @param string $collaboratorId
+ * @return bool
+ * @since 13.0.0
+ */
+ public function hasResult($type, $collaboratorId);
+
+ /**
+ * @param string $type one of: users, groups, remotes, email, circles, lookup
+ * @since 13.0.0
+ */
+ public function unsetResult($type);
+
+ /**
+ * @param string $type one of: users, groups, remotes, email, circles, lookup
+ * @since 13.0.0
+ */
+ public function markExactIdMatch($type);
+
+ /**
+ * @param string $type one of: users, groups, remotes, email, circles, lookup
+ * @return bool
+ * @since 13.0.0
+ */
+ public function hasExactIdMatch($type);
+
+ /**
+ * @return array
+ * @since 13.0.0
+ */
+ public function asArray();
+}