From 1e783492e8a2b1e840ab20a9412f2a31b2a2bbd8 Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Mon, 13 Jun 2022 18:48:49 +0200 Subject: [PATCH] Optimize retrieving display name when searching for users in a group This is recurrent scenario that we are searching for users and then for each users we fetch the displayName. This is inefficient, so instead try to do one query to fetch everything (e.g. Database backend) or use the already existing DisplayNameCache helper. Signed-off-by: Carl Schwan --- apps/user_ldap/lib/Group_LDAP.php | 21 ++++++---- apps/user_ldap/lib/Group_Proxy.php | 4 ++ build/psalm-baseline.xml | 41 +------------------- lib/private/Group/Database.php | 55 +++++++++++++++++++++++++++ lib/private/Group/Group.php | 17 +++------ lib/private/User/LazyUser.php | 18 ++++++++- lib/private/User/Manager.php | 29 ++++++-------- lib/public/Group/Backend/ABackend.php | 11 ++++++ lib/public/GroupInterface.php | 24 +++++++++++- lib/public/IGroup.php | 2 +- tests/lib/Group/GroupTest.php | 24 ++++++------ tests/lib/Group/ManagerTest.php | 55 +++++---------------------- tests/lib/Util/Group/Dummy.php | 46 ++++++++++++++++++---- 13 files changed, 203 insertions(+), 144 deletions(-) diff --git a/apps/user_ldap/lib/Group_LDAP.php b/apps/user_ldap/lib/Group_LDAP.php index 8fcb10cb850d0..b27bd1f8b6750 100644 --- a/apps/user_ldap/lib/Group_LDAP.php +++ b/apps/user_ldap/lib/Group_LDAP.php @@ -47,12 +47,13 @@ use OC; use OC\Cache\CappedMemoryCache; use OC\ServerNotAvailableException; +use OCP\Group\Backend\ABackend; use OCP\Group\Backend\IGetDisplayNameBackend; use OCP\Group\Backend\IDeleteGroupBackend; use OCP\GroupInterface; use Psr\Log\LoggerInterface; -class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, IGetDisplayNameBackend, IDeleteGroupBackend { +class Group_LDAP extends ABackend implements GroupInterface, IGroupLDAP, IGetDisplayNameBackend, IDeleteGroupBackend { protected $enabled = false; /** @var CappedMemoryCache $cachedGroupMembers array of users with gid as key */ @@ -61,10 +62,9 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I protected CappedMemoryCache $cachedGroupsByMember; /** @var CappedMemoryCache $cachedNestedGroups array of groups with gid (DN) as key */ protected CappedMemoryCache $cachedNestedGroups; - /** @var GroupPluginManager */ - protected $groupPluginManager; - /** @var LoggerInterface */ - protected $logger; + protected GroupPluginManager $groupPluginManager; + protected LoggerInterface $logger; + protected Access $access; /** * @var string $ldapGroupMemberAssocAttr contains the LDAP setting (in lower case) with the same name @@ -72,7 +72,7 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I protected $ldapGroupMemberAssocAttr; public function __construct(Access $access, GroupPluginManager $groupPluginManager) { - parent::__construct($access); + $this->access = $access; $filter = $this->access->connection->ldapGroupFilter; $gAssoc = $this->access->connection->ldapGroupMemberAssocAttr; if (!empty($filter) && !empty($gAssoc)) { @@ -1203,7 +1203,7 @@ protected function filterValidGroups(array $listOfGroups): array { * 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); @@ -1370,4 +1370,11 @@ public function getDisplayName(string $gid): string { return ''; } + + public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array { + if (!$this->enabled) { + return []; + } + return parent::searchInGroup($gid, $search, $limit, $offset); + } } diff --git a/apps/user_ldap/lib/Group_Proxy.php b/apps/user_ldap/lib/Group_Proxy.php index ea2fcce679c4a..1377c84656484 100644 --- a/apps/user_ldap/lib/Group_Proxy.php +++ b/apps/user_ldap/lib/Group_Proxy.php @@ -306,4 +306,8 @@ public function getDisplayName(string $gid): string { 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/build/psalm-baseline.xml b/build/psalm-baseline.xml index 1e27a2496c712..9d4c05305a49f 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -136,16 +136,6 @@ - - '\OCA\DAV\CalDAV\CalDavBackend::createCachedCalendarObject' - '\OCA\DAV\CalDAV\CalDavBackend::createSubscription' - '\OCA\DAV\CalDAV\CalDavBackend::deleteCachedCalendarObject' - '\OCA\DAV\CalDAV\CalDavBackend::deleteSubscription' - '\OCA\DAV\CalDAV\CalDavBackend::publishCalendar' - '\OCA\DAV\CalDAV\CalDavBackend::updateCachedCalendarObject' - '\OCA\DAV\CalDAV\CalDavBackend::updateShares' - '\OCA\DAV\CalDAV\CalDavBackend::updateSubscription' - array array @@ -161,16 +151,6 @@ (int)$calendarId - - dispatch - dispatch - dispatch - dispatch - dispatch - dispatch - dispatch - dispatch - Uri\split($principalUri) Uri\split($row['principaluri']) @@ -341,9 +321,6 @@ - - $id - $this->getKey() $this->getKey() @@ -358,16 +335,6 @@ false - - '\OCA\DAV\CardDAV\CardDavBackend::createCard' - '\OCA\DAV\CardDAV\CardDavBackend::deleteCard' - '\OCA\DAV\CardDAV\CardDavBackend::updateCard' - - - dispatch - dispatch - dispatch - $addressBooks[$row['id']][$readOnlyPropertyName] === 0 @@ -889,7 +856,6 @@ get_class($res) === 'OpenSSLAsymmetricKey' - is_object($res) @@ -3366,16 +3332,13 @@ $result $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file, false, $isRename) - + $newUnencryptedSize $result - $stat $this->storage->file_get_contents($path) $this->storage->filesize($path) - $this->storage->getLocalFile($path) - - array + bool int string diff --git a/lib/private/Group/Database.php b/lib/private/Group/Database.php index 7b7ee41def9b2..f445d1ad329c2 100644 --- a/lib/private/Group/Database.php +++ b/lib/private/Group/Database.php @@ -43,6 +43,9 @@ use OCP\Group\Backend\ISetDisplayNameBackend; use OCP\Group\Backend\INamedBackend; use OCP\IDBConnection; +use OCP\IUserManager; +use OC\User\LazyUser; +use OC\User\DisplayNameCache; /** * Class for group management in a SQL Database (e.g. MySQL, SQLite) @@ -377,6 +380,58 @@ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { return $users; } + public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array { + $this->fixDI(); + + $query = $this->dbConn->getQueryBuilder(); + if ($search !== '') { + $query->select('g.uid', 'u.displayname'); + } else { + $query->select('g.uid'); + } + + $query->from('group_user', 'g') + ->where($query->expr()->eq('gid', $query->createNamedParameter($gid))) + ->orderBy('g.uid', 'ASC'); + + 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'))) + ) + // sqlite doesn't like re-using a single named parameter here + ->andWhere( + $query->expr()->orX( + $query->expr()->ilike('g.uid', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')), + $query->expr()->ilike('u.displayname', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')), + $query->expr()->ilike('p.configvalue', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')) + ) + ) + ->orderBy('u.uid_lower', 'ASC'); + } + + if ($limit !== -1) { + $query->setMaxResults($limit); + } + if ($offset !== 0) { + $query->setFirstResult($offset); + } + + $result = $query->executeQuery(); + + $users = []; + $userManager = \OCP\Server::get(IUserManager::class); + $displayNameCache = \OCP\Server::get(DisplayNameCache::class); + while ($row = $result->fetch()) { + $users[$row['uid']] = new LazyUser($row['uid'], $displayNameCache, $userManager, $row['displayname'] ?? null); + } + $result->closeCursor(); + + return $users; + } + /** * get the number of all users matching the search string in a group * @param string $gid diff --git a/lib/private/Group/Group.php b/lib/private/Group/Group.php index 2ef4d2ee23fd3..dd792ce575828 100644 --- a/lib/private/Group/Group.php +++ b/lib/private/Group/Group.php @@ -238,18 +238,13 @@ public function removeUser($user) { } /** - * 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); + $users = array_merge($users, $backend->searchInGroup($this->gid, $search, $limit ?? -1, $offset ?? 0)); if (!is_null($limit) and $limit <= 0) { return $users; } @@ -305,12 +300,12 @@ public function countDisabled() { * @param int $limit * @param int $offset * @return \OC\User\User[] + * @deprecated 25.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); + $users = array_merge($users, $backend->searchInGroup($this->gid, $search, $limit ?? -1, $offset ?? 0)); if (!is_null($limit) and $limit <= 0) { return array_values($users); } diff --git a/lib/private/User/LazyUser.php b/lib/private/User/LazyUser.php index 8e93d6481ab20..089e6f4330f37 100644 --- a/lib/private/User/LazyUser.php +++ b/lib/private/User/LazyUser.php @@ -31,17 +31,27 @@ class LazyUser implements IUser { private ?IUser $user = null; private DisplayNameCache $displayNameCache; private string $uid; + private ?string $displayName; private IUserManager $userManager; + private ?UserInterface $backend; - public function __construct(string $uid, DisplayNameCache $displayNameCache, IUserManager $userManager) { + public function __construct(string $uid, DisplayNameCache $displayNameCache, IUserManager $userManager, ?string $displayName = null, ?UserInterface $backend = null) { $this->displayNameCache = $displayNameCache; $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; @@ -53,6 +63,10 @@ public function getUID() { } public function getDisplayName() { + if ($this->displayName) { + return $this->displayName; + } + return $this->displayNameCache->getDisplayName($this->uid); } diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index a6f5658532536..6ecfd5b01fc46 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -193,7 +193,7 @@ public function get($uid) { * @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); } @@ -284,58 +284,53 @@ public function checkPasswordNoLogging($loginName, $password) { } /** - * 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 25.0.0, use searchDisplayName instead */ public function search($pattern, $limit = null, $offset = null) { $users = []; + $displayNameCache = \OCP\Server::get(DisplayNameCache::class); foreach ($this->backends as $backend) { $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, $displayNameCache, $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 = []; + $displayNameCache = \OCP\Server::get(DisplayNameCache::class); foreach ($this->backends as $backend) { $backendUsers = $backend->getDisplayNames($pattern, $limit, $offset); if (is_array($backendUsers)) { foreach ($backendUsers as $uid => $displayName) { - $users[] = $this->getUserObject($uid, $backend); + $users[] = new LazyUser($uid, $displayNameCache, $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/ABackend.php b/lib/public/Group/Backend/ABackend.php index 2d611c27b4f38..f103ea5419bd8 100644 --- a/lib/public/Group/Backend/ABackend.php +++ b/lib/public/Group/Backend/ABackend.php @@ -65,4 +65,15 @@ public function implementsActions($actions): bool { return (bool)($actions & $implements); } + + public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array { + // Default implementation for compatibility reasons + $displayNameCache = Server::get(DisplayNameCache::class); + $userManager = Server::get(IUserManager::class); + $users = []; + foreach ($this->usersInGroup($gid, $search, $limit, $offset) as $userId) { + $users[$userId] = new LazyUser($userId, $displayNameCache, $userManager); + } + return $users; + } } diff --git a/lib/public/GroupInterface.php b/lib/public/GroupInterface.php index f1765afed961e..597612840181e 100644 --- a/lib/public/GroupInterface.php +++ b/lib/public/GroupInterface.php @@ -107,13 +107,35 @@ public function getGroups($search = '', $limit = -1, $offset = 0); 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 * @since 4.5.0 + * @deprecated 25.0.0 Use searchInGroup instead, for performance reasons */ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0); + + /** + * @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. + * + * + * $users = $groupBackend->searchInGroup('admin', 'John', 10, 0); + * + * + * @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 IUser[] + * @since 25.0.0 + */ + public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array; } diff --git a/lib/public/IGroup.php b/lib/public/IGroup.php index ba13359c472d9..ec26cc55b69ac 100644 --- a/lib/public/IGroup.php +++ b/lib/public/IGroup.php @@ -99,7 +99,7 @@ public function removeUser($user); * @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 b85496620b83b..300ebe2b0fa5a 100644 --- a/tests/lib/Group/GroupTest.php +++ b/tests/lib/Group/GroupTest.php @@ -304,9 +304,9 @@ public function testSearchUsers() { $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'); @@ -326,13 +326,13 @@ public function testSearchUsersMultipleBackends() { $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'); @@ -349,9 +349,9 @@ public function testSearchUsersLimitAndOffset() { $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); @@ -371,13 +371,13 @@ public function testSearchUsersMultipleBackendsLimitAndOffset() { $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); diff --git a/tests/lib/Group/ManagerTest.php b/tests/lib/Group/ManagerTest.php index d689fd4eb7a49..5946144516350 100644 --- a/tests/lib/Group/ManagerTest.php +++ b/tests/lib/Group/ManagerTest.php @@ -24,6 +24,7 @@ namespace Test\Group; use OC\Group\Database; +use OC\User\User; use OC\User\Manager; use OCP\GroupInterface; use OCP\IUser; @@ -87,6 +88,7 @@ private function getTestBackend($implementedActions = null) { 'createGroup', 'addToGroup', 'removeFromGroup', + 'searchInGroup', ]) ->getMock(); $backend->expects($this->any()) @@ -720,7 +722,7 @@ public function testDisplayNamesInGroupWithOneUserBackendWithLimitAndOffsetSpeci 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)) @@ -729,22 +731,9 @@ public function testDisplayNamesInGroupWithOneUserBackendAndSearchEmpty() { ->willReturn(true); $backend->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('testgroup', '', -1, 0) - ->willReturn(['user2', '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; - } - }); + ->willReturn([$this->getTestUser('user2'), $this->getTestUser('user33')]); $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger); $manager->addBackend($backend); @@ -768,22 +757,9 @@ public function testDisplayNamesInGroupWithOneUserBackendAndSearchEmptyAndLimitS ->willReturn(true); $backend->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('testgroup', '', 1, 0) - ->willReturn(['user2']); - - $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; - } - }); + ->willReturn([new User('user2', null, $this->dispatcher)]); $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger); $manager->addBackend($backend); @@ -807,22 +783,9 @@ public function testDisplayNamesInGroupWithOneUserBackendAndSearchEmptyAndLimitA ->willReturn(true); $backend->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('testgroup', '', 1, 1) - ->willReturn(['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; - } - }); + ->willReturn([$this->getTestUser('user33')]); $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger); $manager->addBackend($backend); diff --git a/tests/lib/Util/Group/Dummy.php b/tests/lib/Util/Group/Dummy.php index 3735a5e1167e8..2d86e45cfc164 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 @@ public function createGroup($gid) { * * 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 @@ public function inGroup($uid, $gid) { * * 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 @@ public function addToGroup($uid, $gid) { * * 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 @@ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { } } + 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[] = new DummyUser($user, ''); + } + } + return $result; + } else { + return []; + } + } + /** * get the number of all users in a group * @param string $gid @@ -200,7 +225,7 @@ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { * @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 @@ public function countUsersInGroup($gid, $search = '', $limit = -1, $offset = 0) } return $count; } + return 0; + } + + public function groupExists($gid) { + return isset($this->groups[$gid]); } }