From 5ef8b353ca7c159f8d9784a52a95cb1fe0e84981 Mon Sep 17 00:00:00 2001 From: Louis Chemineau Date: Thu, 23 Jan 2025 13:08:33 +0100 Subject: [PATCH] feat: Support X-NC-Skip-Trashbin header This is useful for clients that want to directly and permanently delete a file. Signed-off-by: Louis Chemineau --- apps/files_trashbin/lib/Storage.php | 42 ++++++++++++++++------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/apps/files_trashbin/lib/Storage.php b/apps/files_trashbin/lib/Storage.php index a1247eaed78ba..a2855a97ac9d8 100644 --- a/apps/files_trashbin/lib/Storage.php +++ b/apps/files_trashbin/lib/Storage.php @@ -10,12 +10,15 @@ use OC\Files\Storage\Wrapper\Wrapper; use OCA\Files_Trashbin\Events\MoveToTrashEvent; use OCA\Files_Trashbin\Trash\ITrashManager; +use OCP\App\IAppManager; use OCP\Encryption\Exceptions\GenericEncryptionException; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\Storage\IStorage; +use OCP\IRequest; use OCP\IUserManager; +use OCP\Server; use Psr\Log\LoggerInterface; class Storage extends Wrapper { @@ -33,6 +36,7 @@ public function __construct( private ?LoggerInterface $logger = null, private ?IEventDispatcher $eventDispatcher = null, private ?IRootFolder $rootFolder = null, + private ?IRequest $request = null, ) { $this->mountPoint = $parameters['mountPoint']; parent::__construct($parameters); @@ -118,26 +122,26 @@ protected function createMoveToTrashEvent(Node $node): MoveToTrashEvent { * @return bool true if the operation succeeded, false otherwise */ private function doDelete(string $path, string $method): bool { - if ( - !\OC::$server->getAppManager()->isEnabledForUser('files_trashbin') - || (pathinfo($path, PATHINFO_EXTENSION) === 'part') - || $this->shouldMoveToTrash($path) === false - ) { - return call_user_func([$this->storage, $method], $path); - } + $isTrashbinEnabled = Server::get(IAppManager::class)->isEnabledForUser('files_trashbin'); + $isPartFile = pathinfo($path, PATHINFO_EXTENSION) === 'part'; + $isSkipTrashHeaderSet = $this->request !== null && $this->request->getHeader('X-NC-Skip-Trashbin') === 'true'; + // We keep the shouldMoveToTrash call at the end to prevent emitting unnecessary event. + $shouldMoveToTrash = $isTrashbinEnabled && !$isPartFile && !$isSkipTrashHeaderSet && $this->shouldMoveToTrash($path); + + if ($shouldMoveToTrash) { + // check permissions before we continue, this is especially important for + // shared files + if (!$this->isDeletable($path)) { + return false; + } - // check permissions before we continue, this is especially important for - // shared files - if (!$this->isDeletable($path)) { - return false; + $isMovedToTrash = $this->trashManager->moveToTrash($this, $path); + if ($isMovedToTrash) { + return true; + } } - $isMovedToTrash = $this->trashManager->moveToTrash($this, $path); - if (!$isMovedToTrash) { - return call_user_func([$this->storage, $method], $path); - } else { - return true; - } + return call_user_func([$this->storage, $method], $path); } /** @@ -149,9 +153,10 @@ public static function setupStorage(): void { $logger = \OC::$server->get(LoggerInterface::class); $eventDispatcher = \OC::$server->get(IEventDispatcher::class); $rootFolder = \OC::$server->get(IRootFolder::class); + $request = \OC::$server->get(IRequest::class); Filesystem::addStorageWrapper( 'oc_trashbin', - function (string $mountPoint, IStorage $storage) use ($trashManager, $userManager, $logger, $eventDispatcher, $rootFolder) { + function (string $mountPoint, IStorage $storage) use ($trashManager, $userManager, $logger, $eventDispatcher, $rootFolder, $request) { return new Storage( ['storage' => $storage, 'mountPoint' => $mountPoint], $trashManager, @@ -159,6 +164,7 @@ function (string $mountPoint, IStorage $storage) use ($trashManager, $userManage $logger, $eventDispatcher, $rootFolder, + $request, ); }, 1);