Merge pull request #58603 from pymnh/feat/extend-group-search-to-teams

feat(UserPlugin): Include teams in group search
This commit is contained in:
Andy Scherzinger 2026-03-09 15:17:41 +01:00 committed by GitHub
commit 56eaf1dbcc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 54 additions and 13 deletions

View file

@ -4,11 +4,11 @@
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Core\Controller;
use Exception;
use OC\Contacts\ContactsMenu\Manager;
use OC\Teams\TeamManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
@ -38,12 +38,10 @@ class ContactsMenuController extends Controller {
public function index(?string $filter = null, ?string $teamId = null): array {
$entries = $this->manager->getEntries($this->userSession->getUser(), $filter);
if ($teamId !== null) {
/** @var TeamManager */
$teamManager = $this->teamManager;
$memberIds = $teamManager->getMembersOfTeam($teamId, $this->userSession->getUser()->getUID());
$memberIds = $this->teamManager->getMembersOfTeam($teamId, $this->userSession->getUser()->getUID());
$entries['contacts'] = array_filter(
$entries['contacts'],
fn (IEntry $entry) => in_array($entry->getProperty('UID'), $memberIds, true)
fn (IEntry $entry) => array_key_exists($entry->getProperty('UID'), $memberIds)
);
}
return $entries;

View file

@ -18,6 +18,7 @@ use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\Share\IShare;
use OCP\Teams\ITeamManager;
use OCP\UserStatus\IManager as IUserStatusManager;
use OCP\UserStatus\IUserStatus;
@ -26,6 +27,7 @@ readonly class UserPlugin implements ISearchPlugin {
private IAppConfig $appConfig,
private IUserManager $userManager,
private IGroupManager $groupManager,
private ITeamManager $teamManager,
private IUserSession $userSession,
private IUserStatusManager $userStatusManager,
private IDBConnection $connection,
@ -41,6 +43,7 @@ readonly class UserPlugin implements ISearchPlugin {
/** @var array<string, array{0: 'wide'|'exact', 1: IUser}> $users */
$users = [];
$lowerSearch = mb_strtolower($search);
$shareeEnumeration = $this->appConfig->getValueString('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
if ($shareeEnumeration) {
@ -67,6 +70,23 @@ readonly class UserPlugin implements ISearchPlugin {
}
}
}
// If teams are enabled, also search in them
if ($this->teamManager->hasTeamSupport()) {
$teams = $this->teamManager->getTeamsForUser($currentUser->getUID());
foreach ($teams as $team) {
$usersInTeam = $this->teamManager->getMembersOfTeam($team->getId(), $currentUser->getUID());
foreach ($usersInTeam as $userId => $displayName) {
if (!str_contains(mb_strtolower($userId), $lowerSearch) && !str_contains(mb_strtolower($displayName), $lowerSearch)) {
continue;
}
$user = $this->userManager->get($userId);
if ($user !== null && $user->isEnabled()) {
$users[$userId] = ['wide', $user];
}
}
}
}
}
if ($shareeEnumerationRestrictToPhone) {
@ -87,8 +107,6 @@ readonly class UserPlugin implements ISearchPlugin {
$shareeEnumerationFullMatchEmail = $this->appConfig->getValueString('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
$shareeEnumerationFullMatchIgnoreSecondDisplayName = $this->appConfig->getValueString('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
$lowerSearch = mb_strtolower($search);
// Re-use the results from earlier if possible
$usersByDisplayName ??= $this->userManager->searchDisplayName($search, $limit, $offset);
foreach ($usersByDisplayName as $user) {

View file

@ -133,7 +133,9 @@ class TeamManager implements ITeamManager {
}
/**
* @return string[]
* Returns a mapping of user id to display name for all members of a given team.
*
* @return array<string, string> userId => displayName
*/
public function getMembersOfTeam(string $teamId, string $userId): array {
$team = $this->getTeam($teamId, $userId);
@ -141,7 +143,11 @@ class TeamManager implements ITeamManager {
return [];
}
$members = $team->getInheritedMembers();
return array_map(fn ($member) => $member->getUserId(), $members);
$result = [];
foreach ($members as $member) {
$result[$member->getUserId()] = $member->getDisplayName();
}
return $result;
}
/**

View file

@ -56,4 +56,23 @@ interface ITeamManager {
* @since 33.0.0
*/
public function getTeamsForUser(string $userId): array;
/**
* Returns a mapping of user ID to display name for all members of a given team.
*
* @param string $teamId ID of the team whose members are being queried
* @param string $userId ID of the user from whose point of view the members are being queried
*
* @return array<string, string> userId => displayName
* @since 34.0.0
*/
public function getMembersOfTeam(string $teamId, string $userId): array;
/**
* Returns whether the Teams backend is available
*
* @return bool
* @since 34.0.0
*/
public function hasTeamSupport(): bool;
}

View file

@ -9,18 +9,18 @@ namespace Tests\Controller;
use OC\Contacts\ContactsMenu\Manager;
use OC\Core\Controller\ContactsMenuController;
use OC\Teams\TeamManager;
use OCP\Contacts\ContactsMenu\IEntry;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Teams\ITeamManager;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class ContactsMenuControllerTest extends TestCase {
private IUserSession&MockObject $userSession;
private Manager&MockObject $contactsManager;
private TeamManager&MockObject $teamManager;
private ITeamManager&MockObject $teamManager;
private ContactsMenuController $controller;
@ -30,7 +30,7 @@ class ContactsMenuControllerTest extends TestCase {
$request = $this->createMock(IRequest::class);
$this->userSession = $this->createMock(IUserSession::class);
$this->contactsManager = $this->createMock(Manager::class);
$this->teamManager = $this->createMock(TeamManager::class);
$this->teamManager = $this->createMock(ITeamManager::class);
$this->controller = new ContactsMenuController(
$request,
@ -86,7 +86,7 @@ class ContactsMenuControllerTest extends TestCase {
$this->teamManager->expects($this->once())
->method('getMembersOfTeam')
->with('team-id', 'current-user')
->willReturn(['member1', 'member3']);
->willReturn(['member1' => 'Member 1', 'member3' => 'Member 3']);
$response = $this->controller->index(teamId: 'team-id');