Skip to content

Commit b1559b9

Browse files
kesselbbackportbot[bot]
authored andcommitted
fix(settings): show the groups display name for non-loaded groups
By default only 25 group objects are loaded. If a user is assigend to a group, that isn't loaded yet, we only show the gid instead of the displayname. Assisted-by: Copilot:gpt-5.4 Assisted-by: ClaudeCode:claude-sonnet-5 Signed-off-by: Daniel Kesselberg <mail@danielkesselberg.de>
1 parent 1b7576d commit b1559b9

11 files changed

Lines changed: 332 additions & 17 deletions

File tree

apps/provisioning_api/lib/Controller/AUserDataOCSController.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99
namespace OCA\Provisioning_API\Controller;
1010

11+
use OC\Group\DisplayNameCache as GroupDisplayNameCache;
1112
use OC\Group\Manager as GroupManager;
1213
use OC\User\Backend;
1314
use OC\User\NoUserException;
@@ -35,6 +36,7 @@
3536

3637
/**
3738
* @psalm-import-type Provisioning_APIUserDetails from ResponseDefinitions
39+
* @psalm-import-type Provisioning_APIUserDetailsGroupDisplayname from ResponseDefinitions
3840
* @psalm-import-type Provisioning_APIUserDetailsQuota from ResponseDefinitions
3941
*/
4042
abstract class AUserDataOCSController extends OCSController {
@@ -61,6 +63,7 @@ public function __construct(
6163
protected ISubAdmin $subAdminManager,
6264
protected IFactory $l10nFactory,
6365
protected IRootFolder $rootFolder,
66+
private GroupDisplayNameCache $groupDisplayNameCache,
6467
) {
6568
parent::__construct($appName, $request);
6669
}
@@ -252,6 +255,35 @@ protected function getUserSubAdminGroupsData(string $userId): array {
252255
return $groups;
253256
}
254257

258+
/**
259+
* A full group has id, name, usercount, disabled, canAdd and canRemove. Only
260+
* the displayname is cached; usercount/disabled are not cached. So this only
261+
* returns an {id, displayname} skeleton instead of the full group.
262+
*
263+
* @param array<string, Provisioning_APIUserDetails|array{id: string}> $userDetails
264+
* @return list<Provisioning_APIUserDetailsGroupDisplayname>
265+
*/
266+
protected function findGroupsWithDisplayname(array $userDetails): array {
267+
$groupIds = [];
268+
269+
foreach ($userDetails as $userDetail) {
270+
if (isset($userDetail['groups'])) {
271+
array_push($groupIds, ...array_values($userDetail['groups']));
272+
}
273+
if (isset($userDetail['subadmin'])) {
274+
array_push($groupIds, ...array_values($userDetail['subadmin']));
275+
}
276+
}
277+
278+
$groupIds = array_unique($groupIds);
279+
sort($groupIds);
280+
281+
return array_map(function ($groupId) {
282+
$displayname = $this->groupDisplayNameCache->getDisplayName($groupId) ?? $groupId;
283+
return ['id' => $groupId, 'displayname' => $displayname];
284+
}, $groupIds);
285+
}
286+
255287
/**
256288
* @param IUser $user
257289
* @return Provisioning_APIUserDetailsQuota

apps/provisioning_api/lib/Controller/GroupsController.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99
namespace OCA\Provisioning_API\Controller;
1010

11+
use OC\Group\DisplayNameCache as GroupDisplayNameCache;
1112
use OCA\Provisioning_API\ResponseDefinitions;
1213
use OCA\Settings\Settings\Admin\Sharing;
1314
use OCA\Settings\Settings\Admin\Users;
@@ -36,6 +37,7 @@
3637
/**
3738
* @psalm-import-type Provisioning_APIGroupDetails from ResponseDefinitions
3839
* @psalm-import-type Provisioning_APIUserDetails from ResponseDefinitions
40+
* @psalm-import-type Provisioning_APIUserDetailsGroupDisplayname from ResponseDefinitions
3941
*/
4042
class GroupsController extends AUserDataOCSController {
4143

@@ -51,6 +53,7 @@ public function __construct(
5153
IFactory $l10nFactory,
5254
IRootFolder $rootFolder,
5355
private LoggerInterface $logger,
56+
GroupDisplayNameCache $groupDisplayNameCache,
5457
) {
5558
parent::__construct($appName,
5659
$request,
@@ -62,6 +65,7 @@ public function __construct(
6265
$subAdminManager,
6366
$l10nFactory,
6467
$rootFolder,
68+
$groupDisplayNameCache,
6569
);
6670
}
6771

@@ -186,7 +190,7 @@ public function getGroupUsers(string $groupId): DataResponse {
186190
* @param int|null $limit Limit the amount of groups returned
187191
* @param int $offset Offset for searching for groups
188192
*
189-
* @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
193+
* @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>, groups: list<Provisioning_APIUserDetailsGroupDisplayname>}, array{}>
190194
* @throws OCSException
191195
*
192196
* 200: Group users details returned
@@ -229,7 +233,10 @@ public function getGroupUsersDetails(string $groupId, string $search = '', ?int
229233
// continue if a users ceased to exist.
230234
}
231235
}
232-
return new DataResponse(['users' => $usersDetails]);
236+
return new DataResponse([
237+
'users' => $usersDetails,
238+
'groups' => $this->findGroupsWithDisplayname($usersDetails),
239+
]);
233240
}
234241

235242
throw new OCSException('The requested group could not be found', OCSController::RESPOND_NOT_FOUND);

apps/provisioning_api/lib/Controller/UsersController.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
use InvalidArgumentException;
1414
use OC\Authentication\Token\RemoteWipe;
15+
use OC\Group\DisplayNameCache as GroupDisplayNameCache;
1516
use OC\Group\Group;
1617
use OC\KnownUser\KnownUserService;
1718
use OC\User\Backend;
@@ -57,6 +58,7 @@
5758
/**
5859
* @psalm-import-type Provisioning_APIGroupDetails from ResponseDefinitions
5960
* @psalm-import-type Provisioning_APIUserDetails from ResponseDefinitions
61+
* @psalm-import-type Provisioning_APIUserDetailsGroupDisplayname from ResponseDefinitions
6062
*/
6163
class UsersController extends AUserDataOCSController {
6264

@@ -83,6 +85,7 @@ public function __construct(
8385
private IPhoneNumberUtil $phoneNumberUtil,
8486
private IAppManager $appManager,
8587
private IAppConfig $appConfig,
88+
GroupDisplayNameCache $groupDisplayNameCache,
8689
) {
8790
parent::__construct(
8891
$appName,
@@ -95,6 +98,7 @@ public function __construct(
9598
$subAdminManager,
9699
$l10nFactory,
97100
$rootFolder,
101+
$groupDisplayNameCache,
98102
);
99103

100104
$this->l10n = $l10nFactory->get($appName);
@@ -148,7 +152,7 @@ public function getUsers(string $search = '', ?int $limit = null, int $offset =
148152
* @param string $search Text to search for
149153
* @param int|null $limit Limit the amount of groups returned
150154
* @param int $offset Offset for searching for groups
151-
* @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
155+
* @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>, groups: list<Provisioning_APIUserDetailsGroupDisplayname>}, array{}>
152156
*
153157
* 200: Users details returned
154158
*/
@@ -200,7 +204,8 @@ public function getUsersDetails(string $search = '', ?int $limit = null, int $of
200204
}
201205

202206
return new DataResponse([
203-
'users' => $usersDetails
207+
'users' => $usersDetails,
208+
'groups' => $this->findGroupsWithDisplayname($usersDetails),
204209
]);
205210
}
206211

apps/provisioning_api/lib/ResponseDefinitions.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
*
2121
* @psalm-type Provisioning_APIUserDetailsScope = 'v2-private'|'v2-local'|'v2-federated'|'v2-published'
2222
*
23+
* @psalm-type Provisioning_APIUserDetailsGroupDisplayname = array{
24+
* id: string,
25+
* displayname: string,
26+
* }
27+
*
2328
* @psalm-type Provisioning_APIUserDetails = array{
2429
* additional_mail: list<string>,
2530
* additional_mailScope?: list<Provisioning_APIUserDetailsScope>,

apps/provisioning_api/openapi-full.json

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,21 @@
333333
}
334334
}
335335
},
336+
"UserDetailsGroupDisplayname": {
337+
"type": "object",
338+
"required": [
339+
"id",
340+
"displayname"
341+
],
342+
"properties": {
343+
"id": {
344+
"type": "string"
345+
},
346+
"displayname": {
347+
"type": "string"
348+
}
349+
}
350+
},
336351
"UserDetailsQuota": {
337352
"type": "object",
338353
"properties": {
@@ -3530,7 +3545,8 @@
35303545
"data": {
35313546
"type": "object",
35323547
"required": [
3533-
"users"
3548+
"users",
3549+
"groups"
35343550
],
35353551
"properties": {
35363552
"users": {
@@ -3553,6 +3569,12 @@
35533569
}
35543570
]
35553571
}
3572+
},
3573+
"groups": {
3574+
"type": "array",
3575+
"items": {
3576+
"$ref": "#/components/schemas/UserDetailsGroupDisplayname"
3577+
}
35563578
}
35573579
}
35583580
}
@@ -3995,7 +4017,8 @@
39954017
"data": {
39964018
"type": "object",
39974019
"required": [
3998-
"users"
4020+
"users",
4021+
"groups"
39994022
],
40004023
"properties": {
40014024
"users": {
@@ -4018,6 +4041,12 @@
40184041
}
40194042
]
40204043
}
4044+
},
4045+
"groups": {
4046+
"type": "array",
4047+
"items": {
4048+
"$ref": "#/components/schemas/UserDetailsGroupDisplayname"
4049+
}
40214050
}
40224051
}
40234052
}

