Skip to content

Commit b8a657b

Browse files
committed
perf: Allow filtering the directory content by mimetype
Signed-off-by: Carl Schwan <carlschwan@kde.org>
1 parent c6c11d4 commit b8a657b

File tree

13 files changed

+121
-117
lines changed

13 files changed

+121
-117
lines changed

apps/files/lib/Controller/ApiController.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ public function getRecentFiles() {
249249
* @param \OCP\Files\Node[] $nodes
250250
* @param int $depth The depth to traverse into the contents of each node
251251
*/
252-
private function getChildren(array $nodes, int $depth = 1, int $currentDepth = 0): array {
252+
private function getChildren(array $nodes, int $depth = 1, int $currentDepth = 0, string $mimeTypeFilter = ''): array {
253253
if ($currentDepth >= $depth) {
254254
return [];
255255
}
@@ -264,7 +264,7 @@ private function getChildren(array $nodes, int $depth = 1, int $currentDepth = 0
264264
$entry = [
265265
'id' => $node->getId(),
266266
'basename' => $basename,
267-
'children' => $this->getChildren($node->getDirectoryListing(), $depth, $currentDepth + 1),
267+
'children' => $this->getChildren($node->getDirectoryListing($mimeTypeFilter), $depth, $currentDepth + 1),
268268
];
269269
$displayName = $node->getName();
270270
if ($basename !== $displayName) {
@@ -308,8 +308,8 @@ public function getFolderTree(string $path = '/', int $depth = 1): JSONResponse
308308
'message' => $this->l10n->t('Invalid folder path'),
309309
], Http::STATUS_BAD_REQUEST);
310310
}
311-
$nodes = $node->getDirectoryListing();
312-
$tree = $this->getChildren($nodes, $depth);
311+
$nodes = $node->getDirectoryListing('httpd/unix-directory');
312+
$tree = $this->getChildren($nodes, $depth, 0, 'httpd/unix-directory');
313313
} catch (NotFoundException $e) {
314314
return new JSONResponse([
315315
'message' => $this->l10n->t('Folder not found'),

apps/files_sharing/lib/External/Cache.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ public function get($file) {
4141
return $result;
4242
}
4343

44-
public function getFolderContentsById($fileId) {
45-
$results = parent::getFolderContentsById($fileId);
44+
public function getFolderContentsById($fileId, ?string $mimeTypeFilter = null): array {
45+
$results = parent::getFolderContentsById($fileId, $mimeTypeFilter);
4646
foreach ($results as &$file) {
4747
$file['displayname_owner'] = $this->cloudId->getDisplayId();
4848
}

lib/private/Files/Cache/Cache.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,21 +216,30 @@ public function getFolderContents($folder) {
216216
* @param int $fileId the file id of the folder
217217
* @return ICacheEntry[]
218218
*/
219-
public function getFolderContentsById($fileId) {
219+
public function getFolderContentsById(int $fileId, ?string $mimeTypeFilter = null) {
220220
if ($fileId > -1) {
221221
$query = $this->getQueryBuilder();
222222
$query->selectFileCache()
223223
->whereParent($fileId)
224224
->whereStorageId($this->getNumericStorageId())
225225
->orderBy('name', 'ASC');
226226

227+
if ($mimeTypeFilter !== null) {
228+
$mimetype = $this->mimetypeLoader->getId($mimeTypeFilter);
229+
if (str_contains($mimeTypeFilter, '/')) {
230+
$query->andWhere($query->expr()->eq('mimetype', $query->createNamedParameter($mimetype)));
231+
} else {
232+
$query->andWhere($query->expr()->eq('mimepart', $query->createNamedParameter($mimetype)));
233+
}
234+
}
235+
227236
$metadataQuery = $query->selectMetadata();
228237

229238
$result = $query->executeQuery();
230239
$files = $result->fetchAll();
231240
$result->closeCursor();
232241

233-
return array_map(function (array $data) use ($metadataQuery) {
242+
return array_map(function (array $data) use ($metadataQuery): ICacheEntry {
234243
$data['metadata'] = $metadataQuery->extractMetadata($data)->asArray();
235244
return self::cacheEntryFromData($data, $this->mimetypeLoader);
236245
}, $files);

lib/private/Files/Cache/FailedCache.php

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,20 @@
1919
* Storage placeholder to represent a missing precondition, storage unavailable
2020
*/
2121
class FailedCache implements ICache {
22-
/** @var bool whether to show the failed storage in the ui */
23-
private $visible;
2422

2523
/**
26-
* FailedCache constructor.
27-
*
28-
* @param bool $visible
24+
* @param bool $visible Whether to show the failed storage in the ui
2925
*/
30-
public function __construct($visible = true) {
31-
$this->visible = $visible;
26+
public function __construct(
27+
private bool $visible = true,
28+
) {
3229
}
3330

34-
35-
public function getNumericStorageId() {
31+
public function getNumericStorageId(): int {
3632
return -1;
3733
}
3834

39-
public function get($file) {
35+
public function get($file): false|ICacheEntry {
4036
if ($file === '') {
4137
return new CacheEntry([
4238
'fileid' => -1,
@@ -51,11 +47,11 @@ public function get($file) {
5147
}
5248
}
5349

54-
public function getFolderContents($folder) {
50+
public function getFolderContents($folder): array {
5551
return [];
5652
}
5753

58-
public function getFolderContentsById($fileId) {
54+
public function getFolderContentsById(int $fileId, ?string $mimeTypeFilter = null): array {
5955
return [];
6056
}
6157

@@ -68,15 +64,15 @@ public function insert($file, array $data) {
6864
public function update($id, array $data) {
6965
}
7066

71-
public function getId($file) {
67+
public function getId($file): int {
7268
return -1;
7369
}
7470

75-
public function getParentId($file) {
71+
public function getParentId($file): int {
7672
return -1;
7773
}
7874

79-
public function inCache($file) {
75+
public function inCache($file): bool {
8076
return false;
8177
}
8278

lib/private/Files/Cache/Wrapper/CacheWrapper.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ public function getFolderContents($folder) {
105105
* @param int $fileId the file id of the folder
106106
* @return array
107107
*/
108-
public function getFolderContentsById($fileId) {
109-
$results = $this->getCache()->getFolderContentsById($fileId);
110-
return array_map([$this, 'formatCacheEntry'], $results);
108+
public function getFolderContentsById(int $fileId, ?string $mimeTypeFilter = null) {
109+
$results = $this->getCache()->getFolderContentsById($fileId, $mimeTypeFilter);
110+
return array_map($this->formatCacheEntry(...), $results);
111111
}
112112

113113
/**

lib/private/Files/Filesystem.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -660,11 +660,11 @@ public static function putFileInfo($path, $data) {
660660
* get the content of a directory
661661
*
662662
* @param string $directory path under datadirectory
663-
* @param string $mimetype_filter limit returned content to this mimetype or mimepart
663+
* @param string $mimeTypeFilter limit returned content to this mimetype or mimepart
664664
* @return \OC\Files\FileInfo[]
665665
*/
666-
public static function getDirectoryContent($directory, $mimetype_filter = '') {
667-
return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter);
666+
public static function getDirectoryContent($directory, $mimeTypeFilter = '') {
667+
return self::$defaultInstance->getDirectoryContent($directory, $mimeTypeFilter);
668668
}
669669

670670
/**

lib/private/Files/Node/Folder.php

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,11 @@ public function isSubNode($node) {
7676
return str_starts_with($node->getPath(), $this->path . '/');
7777
}
7878

79-
/**
80-
* get the content of this directory
81-
*
82-
* @return Node[]
83-
* @throws \OCP\Files\NotFoundException
84-
*/
85-
public function getDirectoryListing() {
86-
$folderContent = $this->view->getDirectoryContent($this->path, '', $this->getFileInfo(false));
79+
#[Override]
80+
public function getDirectoryListing(?string $mimetypeFilter = null): array {
81+
$folderContent = $this->view->getDirectoryContent($this->path, $mimetypeFilter, $this->getFileInfo(false));
8782

88-
return array_map(function (FileInfo $info) {
83+
return array_map(function (FileInfo $info): Node {
8984
if ($info->getMimetype() === FileInfo::MIMETYPE_FOLDER) {
9085
return new Folder($this->root, $this->view, $info->getPath(), $info, $this);
9186
} else {

lib/private/Files/Node/LazyFolder.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -415,10 +415,8 @@ public function isSubNode($node) {
415415
return $this->__call(__FUNCTION__, func_get_args());
416416
}
417417

418-
/**
419-
* @inheritDoc
420-
*/
421-
public function getDirectoryListing() {
418+
#[Override]
419+
public function getDirectoryListing(?string $mimetypeFilter = null): array {
422420
return $this->__call(__FUNCTION__, func_get_args());
423421
}
424422

lib/private/Files/Node/NonExistingFolder.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace OC\Files\Node;
99

1010
use OCP\Files\NotFoundException;
11+
use Override;
1112

1213
class NonExistingFolder extends Folder {
1314
/**
@@ -118,7 +119,8 @@ public function get($path) {
118119
throw new NotFoundException();
119120
}
120121

121-
public function getDirectoryListing() {
122+
#[Override]
123+
public function getDirectoryListing(?string $mimetypeFilter = null): never {
122124
throw new NotFoundException();
123125
}
124126

lib/private/Files/View.php

Lines changed: 65 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,10 +1471,10 @@ public function addSubMounts(FileInfo $info, $extOnly = false): void {
14711471
* get the content of a directory
14721472
*
14731473
* @param string $directory path under datadirectory
1474-
* @param string $mimetype_filter limit returned content to this mimetype or mimepart
1474+
* @param ?non-empty-string $mimeTypeFilter limit returned content to this mimetype or mimepart
14751475
* @return FileInfo[]
14761476
*/
1477-
public function getDirectoryContent($directory, $mimetype_filter = '', ?\OCP\Files\FileInfo $directoryInfo = null) {
1477+
public function getDirectoryContent(string $directory, ?string $mimeTypeFilter = null, ?\OCP\Files\FileInfo $directoryInfo = null) {
14781478
$this->assertPathLength($directory);
14791479
if (!Filesystem::isValidPath($directory)) {
14801480
return [];
@@ -1506,7 +1506,8 @@ public function getDirectoryContent($directory, $mimetype_filter = '', ?\OCP\Fil
15061506
}
15071507

15081508
$folderId = $data->getId();
1509-
$contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
1509+
/** @psalm-suppress TypeDoesNotContainType For legacy compatibility */
1510+
$contents = $cache->getFolderContentsById($folderId, $mimeTypeFilter === '' ? null : $mimeTypeFilter);
15101511

15111512
$sharingDisabled = \OCP\Util::isSharingDisabledForUser();
15121513
$permissionsMask = ~\OCP\Constants::PERMISSION_SHARE;
@@ -1567,79 +1568,79 @@ public function getDirectoryContent($directory, $mimetype_filter = '', ?\OCP\Fil
15671568
$rootEntry = $subCache->get('');
15681569
}
15691570

1570-
if ($rootEntry && ($rootEntry->getPermissions() & Constants::PERMISSION_READ)) {
1571-
$relativePath = trim(substr($mountPoint, $dirLength), '/');
1572-
if ($pos = strpos($relativePath, '/')) {
1573-
//mountpoint inside subfolder add size to the correct folder
1574-
$entryName = substr($relativePath, 0, $pos);
1575-
1576-
// Create parent folders if the mountpoint is inside a subfolder that doesn't exist yet
1577-
if (!isset($files[$entryName])) {
1578-
try {
1579-
[$storage, ] = $this->resolvePath($path . '/' . $entryName);
1580-
// make sure we can create the mountpoint folder, even if the user has a quota of 0
1581-
if ($storage->instanceOfStorage(Quota::class)) {
1582-
$storage->enableQuota(false);
1583-
}
1571+
if (!$rootEntry || !($rootEntry->getPermissions() & Constants::PERMISSION_READ)) {
1572+
continue;
1573+
}
15841574

1585-
if ($this->mkdir($path . '/' . $entryName) !== false) {
1586-
$info = $this->getFileInfo($path . '/' . $entryName);
1587-
if ($info !== false) {
1588-
$files[$entryName] = $info;
1589-
}
1590-
}
1575+
if ($mimeTypeFilter) {
1576+
if (strpos($mimeTypeFilter, '/') !== false && $rootEntry['mimetype'] !== $mimeTypeFilter) {
1577+
continue;
1578+
} elseif (strpos($mimeTypeFilter, '/') === false && $rootEntry['mimepart'] !== $mimeTypeFilter) {
1579+
continue;
1580+
}
1581+
}
15911582

1592-
if ($storage->instanceOfStorage(Quota::class)) {
1593-
$storage->enableQuota(true);
1583+
$relativePath = trim(substr($mountPoint, $dirLength), '/');
1584+
if ($pos = strpos($relativePath, '/')) {
1585+
//mountpoint inside subfolder add size to the correct folder
1586+
$entryName = substr($relativePath, 0, $pos);
1587+
1588+
// Create parent folders if the mountpoint is inside a subfolder that doesn't exist yet
1589+
if (!isset($files[$entryName])) {
1590+
try {
1591+
[$storage, ] = $this->resolvePath($path . '/' . $entryName);
1592+
// make sure we can create the mountpoint folder, even if the user has a quota of 0
1593+
if ($storage->instanceOfStorage(Quota::class)) {
1594+
$storage->enableQuota(false);
1595+
}
1596+
1597+
if ($this->mkdir($path . '/' . $entryName) !== false) {
1598+
$info = $this->getFileInfo($path . '/' . $entryName);
1599+
if ($info !== false) {
1600+
$files[$entryName] = $info;
15941601
}
1595-
} catch (\Exception $e) {
1596-
// Creating the parent folder might not be possible, for example due to a lack of permissions.
1597-
$this->logger->debug('Failed to create non-existent parent', ['exception' => $e, 'path' => $path . '/' . $entryName]);
15981602
}
1599-
}
16001603

1601-
if (isset($files[$entryName])) {
1602-
$files[$entryName]->addSubEntry($rootEntry, $mountPoint);
1603-
}
1604-
} else { //mountpoint in this folder, add an entry for it
1605-
$rootEntry['name'] = $relativePath;
1606-
$rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
1607-
$permissions = $rootEntry['permissions'];
1608-
// do not allow renaming/deleting the mount point if they are not shared files/folders
1609-
// for shared files/folders we use the permissions given by the owner
1610-
if ($mount instanceof MoveableMount) {
1611-
$rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
1612-
} else {
1613-
$rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
1604+
if ($storage->instanceOfStorage(Quota::class)) {
1605+
$storage->enableQuota(true);
1606+
}
1607+
} catch (\Exception $e) {
1608+
// Creating the parent folder might not be possible, for example due to a lack of permissions.
1609+
$this->logger->debug('Failed to create non-existent parent', ['exception' => $e, 'path' => $path . '/' . $entryName]);
16141610
}
1611+
}
16151612

1616-
$rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1613+
if (isset($files[$entryName])) {
1614+
$files[$entryName]->addSubEntry($rootEntry, $mountPoint);
1615+
}
1616+
} else { //mountpoint in this folder, add an entry for it
1617+
$rootEntry['name'] = $relativePath;
1618+
$rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
1619+
$permissions = $rootEntry['permissions'];
1620+
// do not allow renaming/deleting the mount point if they are not shared files/folders
1621+
// for shared files/folders we use the permissions given by the owner
1622+
if ($mount instanceof MoveableMount) {
1623+
$rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
1624+
} else {
1625+
$rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
1626+
}
16171627

1618-
// if sharing was disabled for the user we remove the share permissions
1619-
if ($sharingDisabled) {
1620-
$rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1621-
}
1628+
$rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
16221629

1623-
$ownerId = $subStorage->getOwner('');
1624-
if ($ownerId !== false) {
1625-
$owner = $this->getUserObjectForOwner($ownerId);
1626-
} else {
1627-
$owner = null;
1628-
}
1629-
$files[$rootEntry->getName()] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1630+
// if sharing was disabled for the user we remove the share permissions
1631+
if ($sharingDisabled) {
1632+
$rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
16301633
}
1631-
}
1632-
}
1633-
}
16341634

1635-
if ($mimetype_filter) {
1636-
$files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1637-
if (strpos($mimetype_filter, '/')) {
1638-
return $file->getMimetype() === $mimetype_filter;
1639-
} else {
1640-
return $file->getMimePart() === $mimetype_filter;
1635+
$ownerId = $subStorage->getOwner('');
1636+
if ($ownerId !== false) {
1637+
$owner = $this->getUserObjectForOwner($ownerId);
1638+
} else {
1639+
$owner = null;
1640+
}
1641+
$files[$rootEntry->getName()] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
16411642
}
1642-
});
1643+
}
16431644
}
16441645

16451646
return array_values($files);

0 commit comments

Comments
 (0)