Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 20 additions & 50 deletions lib/Controller/FolderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,6 @@
class FolderController extends OCSController {
private readonly ?IUser $user;

protected const ALLOWED_ORDER_BY = [
'mount_point',
'quota',
'groups',
'acl',
];

public function __construct(
string $AppName,
IRequest $request,
Expand Down Expand Up @@ -110,7 +103,8 @@ private function formatFolder(FolderWithMappingsAndCache $folder): array {
* @param bool $applicable Filter by applicable groups
* @param non-negative-int $offset Number of items to skip.
* @param ?positive-int $limit Number of items to return.
* @param null|'mount_point'|'quota'|'groups'|'acl' $orderBy The key to order by
* @param 'mount_point'|'quota'|'groups'|'acl' $orderBy The key to order by
* @param 'asc'|'desc' $order Sort ascending or descending
* @return DataResponse<Http::STATUS_OK, array<string, GroupFoldersFolder>, array{}>
* @throws OCSNotFoundException Storage not found
* @throws OCSBadRequestException Wrong limit used
Expand All @@ -119,58 +113,39 @@ private function formatFolder(FolderWithMappingsAndCache $folder): array {
*/
#[NoAdminRequired]
#[FrontpageRoute(verb: 'GET', url: '/folders')]
public function getFolders(bool $applicable = false, int $offset = 0, ?int $limit = null, ?string $orderBy = 'mount_point'): DataResponse {
public function getFolders(bool $applicable = false, int $offset = 0, ?int $limit = null, string $orderBy = 'mount_point', string $order = 'asc'): DataResponse {
/** @psalm-suppress DocblockTypeContradiction */
if ($limit !== null && $limit <= 0) {
throw new OCSBadRequestException('The limit must be greater than 0.');
}

/** @psalm-suppress DocblockTypeContradiction */
if (!in_array($orderBy, ['mount_point', 'quota', 'groups', 'acl'], true)) {
throw new OCSBadRequestException('The orderBy is not allowed.');
}

/** @psalm-suppress DocblockTypeContradiction */
if (!in_array($order, ['asc', 'desc'], true)) {
throw new OCSBadRequestException('The order is not allowed.');
}

$storageId = $this->getRootFolderStorageId();
if ($storageId === null) {
throw new OCSNotFoundException();
}

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

$orderBy = in_array($orderBy, self::ALLOWED_ORDER_BY, true)
? $orderBy
: 'mount_point';

// in case of equal orderBy value always fall back to the mount_point - same as on the frontend
/**
* @var GroupFoldersFolder $a
* @var GroupFoldersFolder $b
*/
uasort($folders, function (array $a, array $b) use ($orderBy) {
if ($orderBy === 'groups') {
if (($value = count($a['groups']) - count($b['groups'])) !== 0) {
return $value;
}
} else {
if (($value = $this->compareFolderNames((string)($a[$orderBy] ?? ''), (string)($b[$orderBy] ?? ''))) !== 0) {
return $value;
}
}

// fallback to mount_point
if (($value = $this->compareFolderNames($a['mount_point'] ?? '', $b['mount_point'])) !== 0) {
return $value;
}

// fallback to id
return $a['id'] - $b['id'];
});

$isAdmin = $this->delegationService->isAdminNextcloud() || $this->delegationService->isDelegatedAdmin();
if ($isAdmin && !$applicable) {
// If only the default values are provided the pagination can be skipped.
if ($offset !== 0 || $limit !== null) {
$folders = array_slice($folders, $offset, $limit, true);
}

return new DataResponse($folders);
}

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

// If only the default values are provided the pagination can be skipped.
if ($offset !== 0 || $limit !== null) {
$folders = array_slice($folders, $offset, $limit, true);
}

return new DataResponse($folders);
}

Expand Down
16 changes: 15 additions & 1 deletion lib/Folder/FolderManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,24 @@ private function selectWithFileCache(?IQueryBuilder $query = null): IQueryBuilde
* @return array<int, FolderWithMappingsAndCache>
* @throws Exception
*/
public function getAllFoldersWithSize(): array {
public function getAllFoldersWithSize(int $offset = 0, ?int $limit = null, string $orderBy = 'mount_point', string $order = 'ASC'): array {
$applicableMap = $this->getAllApplicable();

$query = $this->selectWithFileCache();
$query->setFirstResult($offset);
$query->setMaxResults($limit);
if ($orderBy === 'groups') {
$query
->leftJoin('f', 'group_folders_groups', 'g', $query->expr()->eq('f.folder_id', 'g.folder_id'))
->groupBy('f.folder_id')
->orderBy($query->func()->count('g.applicable_id'), $order);
} else {
$query->orderBy($orderBy, $order);
}
// Fallback in case two rows are the same after ordering by the $orderBy
if ($orderBy !== 'mount_point') {
$query->addOrderBy('mount_point', 'ASC');
}

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

Expand Down
1 change: 1 addition & 0 deletions lib/ResponseDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
* size: int,
* acl: bool,
* manage: list<GroupFoldersAclManage>,
* sortIndex?: int,
* }
*/
class ResponseDefinitions {
Expand Down
18 changes: 17 additions & 1 deletion openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@
"items": {
"$ref": "#/components/schemas/AclManage"
}
},
"sortIndex": {
"type": "integer",
"format": "int64"
}
}
},
Expand Down Expand Up @@ -585,7 +589,6 @@
"description": "The key to order by",
"schema": {
"type": "string",
"nullable": true,
"default": "mount_point",
"enum": [
"mount_point",
Expand All @@ -595,6 +598,19 @@
]
}
},
{
"name": "order",
"in": "query",
"description": "Sort ascending or descending",
"schema": {
"type": "string",
"default": "asc",
"enum": [
"asc",
"desc"
]
}
},
{
"name": "OCS-APIRequest",
"in": "header",
Expand Down
3 changes: 2 additions & 1 deletion src/settings/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ export class Api {
return generateUrl(`apps/groupfolders/${endpoint}`)
}

async listFolders(offset = 0, limit?: number, orderBy?: string): Promise<Folder[]> {
async listFolders(offset = 0, limit?: number, orderBy?: string, order?: string): Promise<Folder[]> {
const response = await axios.get<OCSResponse<Folder[]>>(this.getUrl('folders'), {
params: {
offset,
limit,
orderBy,
order,
},
})
return Object.values(response.data.ocs.data)
Expand Down
Loading
Loading