Skip to content

Commit 8694106

Browse files
Merge pull request #4269 from nextcloud/backport/4263/stable32
2 parents c39f4fe + ed504ec commit 8694106

File tree

7 files changed

+139
-136
lines changed

7 files changed

+139
-136
lines changed

β€Žlib/Controller/FolderController.phpβ€Ž

Lines changed: 20 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,6 @@
4242
class FolderController extends OCSController {
4343
private readonly ?IUser $user;
4444

45-
protected const ALLOWED_ORDER_BY = [
46-
'mount_point',
47-
'quota',
48-
'groups',
49-
'acl',
50-
];
51-
5245
public function __construct(
5346
string $AppName,
5447
IRequest $request,
@@ -110,7 +103,8 @@ private function formatFolder(FolderWithMappingsAndCache $folder): array {
110103
* @param bool $applicable Filter by applicable groups
111104
* @param non-negative-int $offset Number of items to skip.
112105
* @param ?positive-int $limit Number of items to return.
113-
* @param null|'mount_point'|'quota'|'groups'|'acl' $orderBy The key to order by
106+
* @param 'mount_point'|'quota'|'groups'|'acl' $orderBy The key to order by
107+
* @param 'asc'|'desc' $order Sort ascending or descending
114108
* @return DataResponse<Http::STATUS_OK, array<string, GroupFoldersFolder>, array{}>
115109
* @throws OCSNotFoundException Storage not found
116110
* @throws OCSBadRequestException Wrong limit used
@@ -119,58 +113,39 @@ private function formatFolder(FolderWithMappingsAndCache $folder): array {
119113
*/
120114
#[NoAdminRequired]
121115
#[FrontpageRoute(verb: 'GET', url: '/folders')]
122-
public function getFolders(bool $applicable = false, int $offset = 0, ?int $limit = null, ?string $orderBy = 'mount_point'): DataResponse {
116+
public function getFolders(bool $applicable = false, int $offset = 0, ?int $limit = null, string $orderBy = 'mount_point', string $order = 'asc'): DataResponse {
117+
/** @psalm-suppress DocblockTypeContradiction */
123118
if ($limit !== null && $limit <= 0) {
124119
throw new OCSBadRequestException('The limit must be greater than 0.');
125120
}
126121

122+
/** @psalm-suppress DocblockTypeContradiction */
123+
if (!in_array($orderBy, ['mount_point', 'quota', 'groups', 'acl'], true)) {
124+
throw new OCSBadRequestException('The orderBy is not allowed.');
125+
}
126+
127+
/** @psalm-suppress DocblockTypeContradiction */
128+
if (!in_array($order, ['asc', 'desc'], true)) {
129+
throw new OCSBadRequestException('The order is not allowed.');
130+
}
131+
127132
$storageId = $this->getRootFolderStorageId();
128133
if ($storageId === null) {
129134
throw new OCSNotFoundException();
130135
}
131136

132137
$folders = [];
133-
foreach ($this->manager->getAllFoldersWithSize() as $id => $folder) {
138+
$i = 0;
139+
foreach ($this->manager->getAllFoldersWithSize($offset, $limit, $orderBy, $order) as $id => $folder) {
134140
// Make them string-indexed for OpenAPI JSON output
135-
$folders[(string)$id] = $this->formatFolder($folder);
141+
// JavaScript doesn't preserve JSON object key orders, so we need to manually add this information.
142+
$folders[(string)$id] = array_merge($this->formatFolder($folder), [
143+
'sortIndex' => $offset + $i++,
144+
]);
136145
}
137146

138-
$orderBy = in_array($orderBy, self::ALLOWED_ORDER_BY, true)
139-
? $orderBy
140-
: 'mount_point';
141-
142-
// in case of equal orderBy value always fall back to the mount_point - same as on the frontend
143-
/**
144-
* @var GroupFoldersFolder $a
145-
* @var GroupFoldersFolder $b
146-
*/
147-
uasort($folders, function (array $a, array $b) use ($orderBy) {
148-
if ($orderBy === 'groups') {
149-
if (($value = count($a['groups']) - count($b['groups'])) !== 0) {
150-
return $value;
151-
}
152-
} else {
153-
if (($value = $this->compareFolderNames((string)($a[$orderBy] ?? ''), (string)($b[$orderBy] ?? ''))) !== 0) {
154-
return $value;
155-
}
156-
}
157-
158-
// fallback to mount_point
159-
if (($value = $this->compareFolderNames($a['mount_point'] ?? '', $b['mount_point'])) !== 0) {
160-
return $value;
161-
}
162-
163-
// fallback to id
164-
return $a['id'] - $b['id'];
165-
});
166-
167147
$isAdmin = $this->delegationService->isAdminNextcloud() || $this->delegationService->isDelegatedAdmin();
168148
if ($isAdmin && !$applicable) {
169-
// If only the default values are provided the pagination can be skipped.
170-
if ($offset !== 0 || $limit !== null) {
171-
$folders = array_slice($folders, $offset, $limit, true);
172-
}
173-
174149
return new DataResponse($folders);
175150
}
176151

@@ -182,11 +157,6 @@ public function getFolders(bool $applicable = false, int $offset = 0, ?int $limi
182157
$folders = array_filter(array_map($this->filterNonAdminFolder(...), $folders));
183158
}
184159

185-
// If only the default values are provided the pagination can be skipped.
186-
if ($offset !== 0 || $limit !== null) {
187-
$folders = array_slice($folders, $offset, $limit, true);
188-
}
189-
190160
return new DataResponse($folders);
191161
}
192162

β€Žlib/Folder/FolderManager.phpβ€Ž

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,24 @@ private function selectWithFileCache(?IQueryBuilder $query = null): IQueryBuilde
132132
* @return array<int, FolderWithMappingsAndCache>
133133
* @throws Exception
134134
*/
135-
public function getAllFoldersWithSize(): array {
135+
public function getAllFoldersWithSize(int $offset = 0, ?int $limit = null, string $orderBy = 'mount_point', string $order = 'ASC'): array {
136136
$applicableMap = $this->getAllApplicable();
137137

138138
$query = $this->selectWithFileCache();
139+
$query->setFirstResult($offset);
140+
$query->setMaxResults($limit);
141+
if ($orderBy === 'groups') {
142+
$query
143+
->leftJoin('f', 'group_folders_groups', 'g', $query->expr()->eq('f.folder_id', 'g.folder_id'))
144+
->groupBy('f.folder_id')
145+
->orderBy($query->func()->count('g.applicable_id'), $order);
146+
} else {
147+
$query->orderBy($orderBy, $order);
148+
}
149+
// Fallback in case two rows are the same after ordering by the $orderBy
150+
if ($orderBy !== 'mount_point') {
151+
$query->addOrderBy('mount_point', 'ASC');
152+
}
139153

140154
$rows = $query->executeQuery()->fetchAll();
141155

β€Žlib/ResponseDefinitions.phpβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
* size: int,
5656
* acl: bool,
5757
* manage: list<GroupFoldersAclManage>,
58+
* sortIndex?: int,
5859
* }
5960
*/
6061
class ResponseDefinitions {

β€Žopenapi.jsonβ€Ž

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@
182182
"items": {
183183
"$ref": "#/components/schemas/AclManage"
184184
}
185+
},
186+
"sortIndex": {
187+
"type": "integer",
188+
"format": "int64"
185189
}
186190
}
187191
},
@@ -585,7 +589,6 @@
585589
"description": "The key to order by",
586590
"schema": {
587591
"type": "string",
588-
"nullable": true,
589592
"default": "mount_point",
590593
"enum": [
591594
"mount_point",
@@ -595,6 +598,19 @@
595598
]
596599
}
597600
},
601+
{
602+
"name": "order",
603+
"in": "query",
604+
"description": "Sort ascending or descending",
605+
"schema": {
606+
"type": "string",
607+
"default": "asc",
608+
"enum": [
609+
"asc",
610+
"desc"
611+
]
612+
}
613+
},
598614
{
599615
"name": "OCS-APIRequest",
600616
"in": "header",

β€Žsrc/settings/Api.tsβ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ export class Api {
1515
return generateUrl(`apps/groupfolders/${endpoint}`)
1616
}
1717

18-
async listFolders(offset = 0, limit?: number, orderBy?: string): Promise<Folder[]> {
18+
async listFolders(offset = 0, limit?: number, orderBy?: string, order?: string): Promise<Folder[]> {
1919
const response = await axios.get<OCSResponse<Folder[]>>(this.getUrl('folders'), {
2020
params: {
2121
offset,
2222
limit,
2323
orderBy,
24+
order,
2425
},
2526
})
2627
return Object.values(response.data.ocs.data)

0 commit comments

Comments
Β (0)