aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_trashbin/lib/Trash
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_trashbin/lib/Trash')
-rw-r--r--apps/files_trashbin/lib/Trash/BackendNotFoundException.php10
-rw-r--r--apps/files_trashbin/lib/Trash/ITrashBackend.php67
-rw-r--r--apps/files_trashbin/lib/Trash/ITrashItem.php70
-rw-r--r--apps/files_trashbin/lib/Trash/ITrashManager.php41
-rw-r--r--apps/files_trashbin/lib/Trash/LegacyTrashBackend.php121
-rw-r--r--apps/files_trashbin/lib/Trash/TrashItem.php172
-rw-r--r--apps/files_trashbin/lib/Trash/TrashManager.php111
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;
+ }
+}