apps/provisioning_api/openapi.json

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,21 @@
333333
}
334334
}
335335
},
336+
"UserDetailsGroupDisplayname": {
337+
"type": "object",
338+
"required": [
339+
"id",
340+
"displayname"
341+
],
342+
"properties": {
343+
"id": {
344+
"type": "string"
345+
},
346+
"displayname": {
347+
"type": "string"
348+
}
349+
}
350+
},
336351
"UserDetailsQuota": {
337352
"type": "object",
338353
"properties": {
@@ -930,7 +945,8 @@
930945
"data": {
931946
"type": "object",
932947
"required": [
933-
"users"
948+
"users",
949+
"groups"
934950
],
935951
"properties": {
936952
"users": {
@@ -953,6 +969,12 @@
953969
}
954970
]
955971
}
972+
},
973+
"groups": {
974+
"type": "array",
975+
"items": {
976+
"$ref": "#/components/schemas/UserDetailsGroupDisplayname"
977+
}
956978
}
957979
}
958980
}
@@ -1506,7 +1528,8 @@
15061528
"data": {
15071529
"type": "object",
15081530
"required": [
1509-
"users"
1531+
"users",
1532+
"groups"
15101533
],
15111534
"properties": {
15121535
"users": {
@@ -1529,6 +1552,12 @@
15291552
}
15301553
]
15311554
}
1555+
},
1556+
"groups": {
1557+
"type": "array",
1558+
"items": {
1559+
"$ref": "#/components/schemas/UserDetailsGroupDisplayname"
1560+
}
15321561
}
15331562
}
15341563
}

