summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCôme Chilliet <91878298+come-nc@users.noreply.github.com>2023-05-02 16:42:09 +0200
committerGitHub <noreply@github.com>2023-05-02 16:42:09 +0200
commitf7632f2fc4660ebc74240a9dd738b6e03a711eaa (patch)
treedcdc484ad3f69f9e3eb785192048c06bb09e6e98
parentac56be10fbd02c09bdeebcaa69d57aaae6613b9f (diff)
parent10296ba7e50ede8eee0ed18f477eb3e67bfd5878 (diff)
downloadnextcloud-server-f7632f2fc4660ebc74240a9dd738b6e03a711eaa.tar.gz
nextcloud-server-f7632f2fc4660ebc74240a9dd738b6e03a711eaa.zip
Merge pull request #32866 from nextcloud/performance/searchInGroup-displayname-cache
Optimize retrieving display name when searching for users in a group
-rw-r--r--apps/user_ldap/lib/Access.php5
-rw-r--r--apps/user_ldap/lib/Group_LDAP.php15
-rw-r--r--apps/user_ldap/lib/Group_Proxy.php6
-rw-r--r--core/Command/User/ListCommand.php2
-rw-r--r--lib/composer/composer/autoload_classmap.php1
-rw-r--r--lib/composer/composer/autoload_static.php1
-rw-r--r--lib/private/Group/Backend.php2
-rw-r--r--lib/private/Group/Database.php37
-rw-r--r--lib/private/Group/Group.php45
-rw-r--r--lib/private/User/LazyUser.php18
-rw-r--r--lib/private/User/Manager.php27
-rw-r--r--lib/public/Group/Backend/ISearchableGroupBackend.php51
-rw-r--r--lib/public/GroupInterface.php6
-rw-r--r--lib/public/IGroup.php2
-rw-r--r--tests/lib/Group/GroupTest.php34
-rw-r--r--tests/lib/Group/ManagerTest.php61
-rw-r--r--tests/lib/Util/Group/Dummy.php46
17 files changed, 221 insertions, 138 deletions
diff --git a/apps/user_ldap/lib/Access.php b/apps/user_ldap/lib/Access.php
index 9a97a28c376..3f120caefe6 100644
--- a/apps/user_ldap/lib/Access.php
+++ b/apps/user_ldap/lib/Access.php
@@ -631,7 +631,7 @@ class Access extends LDAPUtility {
* gives back the user names as they are used ownClod internally
*
* @param array $ldapUsers as returned by fetchList()
- * @return array an array with the user names to use in Nextcloud
+ * @return array<int,string> an array with the user names to use in Nextcloud
*
* gives back the user names as they are used ownClod internally
* @throws \Exception
@@ -644,7 +644,7 @@ class Access extends LDAPUtility {
* gives back the group names as they are used ownClod internally
*
* @param array $ldapGroups as returned by fetchList()
- * @return array an array with the group names to use in Nextcloud
+ * @return array<int,string> an array with the group names to use in Nextcloud
*
* gives back the group names as they are used ownClod internally
* @throws \Exception
@@ -655,6 +655,7 @@ class Access extends LDAPUtility {
/**
* @param array[] $ldapObjects as returned by fetchList()
+ * @return array<int,string>
* @throws \Exception
*/
private function ldap2NextcloudNames(array $ldapObjects, bool $isUsers): array {
diff --git a/apps/user_ldap/lib/Group_LDAP.php b/apps/user_ldap/lib/Group_LDAP.php
index b32e031175f..84267171d37 100644
--- a/apps/user_ldap/lib/Group_LDAP.php
+++ b/apps/user_ldap/lib/Group_LDAP.php
@@ -45,11 +45,11 @@
namespace OCA\User_LDAP;
use Exception;
+use OC\ServerNotAvailableException;
use OCP\Cache\CappedMemoryCache;
use OCP\GroupInterface;
use OCP\Group\Backend\IDeleteGroupBackend;
use OCP\Group\Backend\IGetDisplayNameBackend;
-use OC\ServerNotAvailableException;
use Psr\Log\LoggerInterface;
class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, IGetDisplayNameBackend, IDeleteGroupBackend {
@@ -466,7 +466,7 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
}
/**
- * @return array A list of users that have the given group as gid number
+ * @return array<int,string> A list of users that have the given group as gid number
* @throws ServerNotAvailableException
*/
public function getUsersInGidNumber(
@@ -591,6 +591,7 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
/**
* @throws ServerNotAvailableException
+ * @return array<int,string>
*/
public function getUsersInPrimaryGroup(
string $groupDN,
@@ -840,7 +841,7 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
* @param string $search
* @param int $limit
* @param int $offset
- * @return array with user ids
+ * @return array<int,string> user ids
* @throws Exception
* @throws ServerNotAvailableException
*/
@@ -909,7 +910,11 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
if (empty($ldap_users)) {
break;
}
- $groupUsers[] = $this->access->dn2username($ldap_users[0]['dn'][0]);
+ $uid = $this->access->dn2username($ldap_users[0]['dn'][0]);
+ if (!$uid) {
+ break;
+ }
+ $groupUsers[] = $uid;
break;
default:
//we got DNs, check if we need to filter by search or we can give back all of them
@@ -1163,7 +1168,7 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
* Returns the supported actions as int to be
* compared with GroupInterface::CREATE_GROUP etc.
*/
- public function implementsActions($actions) {
+ public function implementsActions($actions): bool {
return (bool)((GroupInterface::COUNT_USERS |
GroupInterface::DELETE_GROUP |
$this->groupPluginManager->getImplementedActions()) & $actions);
diff --git a/apps/user_ldap/lib/Group_Proxy.php b/apps/user_ldap/lib/Group_Proxy.php
index c8c986318ec..5f8d0562fd9 100644
--- a/apps/user_ldap/lib/Group_Proxy.php
+++ b/apps/user_ldap/lib/Group_Proxy.php
@@ -171,7 +171,7 @@ class Group_Proxy extends Proxy implements \OCP\GroupInterface, IGroupLDAP, IGet
/**
* get a list of all users in a group
*
- * @return string[] with user ids
+ * @return array<int,string> user ids
*/
public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
$this->setup();
@@ -334,4 +334,8 @@ class Group_Proxy extends Proxy implements \OCP\GroupInterface, IGroupLDAP, IGet
public function getBackendName(): string {
return 'LDAP';
}
+
+ public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array {
+ return $this->handleRequest($gid, 'searchInGroup', [$gid, $search, $limit, $offset]);
+ }
}
diff --git a/core/Command/User/ListCommand.php b/core/Command/User/ListCommand.php
index c254a8a11cf..bf4bf7f030e 100644
--- a/core/Command/User/ListCommand.php
+++ b/core/Command/User/ListCommand.php
@@ -74,7 +74,7 @@ class ListCommand extends Base {
}
protected function execute(InputInterface $input, OutputInterface $output): int {
- $users = $this->userManager->search('', (int) $input->getOption('limit'), (int) $input->getOption('offset'));
+ $users = $this->userManager->searchDisplayName('', (int) $input->getOption('limit'), (int) $input->getOption('offset'));
$this->writeArrayInOutputFormat($input, $output, $this->formatUsers($users, (bool)$input->getOption('info')));
return 0;
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 90164534da1..83eef30eed1 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -412,6 +412,7 @@ return array(
'OCP\\Group\\Backend\\IIsAdminBackend' => $baseDir . '/lib/public/Group/Backend/IIsAdminBackend.php',
'OCP\\Group\\Backend\\INamedBackend' => $baseDir . '/lib/public/Group/Backend/INamedBackend.php',
'OCP\\Group\\Backend\\IRemoveFromGroupBackend' => $baseDir . '/lib/public/Group/Backend/IRemoveFromGroupBackend.php',
+ 'OCP\\Group\\Backend\\ISearchableGroupBackend' => $baseDir . '/lib/public/Group/Backend/ISearchableGroupBackend.php',
'OCP\\Group\\Backend\\ISetDisplayNameBackend' => $baseDir . '/lib/public/Group/Backend/ISetDisplayNameBackend.php',
'OCP\\Group\\Events\\BeforeGroupChangedEvent' => $baseDir . '/lib/public/Group/Events/BeforeGroupChangedEvent.php',
'OCP\\Group\\Events\\BeforeGroupCreatedEvent' => $baseDir . '/lib/public/Group/Events/BeforeGroupCreatedEvent.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 43cdde51734..1f97b5518f9 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -445,6 +445,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Group\\Backend\\IIsAdminBackend' => __DIR__ . '/../../..' . '/lib/public/Group/Backend/IIsAdminBackend.php',
'OCP\\Group\\Backend\\INamedBackend' => __DIR__ . '/../../..' . '/lib/public/Group/Backend/INamedBackend.php',
'OCP\\Group\\Backend\\IRemoveFromGroupBackend' => __DIR__ . '/../../..' . '/lib/public/Group/Backend/IRemoveFromGroupBackend.php',
+ 'OCP\\Group\\Backend\\ISearchableGroupBackend' => __DIR__ . '/../../..' . '/lib/public/Group/Backend/ISearchableGroupBackend.php',
'OCP\\Group\\Backend\\ISetDisplayNameBackend' => __DIR__ . '/../../..' . '/lib/public/Group/Backend/ISetDisplayNameBackend.php',
'OCP\\Group\\Events\\BeforeGroupChangedEvent' => __DIR__ . '/../../..' . '/lib/public/Group/Events/BeforeGroupChangedEvent.php',
'OCP\\Group\\Events\\BeforeGroupCreatedEvent' => __DIR__ . '/../../..' . '/lib/public/Group/Events/BeforeGroupCreatedEvent.php',
diff --git a/lib/private/Group/Backend.php b/lib/private/Group/Backend.php
index 037cdacf445..1e3e5ef1164 100644
--- a/lib/private/Group/Backend.php
+++ b/lib/private/Group/Backend.php
@@ -126,7 +126,7 @@ abstract class Backend implements \OCP\GroupInterface {
* @param string $search
* @param int $limit
* @param int $offset
- * @return array an array of user ids
+ * @return array<int,string> an array of user ids
*/
public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
return [];
diff --git a/lib/private/Group/Database.php b/lib/private/Group/Database.php
index 5f17477db77..569cfa5007f 100644
--- a/lib/private/Group/Database.php
+++ b/lib/private/Group/Database.php
@@ -40,9 +40,12 @@ use OCP\Group\Backend\IDeleteGroupBackend;
use OCP\Group\Backend\IGetDisplayNameBackend;
use OCP\Group\Backend\IGroupDetailsBackend;
use OCP\Group\Backend\IRemoveFromGroupBackend;
+use OCP\Group\Backend\ISearchableGroupBackend;
use OCP\Group\Backend\ISetDisplayNameBackend;
use OCP\Group\Backend\INamedBackend;
use OCP\IDBConnection;
+use OCP\IUserManager;
+use OC\User\LazyUser;
/**
* Class for group management in a SQL Database (e.g. MySQL, SQLite)
@@ -57,6 +60,7 @@ class Database extends ABackend implements
IGroupDetailsBackend,
IRemoveFromGroupBackend,
ISetDisplayNameBackend,
+ ISearchableGroupBackend,
INamedBackend {
/** @var string[] */
private $groupCache = [];
@@ -324,29 +328,35 @@ class Database extends ABackend implements
}
/**
- * get a list of all users in a group
+ * Get a list of all users in a group
* @param string $gid
* @param string $search
* @param int $limit
* @param int $offset
- * @return array an array of user ids
+ * @return array<int,string> an array of user ids
*/
- public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
+ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0): array {
+ return array_values(array_map(fn ($user) => $user->getUid(), $this->searchInGroup($gid, $search, $limit, $offset)));
+ }
+
+ public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array {
$this->fixDI();
$query = $this->dbConn->getQueryBuilder();
- $query->select('g.uid')
- ->from('group_user', 'g')
+ $query->select('g.uid', 'u.displayname');
+
+ $query->from('group_user', 'g')
->where($query->expr()->eq('gid', $query->createNamedParameter($gid)))
->orderBy('g.uid', 'ASC');
+ $query->leftJoin('g', 'users', 'u', $query->expr()->eq('g.uid', 'u.uid'));
+
if ($search !== '') {
- $query->leftJoin('g', 'users', 'u', $query->expr()->eq('g.uid', 'u.uid'))
- ->leftJoin('u', 'preferences', 'p', $query->expr()->andX(
- $query->expr()->eq('p.userid', 'u.uid'),
- $query->expr()->eq('p.appid', $query->expr()->literal('settings')),
- $query->expr()->eq('p.configkey', $query->expr()->literal('email')))
- )
+ $query->leftJoin('u', 'preferences', 'p', $query->expr()->andX(
+ $query->expr()->eq('p.userid', 'u.uid'),
+ $query->expr()->eq('p.appid', $query->expr()->literal('settings')),
+ $query->expr()->eq('p.configkey', $query->expr()->literal('email'))
+ ))
// sqlite doesn't like re-using a single named parameter here
->andWhere(
$query->expr()->orX(
@@ -365,11 +375,12 @@ class Database extends ABackend implements
$query->setFirstResult($offset);
}
- $result = $query->execute();
+ $result = $query->executeQuery();
$users = [];
+ $userManager = \OCP\Server::get(IUserManager::class);
while ($row = $result->fetch()) {
- $users[] = $row['uid'];
+ $users[$row['uid']] = new LazyUser($row['uid'], $userManager, $row['displayname'] ?? null);
}
$result->closeCursor();
diff --git a/lib/private/Group/Group.php b/lib/private/Group/Group.php
index cca179bfe19..efc21ad7c0d 100644
--- a/lib/private/Group/Group.php
+++ b/lib/private/Group/Group.php
@@ -33,14 +33,16 @@
namespace OC\Group;
use OC\Hooks\PublicEmitter;
+use OC\User\LazyUser;
+use OCP\GroupInterface;
use OCP\Group\Backend\ICountDisabledInGroup;
use OCP\Group\Backend\IGetDisplayNameBackend;
use OCP\Group\Backend\IHideFromCollaborationBackend;
use OCP\Group\Backend\INamedBackend;
+use OCP\Group\Backend\ISearchableGroupBackend;
use OCP\Group\Backend\ISetDisplayNameBackend;
use OCP\Group\Events\BeforeGroupChangedEvent;
use OCP\Group\Events\GroupChangedEvent;
-use OCP\GroupInterface;
use OCP\IGroup;
use OCP\IUser;
use OCP\IUserManager;
@@ -242,18 +244,23 @@ class Group implements IGroup {
}
/**
- * search for users in the group by userid
- *
- * @param string $search
- * @param int $limit
- * @param int $offset
- * @return \OC\User\User[]
+ * Search for users in the group by userid or display name
+ * @return IUser[]
*/
- public function searchUsers($search, $limit = null, $offset = null) {
+ public function searchUsers(string $search, ?int $limit = null, ?int $offset = null): array {
$users = [];
foreach ($this->backends as $backend) {
- $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset);
- $users += $this->getVerifiedUsers($userIds);
+ if ($backend instanceof ISearchableGroupBackend) {
+ $users += $backend->searchInGroup($this->gid, $search, $limit ?? -1, $offset ?? 0);
+ } else {
+ $userIds = $backend->usersInGroup($this->gid, $search, $limit ?? -1, $offset ?? 0);
+ $userManager = \OCP\Server::get(IUserManager::class);
+ foreach ($userIds as $userId) {
+ if (!isset($users[$userId])) {
+ $users[$userId] = new LazyUser($userId, $userManager);
+ }
+ }
+ }
if (!is_null($limit) and $limit <= 0) {
return $users;
}
@@ -308,18 +315,11 @@ class Group implements IGroup {
* @param string $search
* @param int $limit
* @param int $offset
- * @return \OC\User\User[]
+ * @return IUser[]
+ * @deprecated 27.0.0 Use searchUsers instead (same implementation)
*/
public function searchDisplayName($search, $limit = null, $offset = null) {
- $users = [];
- foreach ($this->backends as $backend) {
- $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset);
- $users = $this->getVerifiedUsers($userIds);
- if (!is_null($limit) and $limit <= 0) {
- return array_values($users);
- }
- }
- return array_values($users);
+ return $this->searchUsers($search, $limit, $offset);
}
/**
@@ -375,10 +375,7 @@ class Group implements IGroup {
* @param string[] $userIds an array containing user IDs
* @return \OC\User\User[] an Array with the userId as Key and \OC\User\User as value
*/
- private function getVerifiedUsers($userIds) {
- if (!is_array($userIds)) {
- return [];
- }
+ private function getVerifiedUsers(array $userIds): array {
$users = [];
foreach ($userIds as $userId) {
$user = $this->userManager->get($userId);
diff --git a/lib/private/User/LazyUser.php b/lib/private/User/LazyUser.php
index 096578b8f37..5472cf6f2b4 100644
--- a/lib/private/User/LazyUser.php
+++ b/lib/private/User/LazyUser.php
@@ -30,16 +30,26 @@ use OCP\UserInterface;
class LazyUser implements IUser {
private ?IUser $user = null;
private string $uid;
+ private ?string $displayName;
private IUserManager $userManager;
+ private ?UserInterface $backend;
- public function __construct(string $uid, IUserManager $userManager) {
+ public function __construct(string $uid, IUserManager $userManager, ?string $displayName = null, ?UserInterface $backend = null) {
$this->uid = $uid;
$this->userManager = $userManager;
+ $this->displayName = $displayName;
+ $this->backend = $backend;
}
private function getUser(): IUser {
if ($this->user === null) {
- $this->user = $this->userManager->get($this->uid);
+ if ($this->backend) {
+ /** @var \OC\User\Manager $manager */
+ $manager = $this->userManager;
+ $this->user = $manager->getUserObject($this->uid, $this->backend);
+ } else {
+ $this->user = $this->userManager->get($this->uid);
+ }
}
/** @var IUser */
$user = $this->user;
@@ -51,6 +61,10 @@ class LazyUser implements IUser {
}
public function getDisplayName() {
+ if ($this->displayName) {
+ return $this->displayName;
+ }
+
return $this->userManager->getDisplayName($this->uid) ?? $this->uid;
}
diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php
index 859ebd2a604..60059d5badd 100644
--- a/lib/private/User/Manager.php
+++ b/lib/private/User/Manager.php
@@ -202,7 +202,7 @@ class Manager extends PublicEmitter implements IUserManager {
* @param bool $cacheUser If false the newly created user object will not be cached
* @return \OC\User\User
*/
- protected function getUserObject($uid, $backend, $cacheUser = true) {
+ public function getUserObject($uid, $backend, $cacheUser = true) {
if ($backend instanceof IGetRealUIDBackend) {
$uid = $backend->getRealUID($uid);
}
@@ -293,12 +293,13 @@ class Manager extends PublicEmitter implements IUserManager {
}
/**
- * search by user id
+ * Search by user id
*
* @param string $pattern
* @param int $limit
* @param int $offset
- * @return \OC\User\User[]
+ * @return IUser[]
+ * @deprecated since 27.0.0, use searchDisplayName instead
*/
public function search($pattern, $limit = null, $offset = null) {
$users = [];
@@ -306,28 +307,24 @@ class Manager extends PublicEmitter implements IUserManager {
$backendUsers = $backend->getUsers($pattern, $limit, $offset);
if (is_array($backendUsers)) {
foreach ($backendUsers as $uid) {
- $users[$uid] = $this->getUserObject($uid, $backend);
+ $users[$uid] = new LazyUser($uid, $this, null, $backend);
}
}
}
- uasort($users, function ($a, $b) {
- /**
- * @var \OC\User\User $a
- * @var \OC\User\User $b
- */
+ uasort($users, function (IUser $a, IUser $b) {
return strcasecmp($a->getUID(), $b->getUID());
});
return $users;
}
/**
- * search by displayName
+ * Search by displayName
*
* @param string $pattern
* @param int $limit
* @param int $offset
- * @return \OC\User\User[]
+ * @return IUser[]
*/
public function searchDisplayName($pattern, $limit = null, $offset = null) {
$users = [];
@@ -335,16 +332,12 @@ class Manager extends PublicEmitter implements IUserManager {
$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
if (is_array($backendUsers)) {
foreach ($backendUsers as $uid => $displayName) {
- $users[] = $this->getUserObject($uid, $backend);
+ $users[] = new LazyUser($uid, $this, $displayName, $backend);
}
}
}
- usort($users, function ($a, $b) {
- /**
- * @var \OC\User\User $a
- * @var \OC\User\User $b
- */
+ usort($users, function (IUser $a, IUser $b) {
return strcasecmp($a->getDisplayName(), $b->getDisplayName());
});
return $users;
diff --git a/lib/public/Group/Backend/ISearchableGroupBackend.php b/lib/public/Group/Backend/ISearchableGroupBackend.php
new file mode 100644
index 00000000000..20753822258
--- /dev/null
+++ b/lib/public/Group/Backend/ISearchableGroupBackend.php
@@ -0,0 +1,51 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu>
+ *
+ * @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\Group\Backend;
+
+use OCP\IUser;
+
+/**
+ * @since 27.0.0
+ */
+interface ISearchableGroupBackend {
+ /**
+ * @brief Get a list of users matching the given search parameters.
+ *
+ * Implementations of this method should return lazy evaluated user objects and
+ * preload if possible the display name.
+ *
+ * <code>
+ * $users = $groupBackend->searchInGroup('admin', 'John', 10, 0);
+ * </code>
+ *
+ * @param string $gid The group id of the user we want to search
+ * @param string $search The part of the display name or user id of the users we
+ * want to search. This can be empty to get all the users.
+ * @param int $limit The limit of results
+ * @param int $offset The offset of the results
+ * @return array<string,IUser> Users indexed by uid
+ * @since 27.0.0
+ */
+ public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array;
+}
diff --git a/lib/public/GroupInterface.php b/lib/public/GroupInterface.php
index b7c07136126..56863100c05 100644
--- a/lib/public/GroupInterface.php
+++ b/lib/public/GroupInterface.php
@@ -106,13 +106,15 @@ interface GroupInterface {
public function groupExists($gid);
/**
- * get a list of all users in a group
+ * @brief Get a list of user ids in a group matching the given search parameters.
+ *
* @param string $gid
* @param string $search
* @param int $limit
* @param int $offset
- * @return array an array of user ids
+ * @return array<int,string> an array of user ids
* @since 4.5.0
+ * @deprecated 27.0.0 Use searchInGroup instead, for performance reasons
*/
public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0);
}
diff --git a/lib/public/IGroup.php b/lib/public/IGroup.php
index ba13359c472..ec26cc55b69 100644
--- a/lib/public/IGroup.php
+++ b/lib/public/IGroup.php
@@ -99,7 +99,7 @@ interface IGroup {
* @return \OCP\IUser[]
* @since 8.0.0
*/
- public function searchUsers($search, $limit = null, $offset = null);
+ public function searchUsers(string $search, ?int $limit = null, ?int $offset = null): array;
/**
* returns the number of users matching the search string
diff --git a/tests/lib/Group/GroupTest.php b/tests/lib/Group/GroupTest.php
index 60f15a65732..ac648fd7b4b 100644
--- a/tests/lib/Group/GroupTest.php
+++ b/tests/lib/Group/GroupTest.php
@@ -303,14 +303,14 @@ class GroupTest extends \Test\TestCase {
$group = new \OC\Group\Group('group1', [$backend], $this->dispatcher, $userManager);
$backend->expects($this->once())
- ->method('usersInGroup')
+ ->method('searchInGroup')
->with('group1', '2')
- ->willReturn(['user2']);
+ ->willReturn(['user2' => new \OC\User\User('user2', null, $this->dispatcher)]);
$users = $group->searchUsers('2');
$this->assertEquals(1, count($users));
- $user2 = $users['user2'];
+ $user2 = reset($users);
$this->assertEquals('user2', $user2->getUID());
}
@@ -325,18 +325,18 @@ class GroupTest extends \Test\TestCase {
$group = new \OC\Group\Group('group1', [$backend1, $backend2], $this->dispatcher, $userManager);
$backend1->expects($this->once())
- ->method('usersInGroup')
+ ->method('searchInGroup')
->with('group1', '2')
- ->willReturn(['user2']);
+ ->willReturn(['user2' => new \OC\User\User('user2', null, $this->dispatcher)]);
$backend2->expects($this->once())
- ->method('usersInGroup')
+ ->method('searchInGroup')
->with('group1', '2')
- ->willReturn(['user2']);
+ ->willReturn(['user2' => new \OC\User\User('user2', null, $this->dispatcher)]);
$users = $group->searchUsers('2');
$this->assertEquals(1, count($users));
- $user2 = $users['user2'];
+ $user2 = reset($users);
$this->assertEquals('user2', $user2->getUID());
}
@@ -348,14 +348,14 @@ class GroupTest extends \Test\TestCase {
$group = new \OC\Group\Group('group1', [$backend], $this->dispatcher, $userManager);
$backend->expects($this->once())
- ->method('usersInGroup')
+ ->method('searchInGroup')
->with('group1', 'user', 1, 1)
- ->willReturn(['user2']);
+ ->willReturn(['user2' => new \OC\User\User('user2', null, $this->dispatcher)]);
$users = $group->searchUsers('user', 1, 1);
$this->assertEquals(1, count($users));
- $user2 = $users['user2'];
+ $user2 = reset($users);
$this->assertEquals('user2', $user2->getUID());
}
@@ -370,19 +370,19 @@ class GroupTest extends \Test\TestCase {
$group = new \OC\Group\Group('group1', [$backend1, $backend2], $this->dispatcher, $userManager);
$backend1->expects($this->once())
- ->method('usersInGroup')
+ ->method('searchInGroup')
->with('group1', 'user', 2, 1)
- ->willReturn(['user2']);
+ ->willReturn(['user2' => new \OC\User\User('user2', null, $this->dispatcher)]);
$backend2->expects($this->once())
- ->method('usersInGroup')
+ ->method('searchInGroup')
->with('group1', 'user', 2, 1)
- ->willReturn(['user1']);
+ ->willReturn(['user1' => new \OC\User\User('user1', null, $this->dispatcher)]);
$users = $group->searchUsers('user', 2, 1);
$this->assertEquals(2, count($users));
- $user2 = $users['user2'];
- $user1 = $users['user1'];
+ $user2 = reset($users);
+ $user1 = next($users);
$this->assertEquals('user2', $user2->getUID());
$this->assertEquals('user1', $user1->getUID());
}
diff --git a/tests/lib/Group/ManagerTest.php b/tests/lib/Group/ManagerTest.php
index 5e29f974706..710d3888d55 100644
--- a/tests/lib/Group/ManagerTest.php
+++ b/tests/lib/Group/ManagerTest.php
@@ -24,8 +24,10 @@
namespace Test\Group;
use OC\Group\Database;
+use OC\User\User;
use OC\User\Manager;
use OCP\GroupInterface;
+use OCP\Group\Backend\ISearchableGroupBackend;
use OCP\ICacheFactory;
use OCP\IUser;
use PHPUnit\Framework\MockObject\MockObject;
@@ -33,6 +35,9 @@ use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Test\TestCase;
+interface ISearchableGroupInterface extends ISearchableGroupBackend, GroupInterface {
+}
+
class ManagerTest extends TestCase {
/** @var Manager|MockObject */
protected $userManager;
@@ -78,7 +83,7 @@ class ManagerTest extends TestCase {
}
// need to declare it this way due to optional methods
// thanks to the implementsActions logic
- $backend = $this->getMockBuilder(GroupInterface::class)
+ $backend = $this->getMockBuilder(ISearchableGroupInterface::class)
->disableOriginalConstructor()
->setMethods([
'getGroupDetails',
@@ -91,6 +96,7 @@ class ManagerTest extends TestCase {
'createGroup',
'addToGroup',
'removeFromGroup',
+ 'searchInGroup',
])
->getMock();
$backend->expects($this->any())
@@ -724,7 +730,7 @@ class ManagerTest extends TestCase {
public function testDisplayNamesInGroupWithOneUserBackendAndSearchEmpty() {
/**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
+ * @var \PHPUnit\Framework\MockObject\MockObject|\OC\Group\Backend $backend
*/
$backend = $this->getTestBackend();
$backend->expects($this->exactly(1))
@@ -733,22 +739,11 @@ class ManagerTest extends TestCase {
->willReturn(true);
$backend->expects($this->once())
- ->method('usersInGroup')
+ ->method('searchInGroup')
->with('testgroup', '', -1, 0)
- ->willReturn(['user2', 'user33']);
+ ->willReturn(['user2' => $this->getTestUser('user2'), 'user33' => $this->getTestUser('user33')]);
- $this->userManager->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($uid) {
- switch ($uid) {
- case 'user1': return $this->getTestUser('user1');
- case 'user2': return $this->getTestUser('user2');
- case 'user3': return $this->getTestUser('user3');
- case 'user33': return $this->getTestUser('user33');
- default:
- return null;
- }
- });
+ $this->userManager->expects($this->never())->method('get');
$manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache);
$manager->addBackend($backend);
@@ -772,22 +767,11 @@ class ManagerTest extends TestCase {
->willReturn(true);
$backend->expects($this->once())
- ->method('usersInGroup')
+ ->method('searchInGroup')
->with('testgroup', '', 1, 0)
- ->willReturn(['user2']);
+ ->willReturn([new User('user2', null, $this->dispatcher)]);
- $this->userManager->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($uid) {
- switch ($uid) {
- case 'user1': return $this->getTestUser('user1');
- case 'user2': return $this->getTestUser('user2');
- case 'user3': return $this->getTestUser('user3');
- case 'user33': return $this->getTestUser('user33');
- default:
- return null;
- }
- });
+ $this->userManager->expects($this->never())->method('get');
$manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache);
$manager->addBackend($backend);
@@ -811,22 +795,11 @@ class ManagerTest extends TestCase {
->willReturn(true);
$backend->expects($this->once())
- ->method('usersInGroup')
+ ->method('searchInGroup')
->with('testgroup', '', 1, 1)
- ->willReturn(['user33']);
+ ->willReturn(['user33' => $this->getTestUser('user33')]);
- $this->userManager->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($uid) {
- switch ($uid) {
- case 'user1': return $this->getTestUser('user1');
- case 'user2': return $this->getTestUser('user2');
- case 'user3': return $this->getTestUser('user3');
- case 'user33': return $this->getTestUser('user33');
- default:
- return null;
- }
- });
+ $this->userManager->expects($this->never())->method('get');
$manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache);
$manager->addBackend($backend);
diff --git a/tests/lib/Util/Group/Dummy.php b/tests/lib/Util/Group/Dummy.php
index 3735a5e1167..a864c8ce9d9 100644
--- a/tests/lib/Util/Group/Dummy.php
+++ b/tests/lib/Util/Group/Dummy.php
@@ -29,12 +29,18 @@
namespace Test\Util\Group;
-use OC\Group\Backend;
+use Test\Util\User\Dummy as DummyUser;
+use OCP\Group\Backend\ABackend;
+use OCP\Group\Backend\IDeleteGroupBackend;
+use OCP\Group\Backend\IAddToGroupBackend;
+use OCP\Group\Backend\IRemoveFromGroupBackend;
+use OCP\Group\Backend\ICreateGroupBackend;
+use OCP\Group\Backend\ICountUsersBackend;
/**
- * dummy group backend, does not keep state, only for testing use
+ * Dummy group backend, does not keep state, only for testing use
*/
-class Dummy extends Backend {
+class Dummy extends ABackend implements ICreateGroupBackend, IDeleteGroupBackend, IAddToGroupBackend, IRemoveFromGroupBackend, ICountUsersBackend {
private $groups = [];
/**
* Try to create a new group
@@ -44,7 +50,7 @@ class Dummy extends Backend {
* Tries to create a new group. If the group name already exists, false will
* be returned.
*/
- public function createGroup($gid) {
+ public function createGroup(string $gid): bool {
if (!isset($this->groups[$gid])) {
$this->groups[$gid] = [];
return true;
@@ -60,7 +66,7 @@ class Dummy extends Backend {
*
* Deletes a group and removes it from the group_user-table
*/
- public function deleteGroup($gid) {
+ public function deleteGroup(string $gid): bool {
if (isset($this->groups[$gid])) {
unset($this->groups[$gid]);
return true;
@@ -93,7 +99,7 @@ class Dummy extends Backend {
*
* Adds a user to a group.
*/
- public function addToGroup($uid, $gid) {
+ public function addToGroup(string $uid, string $gid): bool {
if (isset($this->groups[$gid])) {
if (array_search($uid, $this->groups[$gid]) === false) {
$this->groups[$gid][] = $uid;
@@ -114,7 +120,7 @@ class Dummy extends Backend {
*
* removes the user from a group.
*/
- public function removeFromGroup($uid, $gid) {
+ public function removeFromGroup(string $uid, string $gid): bool {
if (isset($this->groups[$gid])) {
if (($index = array_search($uid, $this->groups[$gid])) !== false) {
unset($this->groups[$gid][$index]);
@@ -192,6 +198,25 @@ class Dummy extends Backend {
}
}
+ public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array {
+ if (isset($this->groups[$gid])) {
+ if (empty($search)) {
+ $length = $limit < 0 ? null : $limit;
+ $users = array_slice($this->groups[$gid], $offset, $length);
+ return array_map(fn ($user) => new DummyUser($user, ''));
+ }
+ $result = [];
+ foreach ($this->groups[$gid] as $user) {
+ if (stripos($user, $search) !== false) {
+ $result[$user] = new DummyUser($user, '');
+ }
+ }
+ return $result;
+ } else {
+ return [];
+ }
+ }
+
/**
* get the number of all users in a group
* @param string $gid
@@ -200,7 +225,7 @@ class Dummy extends Backend {
* @param int $offset
* @return int
*/
- public function countUsersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
+ public function countUsersInGroup(string $gid, string $search = ''): int {
if (isset($this->groups[$gid])) {
if (empty($search)) {
return count($this->groups[$gid]);
@@ -213,5 +238,10 @@ class Dummy extends Backend {
}
return $count;
}
+ return 0;
+ }
+
+ public function groupExists($gid) {
+ return isset($this->groups[$gid]);
}
}