summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/composer/composer/autoload_classmap.php11
-rw-r--r--lib/composer/composer/autoload_static.php11
-rw-r--r--lib/private/App/InfoParser.php6
-rw-r--r--lib/private/Collaboration/Collaborators/GroupPlugin.php124
-rw-r--r--lib/private/Collaboration/Collaborators/LookupPlugin.php85
-rw-r--r--lib/private/Collaboration/Collaborators/MailPlugin.php164
-rw-r--r--lib/private/Collaboration/Collaborators/RemotePlugin.php137
-rw-r--r--lib/private/Collaboration/Collaborators/Search.php89
-rw-r--r--lib/private/Collaboration/Collaborators/SearchResult.php86
-rw-r--r--lib/private/Collaboration/Collaborators/UserPlugin.php149
-rw-r--r--lib/private/Server.php25
-rw-r--r--lib/private/legacy/app.php14
-rw-r--r--lib/public/Collaboration/Collaborators/ISearch.php50
-rw-r--r--lib/public/Collaboration/Collaborators/ISearchPlugin.php42
-rw-r--r--lib/public/Collaboration/Collaborators/ISearchResult.php73
-rw-r--r--lib/public/Collaboration/Collaborators/SearchResultType.php73
16 files changed, 1139 insertions, 0 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 50bd6d33274..9341bd1a762 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -68,6 +68,10 @@ return array(
'OCP\\BackgroundJob\\IJobList' => $baseDir . '/lib/public/BackgroundJob/IJobList.php',
'OCP\\Capabilities\\ICapability' => $baseDir . '/lib/public/Capabilities/ICapability.php',
'OCP\\Capabilities\\IPublicCapability' => $baseDir . '/lib/public/Capabilities/IPublicCapability.php',
+ 'OCP\\Collaboration\\Collaborators\\ISearch' => $baseDir . '/lib/public/Collaboration/Collaborators/ISearch.php',
+ 'OCP\\Collaboration\\Collaborators\\ISearchPlugin' => $baseDir . '/lib/public/Collaboration/Collaborators/ISearchPlugin.php',
+ 'OCP\\Collaboration\\Collaborators\\ISearchResult' => $baseDir . '/lib/public/Collaboration/Collaborators/ISearchResult.php',
+ 'OCP\\Collaboration\\Collaborators\\SearchResultType' => $baseDir . '/lib/public/Collaboration/Collaborators/SearchResultType.php',
'OCP\\Command\\IBus' => $baseDir . '/lib/public/Command/IBus.php',
'OCP\\Command\\ICommand' => $baseDir . '/lib/public/Command/ICommand.php',
'OCP\\Comments\\CommentsEntityEvent' => $baseDir . '/lib/public/Comments/CommentsEntityEvent.php',
@@ -384,6 +388,13 @@ return array(
'OC\\Cache\\CappedMemoryCache' => $baseDir . '/lib/private/Cache/CappedMemoryCache.php',
'OC\\Cache\\File' => $baseDir . '/lib/private/Cache/File.php',
'OC\\CapabilitiesManager' => $baseDir . '/lib/private/CapabilitiesManager.php',
+ 'OC\\Collaboration\\Collaborators\\GroupPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/GroupPlugin.php',
+ 'OC\\Collaboration\\Collaborators\\LookupPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/LookupPlugin.php',
+ 'OC\\Collaboration\\Collaborators\\MailPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/MailPlugin.php',
+ 'OC\\Collaboration\\Collaborators\\RemotePlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/RemotePlugin.php',
+ 'OC\\Collaboration\\Collaborators\\Search' => $baseDir . '/lib/private/Collaboration/Collaborators/Search.php',
+ 'OC\\Collaboration\\Collaborators\\SearchResult' => $baseDir . '/lib/private/Collaboration/Collaborators/SearchResult.php',
+ 'OC\\Collaboration\\Collaborators\\UserPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/UserPlugin.php',
'OC\\Command\\AsyncBus' => $baseDir . '/lib/private/Command/AsyncBus.php',
'OC\\Command\\CallableJob' => $baseDir . '/lib/private/Command/CallableJob.php',
'OC\\Command\\ClosureJob' => $baseDir . '/lib/private/Command/ClosureJob.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 1828957b32a..5715ff6ed05 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -98,6 +98,10 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\BackgroundJob\\IJobList' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/IJobList.php',
'OCP\\Capabilities\\ICapability' => __DIR__ . '/../../..' . '/lib/public/Capabilities/ICapability.php',
'OCP\\Capabilities\\IPublicCapability' => __DIR__ . '/../../..' . '/lib/public/Capabilities/IPublicCapability.php',
+ 'OCP\\Collaboration\\Collaborators\\ISearch' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/ISearch.php',
+ 'OCP\\Collaboration\\Collaborators\\ISearchPlugin' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/ISearchPlugin.php',
+ 'OCP\\Collaboration\\Collaborators\\ISearchResult' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/ISearchResult.php',
+ 'OCP\\Collaboration\\Collaborators\\SearchResultType' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/SearchResultType.php',
'OCP\\Command\\IBus' => __DIR__ . '/../../..' . '/lib/public/Command/IBus.php',
'OCP\\Command\\ICommand' => __DIR__ . '/../../..' . '/lib/public/Command/ICommand.php',
'OCP\\Comments\\CommentsEntityEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/CommentsEntityEvent.php',
@@ -414,6 +418,13 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Cache\\CappedMemoryCache' => __DIR__ . '/../../..' . '/lib/private/Cache/CappedMemoryCache.php',
'OC\\Cache\\File' => __DIR__ . '/../../..' . '/lib/private/Cache/File.php',
'OC\\CapabilitiesManager' => __DIR__ . '/../../..' . '/lib/private/CapabilitiesManager.php',
+ 'OC\\Collaboration\\Collaborators\\GroupPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/GroupPlugin.php',
+ 'OC\\Collaboration\\Collaborators\\LookupPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/LookupPlugin.php',
+ 'OC\\Collaboration\\Collaborators\\MailPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/MailPlugin.php',
+ 'OC\\Collaboration\\Collaborators\\RemotePlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/RemotePlugin.php',
+ 'OC\\Collaboration\\Collaborators\\Search' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/Search.php',
+ 'OC\\Collaboration\\Collaborators\\SearchResult' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/SearchResult.php',
+ 'OC\\Collaboration\\Collaborators\\UserPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/UserPlugin.php',
'OC\\Command\\AsyncBus' => __DIR__ . '/../../..' . '/lib/private/Command/AsyncBus.php',
'OC\\Command\\CallableJob' => __DIR__ . '/../../..' . '/lib/private/Command/CallableJob.php',
'OC\\Command\\ClosureJob' => __DIR__ . '/../../..' . '/lib/private/Command/ClosureJob.php',
diff --git a/lib/private/App/InfoParser.php b/lib/private/App/InfoParser.php
index fef8ab7a1e8..0531682d67a 100644
--- a/lib/private/App/InfoParser.php
+++ b/lib/private/App/InfoParser.php
@@ -165,6 +165,12 @@ class InfoParser {
if (isset($array['activity']['providers']['provider']) && is_array($array['activity']['providers']['provider'])) {
$array['activity']['providers'] = $array['activity']['providers']['provider'];
}
+ if (isset($array['collaboration']['collaborators']['searchPlugins']['searchPlugin'])
+ && is_array($array['collaboration']['collaborators']['searchPlugins']['searchPlugin'])
+ && !isset($array['collaboration']['collaborators']['searchPlugins']['searchPlugin']['class'])
+ ) {
+ $array['collaboration']['collaborators']['searchPlugins'] = $array['collaboration']['collaborators']['searchPlugins']['searchPlugin'];
+ }
if(!is_null($this->cache)) {
$this->cache->set($fileCacheKey, json_encode($array));
diff --git a/lib/private/Collaboration/Collaborators/GroupPlugin.php b/lib/private/Collaboration/Collaborators/GroupPlugin.php
new file mode 100644
index 00000000000..0b2b3d71028
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/GroupPlugin.php
@@ -0,0 +1,124 @@
+<?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\Collaboration\Collaborators\SearchResultType;
+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'] = [];
+ }
+
+ $type = new SearchResultType('groups');
+ $searchResult->addResultSet($type, $result['wide'], $result['exact']);
+
+ return $hasMoreResults;
+ }
+}
diff --git a/lib/private/Collaboration/Collaborators/LookupPlugin.php b/lib/private/Collaboration/Collaborators/LookupPlugin.php
new file mode 100644
index 00000000000..3a6a0943772
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/LookupPlugin.php
@@ -0,0 +1,85 @@
+<?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\Collaboration\Collaborators\SearchResultType;
+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 = [];
+
+ try {
+ $client = $this->clientService->newClient();
+ $response = $client->get(
+ $lookupServerUrl . '/users?search=' . urlencode($search),
+ [
+ 'timeout' => 10,
+ 'connect_timeout' => 3,
+ ]
+ );
+
+ $body = json_decode($response->getBody(), true);
+
+ foreach ($body as $lookup) {
+ $result[] = [
+ 'label' => $lookup['federationId'],
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_REMOTE,
+ 'shareWith' => $lookup['federationId'],
+ ],
+ 'extra' => $lookup,
+ ];
+ }
+ } catch (\Exception $e) {
+ }
+
+ $type = new SearchResultType('lookup');
+ $searchResult->addResultSet($type, $result, []);
+
+ return false;
+ }
+}
diff --git a/lib/private/Collaboration/Collaborators/MailPlugin.php b/lib/private/Collaboration/Collaborators/MailPlugin.php
new file mode 100644
index 00000000000..d28bd3692a4
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/MailPlugin.php
@@ -0,0 +1,164 @@
+<?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\Collaboration\Collaborators\SearchResultType;
+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' => []];
+ $userType = new SearchResultType('users');
+ $emailType = new SearchResultType('emails');
+
+ // 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($userType, $cloud->getUser())) {
+ $singleResult = [[
+ 'label' => $contact['FN'] . " ($emailAddress)",
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_USER,
+ 'shareWith' => $cloud->getUser(),
+ ],
+ ]];
+ $searchResult->addResultSet($userType, [], $singleResult);
+ $searchResult->markExactIdMatch($emailType);
+ }
+ return false;
+ }
+
+ if ($this->shareeEnumeration) {
+ try {
+ $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
+ } catch (\InvalidArgumentException $e) {
+ continue;
+ }
+
+ if (!$searchResult->hasResult($userType, $cloud->getUser())) {
+ $singleResult = [[
+ 'label' => $contact['FN'] . " ($emailAddress)",
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_USER,
+ 'shareWith' => $cloud->getUser(),
+ ]],
+ ];
+ $searchResult->addResultSet($userType, $singleResult, []);
+ }
+ }
+ continue;
+ }
+
+ if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) {
+ if ($exactEmailMatch) {
+ $searchResult->markExactIdMatch($emailType);
+ }
+ $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($emailType) && filter_var($search, FILTER_VALIDATE_EMAIL)) {
+ $result['exact'][] = [
+ 'label' => $search,
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_EMAIL,
+ 'shareWith' => $search,
+ ],
+ ];
+ }
+
+ $searchResult->addResultSet($emailType, $result['wide'], $result['exact']);
+
+ return true;
+ }
+}
diff --git a/lib/private/Collaboration/Collaborators/RemotePlugin.php b/lib/private/Collaboration/Collaborators/RemotePlugin.php
new file mode 100644
index 00000000000..b17a64e4ff1
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/RemotePlugin.php
@@ -0,0 +1,137 @@
+<?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\Collaboration\Collaborators\SearchResultType;
+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' => []];
+ $resultType = new SearchResultType('remotes');
+
+ // 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->markExactIdMatch($resultType);
+ }
+ $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($resultType) && $this->cloudIdManager->isValidCloudId($search) && $offset === 0) {
+ $result['exact'][] = [
+ 'label' => $search,
+ 'value' => [
+ 'shareType' => Share::SHARE_TYPE_REMOTE,
+ 'shareWith' => $search,
+ ],
+ ];
+ }
+
+ $searchResult->addResultSet($resultType, $result['wide'], $result['exact']);
+
+ return true;
+ }
+
+ /**
+ * 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..e9b15dd1201
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/Search.php
@@ -0,0 +1,89 @@
+<?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\Collaboration\Collaborators\SearchResultType;
+use OCP\IContainer;
+use OCP\Share;
+
+class Search implements ISearch {
+ /** @var IContainer */
+ private $c;
+
+ protected $pluginList = [];
+
+ public function __construct(IContainer $c) {
+ $this->c = $c;
+ }
+
+ public function search($search, array $shareTypes, $lookup, $limit, $offset) {
+ $hasMoreResults = false;
+
+ /** @var ISearchResult $searchResult */
+ $searchResult = $this->c->resolve(SearchResult::class);
+
+ foreach ($shareTypes as $type) {
+ if(!isset($this->pluginList[$type])) {
+ continue;
+ }
+ foreach ($this->pluginList[$type] as $plugin) {
+ /** @var ISearchPlugin $searchPlugin */
+ $searchPlugin = $this->c->resolve($plugin);
+ $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
+ $emailType = new SearchResultType('emails');
+ $remoteType = new SearchResultType('remotes');
+ if($searchResult->hasExactIdMatch($emailType) && !$searchResult->hasExactIdMatch($remoteType)) {
+ $searchResult->unsetResult($remoteType);
+ } elseif (!$searchResult->hasExactIdMatch($emailType) && $searchResult->hasExactIdMatch($remoteType)) {
+ $searchResult->unsetResult($emailType);
+ }
+
+ return [$searchResult->asArray(), (bool)$hasMoreResults];
+ }
+
+ public function registerPlugin(array $pluginInfo) {
+ $shareType = constant(Share::class . '::' . $pluginInfo['shareType']);
+ if($shareType === null) {
+ throw new \InvalidArgumentException('Provided ShareType is invalid');
+ }
+ $this->pluginList[$shareType][] = $pluginInfo['class'];
+ }
+}
diff --git a/lib/private/Collaboration/Collaborators/SearchResult.php b/lib/private/Collaboration/Collaborators/SearchResult.php
new file mode 100644
index 00000000000..7b32b388203
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/SearchResult.php
@@ -0,0 +1,86 @@
+<?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;
+use OCP\Collaboration\Collaborators\SearchResultType;
+
+class SearchResult implements ISearchResult {
+
+ protected $result = [
+ 'exact' => [],
+ ];
+
+ protected $exactIdMatches = [];
+
+ public function addResultSet(SearchResultType $type, array $matches, array $exactMatches = null) {
+ $type = $type->getLabel();
+ if(!isset($this->result[$type])) {
+ $this->result[$type] = [];
+ $this->result['exact'][$type] = [];
+ }
+
+ $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(SearchResultType $type) {
+ $this->exactIdMatches[$type->getLabel()] = 1;
+ }
+
+ public function hasExactIdMatch(SearchResultType$type) {
+ return isset($this->exactIdMatches[$type->getLabel()]);
+ }
+
+ public function hasResult(SearchResultType $type, $collaboratorId) {
+ $type = $type->getLabel();
+ if(!isset($this->result[$type])) {
+ return false;
+ }
+
+ $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(SearchResultType $type) {
+ $type = $type->getLabel();
+ $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..86a55aa428f
--- /dev/null
+++ b/lib/private/Collaboration/Collaborators/UserPlugin.php
@@ -0,0 +1,149 @@
+<?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\Collaboration\Collaborators\SearchResultType;
+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->groupManager = $groupManager;
+ $this->userSession = $userSession;
+ $this->userManager = $userManager;
+
+ $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
+ $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'] = [];
+ }
+
+ $type = new SearchResultType('users');
+ $searchResult->addResultSet($type, $result['wide'], $result['exact']);
+
+ return $hasMoreResults;
+ }
+}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index a20d9ccfc01..29aee06d896 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -52,6 +52,10 @@ use OC\AppFramework\Http\Request;
use OC\AppFramework\Utility\SimpleContainer;
use OC\AppFramework\Utility\TimeFactory;
use OC\Authentication\LoginCredentials\Store;
+use OC\Collaboration\Collaborators\GroupPlugin;
+use OC\Collaboration\Collaborators\MailPlugin;
+use OC\Collaboration\Collaborators\RemotePlugin;
+use OC\Collaboration\Collaborators\UserPlugin;
use OC\Command\CronBus;
use OC\Contacts\ContactsMenu\ActionFactory;
use OC\Diagnostics\EventLogger;
@@ -115,6 +119,7 @@ use OCP\Contacts\ContactsMenu\IActionFactory;
use OCP\Lock\ILockingProvider;
use OCP\RichObjectStrings\IValidator;
use OCP\Security\IContentSecurityPolicyManager;
+use OCP\Share;
use OCP\Share\IShareHelper;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -993,6 +998,19 @@ class Server extends ServerContainer implements IServerContainer {
});
$this->registerAlias('ShareManager', \OCP\Share\IManager::class);
+ $this->registerService(\OCP\Collaboration\Collaborators\ISearch::class, function(Server $c) {
+ $instance = new Collaboration\Collaborators\Search($c);
+
+ // register default plugins
+ $instance->registerPlugin(['shareType' => 'SHARE_TYPE_USER', 'class' => UserPlugin::class]);
+ $instance->registerPlugin(['shareType' => 'SHARE_TYPE_GROUP', 'class' => GroupPlugin::class]);
+ $instance->registerPlugin(['shareType' => 'SHARE_TYPE_EMAIL', 'class' => MailPlugin::class]);
+ $instance->registerPlugin(['shareType' => 'SHARE_TYPE_REMOTE', 'class' => RemotePlugin::class]);
+
+ return $instance;
+ });
+ $this->registerAlias('CollaboratorSearch', \OCP\Collaboration\Collaborators\ISearch::class);
+
$this->registerService('SettingsManager', function (Server $c) {
$manager = new \OC\Settings\Manager(
$c->getLogger(),
@@ -1777,6 +1795,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/private/legacy/app.php b/lib/private/legacy/app.php
index 24c7a344e00..a33c9be20fc 100644
--- a/lib/private/legacy/app.php
+++ b/lib/private/legacy/app.php
@@ -174,6 +174,20 @@ class OC_App {
\OC::$server->getActivityManager()->registerProvider($provider);
}
}
+ if (!empty($info['collaboration']['plugins'])) {
+ // deal with one or many plugin entries
+ $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
+ [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
+ foreach ($plugins as $plugin) {
+ if($plugin['@attributes']['type'] === 'collaborator-search') {
+ $pluginInfo = [
+ 'shareType' => $plugin['@attributes']['share-type'],
+ 'class' => $plugin['@value'],
+ ];
+ \OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo);
+ }
+ }
+ }
}
/**
diff --git a/lib/public/Collaboration/Collaborators/ISearch.php b/lib/public/Collaboration/Collaborators/ISearch.php
new file mode 100644
index 00000000000..281893908ee
--- /dev/null
+++ b/lib/public/Collaboration/Collaborators/ISearch.php
@@ -0,0 +1,50 @@
+<?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
+ *
+ * @package OCP\Collaboration\Collaborators
+ * @since 13.0.0
+ */
+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);
+
+ /**
+ * @param array $pluginInfo with keys 'shareType' containing the name of a corresponding constant in \OCP\Share and
+ * 'class' with the class name of the plugin
+ * @since 13.0.0
+ */
+ public function registerPlugin(array $pluginInfo);
+}
diff --git a/lib/public/Collaboration/Collaborators/ISearchPlugin.php b/lib/public/Collaboration/Collaborators/ISearchPlugin.php
new file mode 100644
index 00000000000..bc6fa53799b
--- /dev/null
+++ b/lib/public/Collaboration/Collaborators/ISearchPlugin.php
@@ -0,0 +1,42 @@
+<?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
+ *
+ * @package OCP\Collaboration\Collaborators
+ * @since 13.0.0
+ */
+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..abea5df8598
--- /dev/null
+++ b/lib/public/Collaboration/Collaborators/ISearchResult.php
@@ -0,0 +1,73 @@
+<?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
+ *
+ * @package OCP\Collaboration\Collaborators
+ * @since 13.0.0
+ */
+interface ISearchResult {
+ /**
+ * @param SearchResultType $type
+ * @param array $matches
+ * @param array|null $exactMatches
+ * @since 13.0.0
+ */
+ public function addResultSet(SearchResultType $type, array $matches, array $exactMatches = null);
+
+ /**
+ * @param SearchResultType $type
+ * @param string $collaboratorId
+ * @return bool
+ * @since 13.0.0
+ */
+ public function hasResult(SearchResultType $type, $collaboratorId);
+
+ /**
+ * @param SearchResultType $type
+ * @since 13.0.0
+ */
+ public function unsetResult(SearchResultType $type);
+
+ /**
+ * @param SearchResultType $type
+ * @since 13.0.0
+ */
+ public function markExactIdMatch(SearchResultType $type);
+
+ /**
+ * @param SearchResultType $type
+ * @return bool
+ * @since 13.0.0
+ */
+ public function hasExactIdMatch(SearchResultType $type);
+
+ /**
+ * @return array
+ * @since 13.0.0
+ */
+ public function asArray();
+}
diff --git a/lib/public/Collaboration/Collaborators/SearchResultType.php b/lib/public/Collaboration/Collaborators/SearchResultType.php
new file mode 100644
index 00000000000..e4ad888d6e4
--- /dev/null
+++ b/lib/public/Collaboration/Collaborators/SearchResultType.php
@@ -0,0 +1,73 @@
+<?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;
+
+/**
+ * Class SearchResultType
+ *
+ * @package OCP\Collaboration\Collaborators
+ * @since 13.0.0
+ */
+class SearchResultType {
+ /** @var string */
+ protected $label;
+
+ /**
+ * SearchResultType constructor.
+ *
+ * @param string $label
+ * @since 13.0.0
+ */
+ public function __construct($label) {
+ $this->label = $this->getValidatedType($label);
+ }
+
+ /**
+ * @return string
+ * @since 13.0.0
+ */
+ public function getLabel() {
+ return $this->label;
+ }
+
+ /**
+ * @param $type
+ * @return string
+ * @throws \InvalidArgumentException
+ * @since 13.0.0
+ */
+ protected function getValidatedType($type) {
+ $type = trim(strval($type));
+
+ if($type === '') {
+ throw new \InvalidArgumentException('Type must not be empty');
+ }
+
+ if($type === 'exact') {
+ throw new \InvalidArgumentException('Provided type is a reserved word');
+ }
+
+ return $type;
+ }
+}