aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_trashbin
diff options
context:
space:
mode:
authorprovokateurin <kate@provokateurin.de>2025-01-07 17:02:43 +0100
committerprovokateurin <kate@provokateurin.de>2025-01-13 15:19:19 +0100
commit31c21c779723fb6efbe6aa89b2093091c5d3e187 (patch)
tree08907a7eb60d849ec9ae72d0d894d0be8496b4c6 /apps/files_trashbin
parent26fa4da8c2fe461dd73fd92a5c593eecfc6605e5 (diff)
downloadnextcloud-server-31c21c779723fb6efbe6aa89b2093091c5d3e187.tar.gz
nextcloud-server-31c21c779723fb6efbe6aa89b2093091c5d3e187.zip
feat(files_trashbin): Allow preventing trash to be deleted permanently
Signed-off-by: provokateurin <kate@provokateurin.de>
Diffstat (limited to 'apps/files_trashbin')
-rw-r--r--apps/files_trashbin/composer/composer/autoload_classmap.php2
-rw-r--r--apps/files_trashbin/composer/composer/autoload_static.php2
-rw-r--r--apps/files_trashbin/lib/AppInfo/Application.php7
-rw-r--r--apps/files_trashbin/lib/Capabilities.php11
-rw-r--r--apps/files_trashbin/lib/Listeners/BeforeTemplateRendered.php32
-rw-r--r--apps/files_trashbin/lib/Listeners/LoadAdditionalScripts.php9
-rw-r--r--apps/files_trashbin/lib/Sabre/AbstractTrash.php6
-rw-r--r--apps/files_trashbin/lib/Sabre/TrashRoot.php5
-rw-r--r--apps/files_trashbin/lib/Service/ConfigService.php27
-rw-r--r--apps/files_trashbin/openapi.json6
-rw-r--r--apps/files_trashbin/src/fileListActions/emptyTrashAction.ts11
-rw-r--r--apps/files_trashbin/tests/CapabilitiesTest.php5
12 files changed, 118 insertions, 5 deletions
diff --git a/apps/files_trashbin/composer/composer/autoload_classmap.php b/apps/files_trashbin/composer/composer/autoload_classmap.php
index 4ff7c561748..23e2e7baff6 100644
--- a/apps/files_trashbin/composer/composer/autoload_classmap.php
+++ b/apps/files_trashbin/composer/composer/autoload_classmap.php
@@ -23,6 +23,7 @@ return array(
'OCA\\Files_Trashbin\\Expiration' => $baseDir . '/../lib/Expiration.php',
'OCA\\Files_Trashbin\\Helper' => $baseDir . '/../lib/Helper.php',
'OCA\\Files_Trashbin\\Listener\\EventListener' => $baseDir . '/../lib/Listener/EventListener.php',
+ 'OCA\\Files_Trashbin\\Listeners\\BeforeTemplateRendered' => $baseDir . '/../lib/Listeners/BeforeTemplateRendered.php',
'OCA\\Files_Trashbin\\Listeners\\LoadAdditionalScripts' => $baseDir . '/../lib/Listeners/LoadAdditionalScripts.php',
'OCA\\Files_Trashbin\\Listeners\\SyncLivePhotosListener' => $baseDir . '/../lib/Listeners/SyncLivePhotosListener.php',
'OCA\\Files_Trashbin\\Migration\\Version1010Date20200630192639' => $baseDir . '/../lib/Migration/Version1010Date20200630192639.php',
@@ -40,6 +41,7 @@ return array(
'OCA\\Files_Trashbin\\Sabre\\TrashHome' => $baseDir . '/../lib/Sabre/TrashHome.php',
'OCA\\Files_Trashbin\\Sabre\\TrashRoot' => $baseDir . '/../lib/Sabre/TrashRoot.php',
'OCA\\Files_Trashbin\\Sabre\\TrashbinPlugin' => $baseDir . '/../lib/Sabre/TrashbinPlugin.php',
+ 'OCA\\Files_Trashbin\\Service\\ConfigService' => $baseDir . '/../lib/Service/ConfigService.php',
'OCA\\Files_Trashbin\\Storage' => $baseDir . '/../lib/Storage.php',
'OCA\\Files_Trashbin\\Trash\\BackendNotFoundException' => $baseDir . '/../lib/Trash/BackendNotFoundException.php',
'OCA\\Files_Trashbin\\Trash\\ITrashBackend' => $baseDir . '/../lib/Trash/ITrashBackend.php',
diff --git a/apps/files_trashbin/composer/composer/autoload_static.php b/apps/files_trashbin/composer/composer/autoload_static.php
index 4b09239aa1e..fc604299261 100644
--- a/apps/files_trashbin/composer/composer/autoload_static.php
+++ b/apps/files_trashbin/composer/composer/autoload_static.php
@@ -38,6 +38,7 @@ class ComposerStaticInitFiles_Trashbin
'OCA\\Files_Trashbin\\Expiration' => __DIR__ . '/..' . '/../lib/Expiration.php',
'OCA\\Files_Trashbin\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
'OCA\\Files_Trashbin\\Listener\\EventListener' => __DIR__ . '/..' . '/../lib/Listener/EventListener.php',
+ 'OCA\\Files_Trashbin\\Listeners\\BeforeTemplateRendered' => __DIR__ . '/..' . '/../lib/Listeners/BeforeTemplateRendered.php',
'OCA\\Files_Trashbin\\Listeners\\LoadAdditionalScripts' => __DIR__ . '/..' . '/../lib/Listeners/LoadAdditionalScripts.php',
'OCA\\Files_Trashbin\\Listeners\\SyncLivePhotosListener' => __DIR__ . '/..' . '/../lib/Listeners/SyncLivePhotosListener.php',
'OCA\\Files_Trashbin\\Migration\\Version1010Date20200630192639' => __DIR__ . '/..' . '/../lib/Migration/Version1010Date20200630192639.php',
@@ -55,6 +56,7 @@ class ComposerStaticInitFiles_Trashbin
'OCA\\Files_Trashbin\\Sabre\\TrashHome' => __DIR__ . '/..' . '/../lib/Sabre/TrashHome.php',
'OCA\\Files_Trashbin\\Sabre\\TrashRoot' => __DIR__ . '/..' . '/../lib/Sabre/TrashRoot.php',
'OCA\\Files_Trashbin\\Sabre\\TrashbinPlugin' => __DIR__ . '/..' . '/../lib/Sabre/TrashbinPlugin.php',
+ 'OCA\\Files_Trashbin\\Service\\ConfigService' => __DIR__ . '/..' . '/../lib/Service/ConfigService.php',
'OCA\\Files_Trashbin\\Storage' => __DIR__ . '/..' . '/../lib/Storage.php',
'OCA\\Files_Trashbin\\Trash\\BackendNotFoundException' => __DIR__ . '/..' . '/../lib/Trash/BackendNotFoundException.php',
'OCA\\Files_Trashbin\\Trash\\ITrashBackend' => __DIR__ . '/..' . '/../lib/Trash/ITrashBackend.php',
diff --git a/apps/files_trashbin/lib/AppInfo/Application.php b/apps/files_trashbin/lib/AppInfo/Application.php
index 115e6e418b4..000677de96c 100644
--- a/apps/files_trashbin/lib/AppInfo/Application.php
+++ b/apps/files_trashbin/lib/AppInfo/Application.php
@@ -8,10 +8,12 @@ namespace OCA\Files_Trashbin\AppInfo;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
+use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent;
use OCA\Files_Trashbin\Capabilities;
use OCA\Files_Trashbin\Events\BeforeNodeRestoredEvent;
use OCA\Files_Trashbin\Expiration;
use OCA\Files_Trashbin\Listener\EventListener;
+use OCA\Files_Trashbin\Listeners\BeforeTemplateRendered;
use OCA\Files_Trashbin\Listeners\LoadAdditionalScripts;
use OCA\Files_Trashbin\Listeners\SyncLivePhotosListener;
use OCA\Files_Trashbin\Trash\ITrashManager;
@@ -52,6 +54,11 @@ class Application extends App implements IBootstrap {
LoadAdditionalScripts::class
);
+ $context->registerEventListener(
+ BeforeTemplateRenderedEvent::class,
+ BeforeTemplateRendered::class
+ );
+
$context->registerEventListener(BeforeNodeRestoredEvent::class, SyncLivePhotosListener::class);
$context->registerEventListener(NodeWrittenEvent::class, EventListener::class);
diff --git a/apps/files_trashbin/lib/Capabilities.php b/apps/files_trashbin/lib/Capabilities.php
index 863d3692fb6..62be7bcb1a1 100644
--- a/apps/files_trashbin/lib/Capabilities.php
+++ b/apps/files_trashbin/lib/Capabilities.php
@@ -6,6 +6,7 @@
*/
namespace OCA\Files_Trashbin;
+use OCA\Files_Trashbin\Service\ConfigService;
use OCP\Capabilities\ICapability;
/**
@@ -18,12 +19,18 @@ class Capabilities implements ICapability {
/**
* Return this classes capabilities
*
- * @return array{files: array{undelete: bool}}
+ * @return array{
+ * files: array{
+ * undelete: bool,
+ * delete_from_trash: bool
+ * }
+ * }
*/
public function getCapabilities() {
return [
'files' => [
- 'undelete' => true
+ 'undelete' => true,
+ 'delete_from_trash' => ConfigService::getDeleteFromTrashEnabled(),
]
];
}
diff --git a/apps/files_trashbin/lib/Listeners/BeforeTemplateRendered.php b/apps/files_trashbin/lib/Listeners/BeforeTemplateRendered.php
new file mode 100644
index 00000000000..d62618583f7
--- /dev/null
+++ b/apps/files_trashbin/lib/Listeners/BeforeTemplateRendered.php
@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Files_Trashbin\Listeners;
+
+use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent;
+use OCA\Files_Trashbin\Service\ConfigService;
+use OCP\AppFramework\Services\IInitialState;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+
+/** @template-implements IEventListener<BeforeTemplateRenderedEvent> */
+class BeforeTemplateRendered implements IEventListener {
+ public function __construct(
+ private IInitialState $initialState,
+ ) {
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof BeforeTemplateRenderedEvent)) {
+ return;
+ }
+
+ ConfigService::injectInitialState($this->initialState);
+ }
+}
diff --git a/apps/files_trashbin/lib/Listeners/LoadAdditionalScripts.php b/apps/files_trashbin/lib/Listeners/LoadAdditionalScripts.php
index 4bfa6bbef3f..7940b934ace 100644
--- a/apps/files_trashbin/lib/Listeners/LoadAdditionalScripts.php
+++ b/apps/files_trashbin/lib/Listeners/LoadAdditionalScripts.php
@@ -10,17 +10,26 @@ namespace OCA\Files_Trashbin\Listeners;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Files_Trashbin\AppInfo\Application;
+use OCA\Files_Trashbin\Service\ConfigService;
+use OCP\AppFramework\Services\IInitialState;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Util;
/** @template-implements IEventListener<LoadAdditionalScriptsEvent> */
class LoadAdditionalScripts implements IEventListener {
+ public function __construct(
+ private IInitialState $initialState,
+ ) {
+ }
+
public function handle(Event $event): void {
if (!($event instanceof LoadAdditionalScriptsEvent)) {
return;
}
Util::addInitScript(Application::APP_ID, 'init');
+
+ ConfigService::injectInitialState($this->initialState);
}
}
diff --git a/apps/files_trashbin/lib/Sabre/AbstractTrash.php b/apps/files_trashbin/lib/Sabre/AbstractTrash.php
index 343c8b6be6d..f032395437b 100644
--- a/apps/files_trashbin/lib/Sabre/AbstractTrash.php
+++ b/apps/files_trashbin/lib/Sabre/AbstractTrash.php
@@ -8,10 +8,12 @@ declare(strict_types=1);
*/
namespace OCA\Files_Trashbin\Sabre;
+use OCA\Files_Trashbin\Service\ConfigService;
use OCA\Files_Trashbin\Trash\ITrashItem;
use OCA\Files_Trashbin\Trash\ITrashManager;
use OCP\Files\FileInfo;
use OCP\IUser;
+use Sabre\DAV\Exception\Forbidden;
abstract class AbstractTrash implements ITrash {
public function __construct(
@@ -73,6 +75,10 @@ abstract class AbstractTrash implements ITrash {
}
public function delete() {
+ if (!ConfigService::getDeleteFromTrashEnabled()) {
+ throw new Forbidden('Not allowed to delete items from the trash bin');
+ }
+
$this->trashManager->removeItem($this->data);
}
diff --git a/apps/files_trashbin/lib/Sabre/TrashRoot.php b/apps/files_trashbin/lib/Sabre/TrashRoot.php
index d6a4f5cc67e..dd89583d9a1 100644
--- a/apps/files_trashbin/lib/Sabre/TrashRoot.php
+++ b/apps/files_trashbin/lib/Sabre/TrashRoot.php
@@ -8,6 +8,7 @@ declare(strict_types=1);
*/
namespace OCA\Files_Trashbin\Sabre;
+use OCA\Files_Trashbin\Service\ConfigService;
use OCA\Files_Trashbin\Trash\ITrashItem;
use OCA\Files_Trashbin\Trash\ITrashManager;
use OCA\Files_Trashbin\Trashbin;
@@ -26,6 +27,10 @@ class TrashRoot implements ICollection {
}
public function delete() {
+ if (!ConfigService::getDeleteFromTrashEnabled()) {
+ throw new Forbidden('Not allowed to delete items from the trash bin');
+ }
+
Trashbin::deleteAll();
foreach ($this->trashManager->listTrashRoot($this->user) as $trashItem) {
$this->trashManager->removeItem($trashItem);
diff --git a/apps/files_trashbin/lib/Service/ConfigService.php b/apps/files_trashbin/lib/Service/ConfigService.php
new file mode 100644
index 00000000000..9e7826fe580
--- /dev/null
+++ b/apps/files_trashbin/lib/Service/ConfigService.php
@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OCA\Files_Trashbin\Service;
+
+use OCP\AppFramework\Services\IInitialState;
+use OCP\IConfig;
+use OCP\Server;
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+class ConfigService {
+ public static function getDeleteFromTrashEnabled(): bool {
+ return Server::get(IConfig::class)->getSystemValueBool('files.trash.delete', true);
+ }
+
+ public static function injectInitialState(IInitialState $initialState): void {
+ $initialState->provideLazyInitialState('config', function () {
+ return [
+ 'allow_delete' => ConfigService::getDeleteFromTrashEnabled(),
+ ];
+ });
+ }
+}
diff --git a/apps/files_trashbin/openapi.json b/apps/files_trashbin/openapi.json
index 6662fe10815..716d34db641 100644
--- a/apps/files_trashbin/openapi.json
+++ b/apps/files_trashbin/openapi.json
@@ -29,11 +29,15 @@
"files": {
"type": "object",
"required": [
- "undelete"
+ "undelete",
+ "delete_from_trash"
],
"properties": {
"undelete": {
"type": "boolean"
+ },
+ "delete_from_trash": {
+ "type": "boolean"
}
}
}
diff --git a/apps/files_trashbin/src/fileListActions/emptyTrashAction.ts b/apps/files_trashbin/src/fileListActions/emptyTrashAction.ts
index 25e3083abb9..a77a4ad89f3 100644
--- a/apps/files_trashbin/src/fileListActions/emptyTrashAction.ts
+++ b/apps/files_trashbin/src/fileListActions/emptyTrashAction.ts
@@ -19,6 +19,11 @@ import { logger } from '../logger.ts'
import { generateRemoteUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
import { emit } from '@nextcloud/event-bus'
+import { loadState } from '@nextcloud/initial-state'
+
+export type FilesTrashbinConfigState = {
+ allow_delete: boolean;
+}
const emptyTrash = async (): Promise<boolean> => {
try {
@@ -42,6 +47,12 @@ export const emptyTrashAction = new FileListAction({
if (view.id !== 'trashbin') {
return false
}
+
+ const config = loadState<FilesTrashbinConfigState>('files_trashbin', 'config')
+ if (!config.allow_delete) {
+ return false
+ }
+
return nodes.length > 0 && folder.path === '/'
},
diff --git a/apps/files_trashbin/tests/CapabilitiesTest.php b/apps/files_trashbin/tests/CapabilitiesTest.php
index a2a17ca349e..a5e4e79aefd 100644
--- a/apps/files_trashbin/tests/CapabilitiesTest.php
+++ b/apps/files_trashbin/tests/CapabilitiesTest.php
@@ -17,11 +17,12 @@ class CapabilitiesTest extends TestCase {
parent::setUp();
$this->capabilities = new Capabilities();
}
-
+
public function testGetCapabilities(): void {
$capabilities = [
'files' => [
- 'undelete' => true
+ 'undelete' => true,
+ 'delete_from_trash' => true,
]
];