diff options
Diffstat (limited to 'apps/files_trashbin/lib/Trash')
-rw-r--r-- | apps/files_trashbin/lib/Trash/BackendNotFoundException.php | 10 | ||||
-rw-r--r-- | apps/files_trashbin/lib/Trash/ITrashBackend.php | 67 | ||||
-rw-r--r-- | apps/files_trashbin/lib/Trash/ITrashItem.php | 70 | ||||
-rw-r--r-- | apps/files_trashbin/lib/Trash/ITrashManager.php | 41 | ||||
-rw-r--r-- | apps/files_trashbin/lib/Trash/LegacyTrashBackend.php | 121 | ||||
-rw-r--r-- | apps/files_trashbin/lib/Trash/TrashItem.php | 172 | ||||
-rw-r--r-- | apps/files_trashbin/lib/Trash/TrashManager.php | 111 |
7 files changed, 592 insertions, 0 deletions
diff --git a/apps/files_trashbin/lib/Trash/BackendNotFoundException.php b/apps/files_trashbin/lib/Trash/BackendNotFoundException.php new file mode 100644 index 00000000000..292b6ee293c --- /dev/null +++ b/apps/files_trashbin/lib/Trash/BackendNotFoundException.php @@ -0,0 +1,10 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Files_Trashbin\Trash; + +class BackendNotFoundException extends \Exception { +} diff --git a/apps/files_trashbin/lib/Trash/ITrashBackend.php b/apps/files_trashbin/lib/Trash/ITrashBackend.php new file mode 100644 index 00000000000..11b3132bfba --- /dev/null +++ b/apps/files_trashbin/lib/Trash/ITrashBackend.php @@ -0,0 +1,67 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Files_Trashbin\Trash; + +use OCP\Files\Node; +use OCP\Files\Storage\IStorage; +use OCP\IUser; + +/** + * @since 15.0.0 + */ +interface ITrashBackend { + /** + * List all trash items in the root of the trashbin + * + * @param IUser $user + * @return ITrashItem[] + * @since 15.0.0 + */ + public function listTrashRoot(IUser $user): array; + + /** + * List all trash items in a subfolder in the trashbin + * + * @param ITrashItem $folder + * @return ITrashItem[] + * @since 15.0.0 + */ + public function listTrashFolder(ITrashItem $folder): array; + + /** + * Restore a trashbin item + * + * @param ITrashItem $item + * @since 15.0.0 + */ + public function restoreItem(ITrashItem $item); + + /** + * Permanently remove an item from trash + * + * @param ITrashItem $item + * @since 15.0.0 + */ + public function removeItem(ITrashItem $item); + + /** + * Move a file or folder to trash + * + * @param IStorage $storage + * @param string $internalPath + * @return boolean whether or not the file was moved to trash, if false then the file should be deleted normally + * @since 15.0.0 + */ + public function moveToTrash(IStorage $storage, string $internalPath): bool; + + /** + * @param IUser $user + * @param int $fileId + * @return Node|null + */ + public function getTrashNodeById(IUser $user, int $fileId); +} diff --git a/apps/files_trashbin/lib/Trash/ITrashItem.php b/apps/files_trashbin/lib/Trash/ITrashItem.php new file mode 100644 index 00000000000..299cac49a69 --- /dev/null +++ b/apps/files_trashbin/lib/Trash/ITrashItem.php @@ -0,0 +1,70 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Files_Trashbin\Trash; + +use OCP\Files\FileInfo; +use OCP\IUser; + +/** + * @since 15.0.0 + */ +interface ITrashItem extends FileInfo { + /** + * Get the trash backend for this item + * + * @return ITrashBackend + * @since 15.0.0 + */ + public function getTrashBackend(): ITrashBackend; + + /** + * Get the original location for the trash item + * + * @return string + * @since 15.0.0 + */ + public function getOriginalLocation(): string; + + /** + * Get the timestamp that the file was moved to trash + * + * @return int + * @since 15.0.0 + */ + public function getDeletedTime(): int; + + /** + * Get the path of the item relative to the users trashbin + * + * @return string + * @since 15.0.0 + */ + public function getTrashPath(): string; + + /** + * Whether the item is a deleted item in the root of the trash, or a file in a subfolder + * + * @return bool + * @since 15.0.0 + */ + public function isRootItem(): bool; + + /** + * Get the user for which this trash item applies + * + * @return IUser + * @since 15.0.0 + */ + public function getUser(): IUser; + + /** + * @since 30.0.0 + */ + public function getDeletedBy(): ?IUser; + + public function getTitle(): string; +} diff --git a/apps/files_trashbin/lib/Trash/ITrashManager.php b/apps/files_trashbin/lib/Trash/ITrashManager.php new file mode 100644 index 00000000000..743ea01358a --- /dev/null +++ b/apps/files_trashbin/lib/Trash/ITrashManager.php @@ -0,0 +1,41 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Files_Trashbin\Trash; + +use OCP\IUser; + +interface ITrashManager extends ITrashBackend { + /** + * Add a backend for the trashbin + * + * @param string $storageType + * @param ITrashBackend $backend + * @since 15.0.0 + */ + public function registerBackend(string $storageType, ITrashBackend $backend); + + /** + * List all trash items in the root of the trashbin + * + * @param IUser $user + * @return ITrashItem[] + * @since 15.0.0 + */ + public function listTrashRoot(IUser $user): array; + + /** + * Temporally prevent files from being moved to the trash + * + * @since 15.0.0 + */ + public function pauseTrash(); + + /** + * @since 15.0.0 + */ + public function resumeTrash(); +} diff --git a/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php b/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php new file mode 100644 index 00000000000..204defde35c --- /dev/null +++ b/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php @@ -0,0 +1,121 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Files_Trashbin\Trash; + +use OC\Files\Filesystem; +use OC\Files\View; +use OCA\Files_Trashbin\Helper; +use OCA\Files_Trashbin\Storage; +use OCA\Files_Trashbin\Trashbin; +use OCP\Files\FileInfo; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\Files\Storage\IStorage; +use OCP\IUser; +use OCP\IUserManager; + +class LegacyTrashBackend implements ITrashBackend { + /** @var array */ + private $deletedFiles = []; + + public function __construct( + private IRootFolder $rootFolder, + private IUserManager $userManager, + ) { + } + + /** + * @param array $items + * @param IUser $user + * @param ITrashItem $parent + * @return ITrashItem[] + */ + private function mapTrashItems(array $items, IUser $user, ?ITrashItem $parent = null): array { + $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); + } + + public function listTrashRoot(IUser $user): array { + $entries = Helper::getTrashFiles('/', $user->getUID()); + return $this->mapTrashItems($entries, $user); + } + + public function listTrashFolder(ITrashItem $folder): array { + $user = $folder->getUser(); + $entries = Helper::getTrashFiles($folder->getTrashPath(), $user->getUID()); + return $this->mapTrashItems($entries, $user, $folder); + } + + public function restoreItem(ITrashItem $item) { + Trashbin::restore($item->getTrashPath(), $item->getName(), $item->isRootItem() ? $item->getDeletedTime() : null); + } + + public function removeItem(ITrashItem $item) { + $user = $item->getUser(); + if ($item->isRootItem()) { + $path = substr($item->getTrashPath(), 0, -strlen('.d' . $item->getDeletedTime())); + Trashbin::delete($path, $user->getUID(), $item->getDeletedTime()); + } else { + Trashbin::delete($item->getTrashPath(), $user->getUID(), null); + } + } + + public function moveToTrash(IStorage $storage, string $internalPath): bool { + if (!$storage instanceof Storage) { + return false; + } + $normalized = Filesystem::normalizePath($storage->getMountPoint() . '/' . $internalPath, true, false, true); + $view = Filesystem::getView(); + if (!isset($this->deletedFiles[$normalized]) && $view instanceof View) { + $this->deletedFiles[$normalized] = $normalized; + if ($filesPath = $view->getRelativePath($normalized)) { + $filesPath = trim($filesPath, '/'); + $result = Trashbin::move2trash($filesPath); + } else { + $result = false; + } + unset($this->deletedFiles[$normalized]); + } else { + $result = false; + } + + return $result; + } + + public function getTrashNodeById(IUser $user, int $fileId) { + try { + $userFolder = $this->rootFolder->getUserFolder($user->getUID()); + $trash = $userFolder->getParent()->get('files_trashbin/files'); + if ($trash instanceof Folder) { + return $trash->getFirstNodeById($fileId); + } else { + return null; + } + } catch (NotFoundException $e) { + return null; + } + } +} diff --git a/apps/files_trashbin/lib/Trash/TrashItem.php b/apps/files_trashbin/lib/Trash/TrashItem.php new file mode 100644 index 00000000000..2ae999a2069 --- /dev/null +++ b/apps/files_trashbin/lib/Trash/TrashItem.php @@ -0,0 +1,172 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Files_Trashbin\Trash; + +use OCP\Files\FileInfo; +use OCP\IUser; + +class TrashItem implements ITrashItem { + + public function __construct( + private ITrashBackend $backend, + private string $originalLocation, + private int $deletedTime, + private string $trashPath, + private FileInfo $fileInfo, + private IUser $user, + private ?IUser $deletedBy, + ) { + } + + public function getTrashBackend(): ITrashBackend { + return $this->backend; + } + + public function getOriginalLocation(): string { + return $this->originalLocation; + } + + public function getDeletedTime(): int { + return $this->deletedTime; + } + + public function getTrashPath(): string { + return $this->trashPath; + } + + public function isRootItem(): bool { + return substr_count($this->getTrashPath(), '/') === 1; + } + + public function getUser(): IUser { + return $this->user; + } + + public function getEtag() { + return $this->fileInfo->getEtag(); + } + + public function getSize($includeMounts = true) { + return $this->fileInfo->getSize($includeMounts); + } + + public function getMtime() { + return $this->fileInfo->getMtime(); + } + + public function getName() { + return $this->fileInfo->getName(); + } + + public function getInternalPath() { + return $this->fileInfo->getInternalPath(); + } + + public function getPath() { + return $this->fileInfo->getPath(); + } + + public function getMimetype() { + return $this->fileInfo->getMimetype(); + } + + public function getMimePart() { + return $this->fileInfo->getMimePart(); + } + + public function getStorage() { + return $this->fileInfo->getStorage(); + } + + public function getId() { + return $this->fileInfo->getId(); + } + + public function isEncrypted() { + return $this->fileInfo->isEncrypted(); + } + + public function getPermissions() { + return $this->fileInfo->getPermissions(); + } + + public function getType() { + return $this->fileInfo->getType(); + } + + public function isReadable() { + return $this->fileInfo->isReadable(); + } + + public function isUpdateable() { + return $this->fileInfo->isUpdateable(); + } + + public function isCreatable() { + return $this->fileInfo->isCreatable(); + } + + public function isDeletable() { + return $this->fileInfo->isDeletable(); + } + + public function isShareable() { + return $this->fileInfo->isShareable(); + } + + public function isShared() { + return $this->fileInfo->isShared(); + } + + public function isMounted() { + return $this->fileInfo->isMounted(); + } + + public function getMountPoint() { + return $this->fileInfo->getMountPoint(); + } + + public function getOwner() { + return $this->fileInfo->getOwner(); + } + + public function getChecksum() { + return $this->fileInfo->getChecksum(); + } + + public function getExtension(): string { + return $this->fileInfo->getExtension(); + } + + public function getTitle(): string { + return $this->getOriginalLocation(); + } + + public function getCreationTime(): int { + return $this->fileInfo->getCreationTime(); + } + + public function getUploadTime(): int { + return $this->fileInfo->getUploadTime(); + } + + public function getParentId(): int { + return $this->fileInfo->getParentId(); + } + + public function getDeletedBy(): ?IUser { + return $this->deletedBy; + } + + /** + * @inheritDoc + * @return array<string, int|string|bool|float|string[]|int[]> + */ + public function getMetadata(): array { + return $this->fileInfo->getMetadata(); + } +} diff --git a/apps/files_trashbin/lib/Trash/TrashManager.php b/apps/files_trashbin/lib/Trash/TrashManager.php new file mode 100644 index 00000000000..521a576c00a --- /dev/null +++ b/apps/files_trashbin/lib/Trash/TrashManager.php @@ -0,0 +1,111 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Files_Trashbin\Trash; + +use OCP\Files\Storage\IStorage; +use OCP\IUser; + +class TrashManager implements ITrashManager { + /** @var ITrashBackend[] */ + private $backends = []; + + private $trashPaused = false; + + public function registerBackend(string $storageType, ITrashBackend $backend) { + $this->backends[$storageType] = $backend; + } + + /** + * @return ITrashBackend[] + */ + private function getBackends(): array { + return $this->backends; + } + + public function listTrashRoot(IUser $user): array { + $items = array_reduce($this->getBackends(), function (array $items, ITrashBackend $backend) use ($user) { + return array_merge($items, $backend->listTrashRoot($user)); + }, []); + usort($items, function (ITrashItem $a, ITrashItem $b) { + return $b->getDeletedTime() - $a->getDeletedTime(); + }); + return $items; + } + + private function getBackendForItem(ITrashItem $item) { + return $item->getTrashBackend(); + } + + public function listTrashFolder(ITrashItem $folder): array { + return $this->getBackendForItem($folder)->listTrashFolder($folder); + } + + public function restoreItem(ITrashItem $item) { + return $this->getBackendForItem($item)->restoreItem($item); + } + + public function removeItem(ITrashItem $item) { + $this->getBackendForItem($item)->removeItem($item); + } + + /** + * @param IStorage $storage + * @return ITrashBackend + * @throws BackendNotFoundException + */ + public function getBackendForStorage(IStorage $storage): ITrashBackend { + $fullType = get_class($storage); + $foundType = array_reduce(array_keys($this->backends), function ($type, $registeredType) use ($storage) { + if ( + $storage->instanceOfStorage($registeredType) + && ($type === '' || is_subclass_of($registeredType, $type)) + ) { + return $registeredType; + } else { + return $type; + } + }, ''); + if ($foundType === '') { + throw new BackendNotFoundException("Trash backend for $fullType not found"); + } else { + return $this->backends[$foundType]; + } + } + + public function moveToTrash(IStorage $storage, string $internalPath): bool { + if ($this->trashPaused) { + return false; + } + try { + $backend = $this->getBackendForStorage($storage); + $this->trashPaused = true; + $result = $backend->moveToTrash($storage, $internalPath); + $this->trashPaused = false; + return $result; + } catch (BackendNotFoundException $e) { + return false; + } + } + + public function getTrashNodeById(IUser $user, int $fileId) { + foreach ($this->backends as $backend) { + $item = $backend->getTrashNodeById($user, $fileId); + if ($item !== null) { + return $item; + } + } + return null; + } + + public function pauseTrash() { + $this->trashPaused = true; + } + + public function resumeTrash() { + $this->trashPaused = false; + } +} |