apps/provisioning_api/tests/Controller/GroupsControllerTest.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88
namespace OCA\Provisioning_API\Tests\Controller;
99

10+
use OC\Group\DisplayNameCache as GroupDisplayNameCache;
1011
use OC\Group\Manager;
1112
use OC\User\NoUserException;
1213
use OCA\Provisioning_API\Controller\GroupsController;
@@ -36,6 +37,7 @@ class GroupsControllerTest extends \Test\TestCase {
3637
protected IFactory&MockObject $l10nFactory;
3738
protected LoggerInterface&MockObject $logger;
3839
protected GroupsController&MockObject $api;
40+
private GroupDisplayNameCache&MockObject $groupDisplayNameCache;
3941

4042
private IRootFolder $rootFolder;
4143

@@ -53,6 +55,7 @@ protected function setUp(): void {
5355
$this->l10nFactory = $this->createMock(IFactory::class);
5456
$this->logger = $this->createMock(LoggerInterface::class);
5557
$this->rootFolder = $this->createMock(IRootFolder::class);
58+
$this->groupDisplayNameCache = $this->createMock(GroupDisplayNameCache::class);
5659

5760
$this->groupManager
5861
->method('getSubAdmin')
@@ -70,7 +73,8 @@ protected function setUp(): void {
7073
$this->subAdminManager,
7174
$this->l10nFactory,
7275
$this->rootFolder,
73-
$this->logger
76+
$this->logger,
77+
$this->groupDisplayNameCache,
7478
])
7579
->onlyMethods(['fillStorageInfo'])
7680
->getMock();

0 commit comments

Comments
 (0)