Skip to content
Closed
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
127 changes: 69 additions & 58 deletions apps/files_trashbin/lib/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)) {
Expand All @@ -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 !== '') {
Expand All @@ -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);
}
}
40 changes: 18 additions & 22 deletions apps/files_trashbin/lib/Sabre/TrashRoot.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
) {
}

Expand Down Expand Up @@ -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'),
};
}
}
9 changes: 9 additions & 0 deletions apps/files_trashbin/lib/Trash/ITrashManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
55 changes: 28 additions & 27 deletions apps/files_trashbin/lib/Trash/LegacyTrashBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
20 changes: 20 additions & 0 deletions apps/files_trashbin/lib/Trash/TrashManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
8 changes: 0 additions & 8 deletions build/psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1828,14 +1828,6 @@
<code><![CDATA[INode]]></code>
</MismatchingDocblockReturnType>
</file>
<file src="apps/files_trashbin/lib/Sabre/TrashRoot.php">
<InvalidReturnStatement>
<code><![CDATA[$entry]]></code>
</InvalidReturnStatement>
<InvalidReturnType>
<code><![CDATA[ITrash]]></code>
</InvalidReturnType>
</file>
<file src="apps/files_trashbin/lib/Storage.php">
<DeprecatedMethod>
<code><![CDATA[dispatch]]></code>
Expand Down
Loading