diff --git a/apps/files_trashbin/lib/Helper.php b/apps/files_trashbin/lib/Helper.php
index 746832e9280cd..e4fb4c46c2bf6 100644
--- a/apps/files_trashbin/lib/Helper.php
+++ b/apps/files_trashbin/lib/Helper.php
@@ -12,6 +12,7 @@
use OCP\Constants;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\IMimeTypeDetector;
+use OCP\Files\Mount\IMountPoint;
use OCP\Server;
class Helper {
@@ -26,9 +27,6 @@ class Helper {
* @return \OCP\Files\FileInfo[]
*/
public static function getTrashFiles($dir, $user, $sortAttribute = '', $sortDescending = false) {
- $result = [];
- $timestamp = null;
-
$view = new View('/' . $user . '/files_trashbin/files');
if (ltrim($dir, '/') !== '' && !$view->is_dir($dir)) {
@@ -39,50 +37,13 @@ public static function getTrashFiles($dir, $user, $sortAttribute = '', $sortDesc
$storage = $mount->getStorage();
$absoluteDir = $view->getAbsolutePath($dir);
$internalPath = $mount->getInternalPath($absoluteDir);
-
$extraData = Trashbin::getExtraData($user);
+
+ $result = [];
+ $timestamp = null;
$dirContent = $storage->getCache()->getFolderContents($mount->getInternalPath($view->getAbsolutePath($dir)));
foreach ($dirContent as $entry) {
- $entryName = $entry->getName();
- $name = $entryName;
- if ($dir === '' || $dir === '/') {
- $pathparts = pathinfo($entryName);
- $timestamp = substr($pathparts['extension'], 1);
- $name = $pathparts['filename'];
- } elseif ($timestamp === null) {
- // for subfolders we need to calculate the timestamp only once
- $parts = explode('/', ltrim($dir, '/'));
- $timestamp = substr(pathinfo($parts[0], PATHINFO_EXTENSION), 1);
- }
- $originalPath = '';
- $originalName = substr($entryName, 0, -strlen($timestamp) - 2);
- if (isset($extraData[$originalName][$timestamp]['location'])) {
- $originalPath = $extraData[$originalName][$timestamp]['location'];
- if (substr($originalPath, -1) === '/') {
- $originalPath = substr($originalPath, 0, -1);
- }
- }
- $type = $entry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE ? 'dir' : 'file';
- $i = [
- 'name' => $name,
- 'mtime' => $timestamp,
- 'mimetype' => $type === 'dir' ? 'httpd/unix-directory' : Server::get(IMimeTypeDetector::class)->detectPath($name),
- 'type' => $type,
- 'directory' => ($dir === '/') ? '' : $dir,
- 'size' => $entry->getSize(),
- 'etag' => '',
- 'permissions' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE,
- 'fileid' => $entry->getId(),
- ];
- if ($originalPath) {
- if ($originalPath !== '.') {
- $i['extraData'] = $originalPath . '/' . $originalName;
- } else {
- $i['extraData'] = $originalName;
- }
- }
- $i['deletedBy'] = $extraData[$originalName][$timestamp]['deletedBy'] ?? null;
- $result[] = new FileInfo($absoluteDir . '/' . $i['name'], $storage, $internalPath . '/' . $i['name'], $i, $mount);
+ $result[] = self::buildFileInfo($entry, $dir, $timestamp, $extraData, $storage, $absoluteDir, $internalPath, $mount);
}
if ($sortAttribute !== '') {
@@ -91,20 +52,70 @@ public static function getTrashFiles($dir, $user, $sortAttribute = '', $sortDesc
return $result;
}
- /**
- * Format file infos for JSON
- *
- * @param \OCP\Files\FileInfo[] $fileInfos file infos
- */
- public static function formatFileInfos($fileInfos) {
- $files = [];
- foreach ($fileInfos as $i) {
- $entry = \OCA\Files\Helper::formatFileInfo($i);
- $entry['id'] = $i->getId();
- $entry['etag'] = $entry['mtime']; // add fake etag, it is only needed to identify the preview image
- $entry['permissions'] = Constants::PERMISSION_READ;
- $files[] = $entry;
+ public static function getTrashFile(string $dir, string $user, string $name): ?FileInfo {
+ $timestamp = null;
+
+ $view = new View('/' . $user . '/files_trashbin/files');
+
+ if (ltrim($dir, '/') !== '' && !$view->is_dir($dir)) {
+ throw new \Exception('Directory does not exists');
+ }
+
+ $mount = $view->getMount($dir);
+ $storage = $mount->getStorage();
+ $absoluteDir = $view->getAbsolutePath($dir);
+ $internalPath = $mount->getInternalPath($absoluteDir);
+
+ $extraData = Trashbin::getExtraData($user);
+ $entry = $storage->getCache()->get($mount->getInternalPath($view->getAbsolutePath($dir . '/' . $name)));
+ if ($entry === false) {
+ return null;
+ }
+
+ $timestamp = null;
+ return self::buildFileInfo($entry, $dir, $timestamp, $extraData, $storage, $absoluteDir, $internalPath, $mount);
+ }
+
+ private static function buildFileInfo(ICacheEntry $entry, string $dir, ?string &$timestamp, array $extraData, $storage, string $absoluteDir, string $internalPath, IMountPoint $mount): FileInfo {
+ $entryName = $entry->getName();
+ $name = $entryName;
+ if ($dir === '' || $dir === '/') {
+ $pathparts = pathinfo($entryName);
+ $timestamp = substr($pathparts['extension'], 1);
+ $name = $pathparts['filename'];
+ } elseif ($timestamp === null) {
+ // for subfolders we need to calculate the timestamp only once
+ $parts = explode('/', ltrim($dir, '/'));
+ $timestamp = substr(pathinfo($parts[0], PATHINFO_EXTENSION), 1);
+ }
+ $originalPath = '';
+ $originalName = substr($entryName, 0, -strlen($timestamp) - 2);
+ if (isset($extraData[$originalName][$timestamp]['location'])) {
+ $originalPath = $extraData[$originalName][$timestamp]['location'];
+ if (substr($originalPath, -1) === '/') {
+ $originalPath = substr($originalPath, 0, -1);
+ }
+ }
+ $type = $entry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE ? 'dir' : 'file';
+ $i = [
+ 'name' => $name,
+ 'mtime' => $timestamp,
+ 'mimetype' => $type === 'dir' ? 'httpd/unix-directory' : Server::get(IMimeTypeDetector::class)->detectPath($name),
+ 'type' => $type,
+ 'directory' => ($dir === '/') ? '' : $dir,
+ 'size' => $entry->getSize(),
+ 'etag' => '',
+ 'permissions' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE,
+ 'fileid' => $entry->getId(),
+ ];
+ if ($originalPath) {
+ if ($originalPath !== '.') {
+ $i['extraData'] = $originalPath . '/' . $originalName;
+ } else {
+ $i['extraData'] = $originalName;
+ }
}
- return $files;
+ $i['deletedBy'] = $extraData[$originalName][$timestamp]['deletedBy'] ?? null;
+ return new FileInfo($absoluteDir . '/' . $i['name'], $storage, $internalPath . '/' . $i['name'], $i, $mount);
}
}
diff --git a/apps/files_trashbin/lib/Sabre/TrashRoot.php b/apps/files_trashbin/lib/Sabre/TrashRoot.php
index dd89583d9a1b3..f44eaa4340680 100644
--- a/apps/files_trashbin/lib/Sabre/TrashRoot.php
+++ b/apps/files_trashbin/lib/Sabre/TrashRoot.php
@@ -14,15 +14,17 @@
use OCA\Files_Trashbin\Trashbin;
use OCP\Files\FileInfo;
use OCP\IUser;
+use RuntimeException;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
+use Sabre\DAV\INode;
class TrashRoot implements ICollection {
public function __construct(
- private IUser $user,
- private ITrashManager $trashManager,
+ private readonly IUser $user,
+ private readonly ITrashManager $trashManager,
) {
}
@@ -56,38 +58,32 @@ public function createDirectory($name) {
public function getChildren(): array {
$entries = $this->trashManager->listTrashRoot($this->user);
- $children = array_map(function (ITrashItem $entry) {
- if ($entry->getType() === FileInfo::TYPE_FOLDER) {
- return new TrashFolder($this->trashManager, $entry);
- }
- return new TrashFile($this->trashManager, $entry);
- }, $entries);
-
- return $children;
+ return array_map($this->trashItemToTrashNode(...), $entries);
}
public function getChild($name): ITrash {
- $entries = $this->getChildren();
+ $entry = $this->trashManager->getTrashRootItem($this->user, $name);
- foreach ($entries as $entry) {
- if ($entry->getName() === $name) {
- return $entry;
- }
+ if ($entry === null) {
+ throw new NotFound();
}
- throw new NotFound();
+ return $this->trashItemToTrashNode($entry);
}
public function childExists($name): bool {
- try {
- $this->getChild($name);
- return true;
- } catch (NotFound $e) {
- return false;
- }
+ return $this->trashManager->getTrashRootItem($this->user, $name) !== null;
}
public function getLastModified(): int {
return 0;
}
+
+ private function trashItemToTrashNode(ITrashItem $entry): ITrash&INode {
+ return match ($entry->getType()) {
+ FileInfo::TYPE_FOLDER => new TrashFolder($this->trashManager, $entry),
+ FileInfo::TYPE_FILE => new TrashFile($this->trashManager, $entry),
+ default => throw new RuntimeException('Invalid FileInfo object'),
+ };
+ }
}
diff --git a/apps/files_trashbin/lib/Trash/ITrashManager.php b/apps/files_trashbin/lib/Trash/ITrashManager.php
index 743ea01358a1d..5547861a06db1 100644
--- a/apps/files_trashbin/lib/Trash/ITrashManager.php
+++ b/apps/files_trashbin/lib/Trash/ITrashManager.php
@@ -27,6 +27,15 @@ public function registerBackend(string $storageType, ITrashBackend $backend);
*/
public function listTrashRoot(IUser $user): array;
+ /**
+ * Get a specific item in the root of the trashbin
+ *
+ * @param IUser $user
+ * @return ?ITrashItem
+ * @since 32.0.13
+ */
+ public function getTrashRootItem(IUser $user, string $name): ?ITrashItem;
+
/**
* Temporally prevent files from being moved to the trash
*
diff --git a/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php b/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php
index 204defde35c72..2117596eda802 100644
--- a/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php
+++ b/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php
@@ -29,44 +29,45 @@ public function __construct(
) {
}
- /**
- * @param array $items
- * @param IUser $user
- * @param ITrashItem $parent
- * @return ITrashItem[]
- */
- private function mapTrashItems(array $items, IUser $user, ?ITrashItem $parent = null): array {
+ private function mapTrashItem(FileInfo $file, IUser $user, ?ITrashItem $parent = null): ITrashItem {
$parentTrashPath = ($parent instanceof ITrashItem) ? $parent->getTrashPath() : '';
$isRoot = $parent === null;
- return array_map(function (FileInfo $file) use ($parent, $parentTrashPath, $isRoot, $user) {
- $originalLocation = $isRoot ? $file['extraData'] : $parent->getOriginalLocation() . '/' . $file->getName();
- if (!$originalLocation) {
- $originalLocation = $file->getName();
- }
- /** @psalm-suppress UndefinedInterfaceMethod */
- $deletedBy = $this->userManager->get($file['deletedBy']) ?? $parent?->getDeletedBy();
- $trashFilename = Trashbin::getTrashFilename($file->getName(), $file->getMtime());
- return new TrashItem(
- $this,
- $originalLocation,
- $file->getMTime(),
- $parentTrashPath . '/' . ($isRoot ? $trashFilename : $file->getName()),
- $file,
- $user,
- $deletedBy,
- );
- }, $items);
+
+ $originalLocation = $isRoot ? $file['extraData'] : $parent->getOriginalLocation() . '/' . $file->getName();
+ if (!$originalLocation) {
+ $originalLocation = $file->getName();
+ }
+ /** @psalm-suppress UndefinedInterfaceMethod */
+ $deletedBy = $this->userManager->get($file['deletedBy']) ?? $parent?->getDeletedBy();
+ $trashFilename = Trashbin::getTrashFilename($file->getName(), $file->getMtime());
+ return new TrashItem(
+ $this,
+ $originalLocation,
+ $file->getMTime(),
+ $parentTrashPath . '/' . ($isRoot ? $trashFilename : $file->getName()),
+ $file,
+ $user,
+ $deletedBy,
+ );
}
public function listTrashRoot(IUser $user): array {
$entries = Helper::getTrashFiles('/', $user->getUID());
- return $this->mapTrashItems($entries, $user);
+ return array_map(fn (FileInfo $fileInfo): ITrashItem => $this->mapTrashItem($fileInfo, $user), $entries);
+ }
+
+ public function getTrashRootItem(IUser $user, string $name): ?ITrashItem {
+ $entry = Helper::getTrashFile('/', $user->getUID(), $name);
+ if ($entry === null) {
+ return null;
+ }
+ return $this->mapTrashItem($entry, $user);
}
public function listTrashFolder(ITrashItem $folder): array {
$user = $folder->getUser();
$entries = Helper::getTrashFiles($folder->getTrashPath(), $user->getUID());
- return $this->mapTrashItems($entries, $user, $folder);
+ return array_map(fn (FileInfo $fileInfo): ITrashItem => $this->mapTrashItem($fileInfo, $user, $folder), $entries);
}
public function restoreItem(ITrashItem $item) {
diff --git a/apps/files_trashbin/lib/Trash/TrashManager.php b/apps/files_trashbin/lib/Trash/TrashManager.php
index 521a576c00a7b..04cac30f8f4e9 100644
--- a/apps/files_trashbin/lib/Trash/TrashManager.php
+++ b/apps/files_trashbin/lib/Trash/TrashManager.php
@@ -36,6 +36,26 @@ public function listTrashRoot(IUser $user): array {
return $items;
}
+ #[\Override]
+ public function getTrashRootItem(IUser $user, string $name): ?ITrashItem {
+ foreach ($this->getBackends() as $backend) {
+ if (method_exists($backend, 'getTrashRootItem')) {
+ $item = $backend->getTrashRootItem($user, $name);
+ if ($item !== null) {
+ return $item;
+ }
+ } else {
+ $items = $backend->listTrashRoot($user);
+ foreach ($items as $item) {
+ if ($item->getName() === $name) {
+ return $item;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
private function getBackendForItem(ITrashItem $item) {
return $item->getTrashBackend();
}
diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml
index b2ce8989bd72a..b69ada09a7c7f 100644
--- a/build/psalm-baseline.xml
+++ b/build/psalm-baseline.xml
@@ -1828,14 +1828,6 @@
-
-
-
-
-
-
-
-