aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFerdinand Thiessen <opensource@fthiessen.de>2024-07-16 12:39:29 +0200
committerFerdinand Thiessen <opensource@fthiessen.de>2024-07-17 16:17:14 +0200
commit0936af996013ab07fe845fb852b80b3ab1d536f0 (patch)
tree35af0615e55836c53c149b2884e2cde10d8409b5
parent19ba872e9aae98686ad61acd543f9945d8577797 (diff)
downloadnextcloud-server-0936af996013ab07fe845fb852b80b3ab1d536f0.tar.gz
nextcloud-server-0936af996013ab07fe845fb852b80b3ab1d536f0.zip
feat(files): Allow to configure Windows filename compatibility in the settings
This adds an admin setting to toggle Windows filename compatibility. Co-authored-by: Ferdinand Thiessen <opensource@fthiessen.de> Co-authored-by: Louis <louis@chmn.me> Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
-rw-r--r--apps/files/appinfo/info.xml8
-rw-r--r--apps/files/composer/composer/autoload_classmap.php4
-rw-r--r--apps/files/composer/composer/autoload_static.php4
-rw-r--r--apps/files/lib/AppInfo/Application.php9
-rw-r--r--apps/files/lib/Listener/DeclarativeSettingsGetValueEventListener.php39
-rw-r--r--apps/files/lib/Listener/DeclarativeSettingsRegisterFormEventListener.php50
-rw-r--r--apps/files/lib/Listener/DeclarativeSettingsSetValueEventListener.php40
-rw-r--r--apps/files/lib/Service/SettingsService.php63
-rw-r--r--apps/files/lib/Settings/PersonalSettings.php1
-rw-r--r--apps/files/templates/settings-personal.php3
10 files changed, 214 insertions, 7 deletions
diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml
index 4a920b499ce..f3da2057f65 100644
--- a/apps/files/appinfo/info.xml
+++ b/apps/files/appinfo/info.xml
@@ -51,6 +51,10 @@
<command>OCA\Files\Command\Object\Put</command>
</commands>
+ <settings>
+ <personal>OCA\Files\Settings\PersonalSettings</personal>
+ </settings>
+
<activity>
<settings>
<setting>OCA\Files\Activity\Settings\FavoriteAction</setting>
@@ -77,8 +81,4 @@
</navigation>
</navigations>
- <settings>
- <personal>OCA\Files\Settings\PersonalSettings</personal>
- </settings>
-
</info>
diff --git a/apps/files/composer/composer/autoload_classmap.php b/apps/files/composer/composer/autoload_classmap.php
index f7880847ac0..68cdabb3dcd 100644
--- a/apps/files/composer/composer/autoload_classmap.php
+++ b/apps/files/composer/composer/autoload_classmap.php
@@ -58,6 +58,9 @@ return array(
'OCA\\Files\\Event\\LoadSidebar' => $baseDir . '/../lib/Event/LoadSidebar.php',
'OCA\\Files\\Exception\\TransferOwnershipException' => $baseDir . '/../lib/Exception/TransferOwnershipException.php',
'OCA\\Files\\Helper' => $baseDir . '/../lib/Helper.php',
+ 'OCA\\Files\\Listener\\DeclarativeSettingsGetValueEventListener' => $baseDir . '/../lib/Listener/DeclarativeSettingsGetValueEventListener.php',
+ 'OCA\\Files\\Listener\\DeclarativeSettingsRegisterFormEventListener' => $baseDir . '/../lib/Listener/DeclarativeSettingsRegisterFormEventListener.php',
+ 'OCA\\Files\\Listener\\DeclarativeSettingsSetValueEventListener' => $baseDir . '/../lib/Listener/DeclarativeSettingsSetValueEventListener.php',
'OCA\\Files\\Listener\\LoadSearchPluginsListener' => $baseDir . '/../lib/Listener/LoadSearchPluginsListener.php',
'OCA\\Files\\Listener\\LoadSidebarListener' => $baseDir . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files\\Listener\\RenderReferenceEventListener' => $baseDir . '/../lib/Listener/RenderReferenceEventListener.php',
@@ -70,6 +73,7 @@ return array(
'OCA\\Files\\Service\\DirectEditingService' => $baseDir . '/../lib/Service/DirectEditingService.php',
'OCA\\Files\\Service\\LivePhotosService' => $baseDir . '/../lib/Service/LivePhotosService.php',
'OCA\\Files\\Service\\OwnershipTransferService' => $baseDir . '/../lib/Service/OwnershipTransferService.php',
+ 'OCA\\Files\\Service\\SettingsService' => $baseDir . '/../lib/Service/SettingsService.php',
'OCA\\Files\\Service\\TagService' => $baseDir . '/../lib/Service/TagService.php',
'OCA\\Files\\Service\\UserConfig' => $baseDir . '/../lib/Service/UserConfig.php',
'OCA\\Files\\Service\\ViewConfig' => $baseDir . '/../lib/Service/ViewConfig.php',
diff --git a/apps/files/composer/composer/autoload_static.php b/apps/files/composer/composer/autoload_static.php
index 9ba311f2776..ca88e773e4a 100644
--- a/apps/files/composer/composer/autoload_static.php
+++ b/apps/files/composer/composer/autoload_static.php
@@ -73,6 +73,9 @@ class ComposerStaticInitFiles
'OCA\\Files\\Event\\LoadSidebar' => __DIR__ . '/..' . '/../lib/Event/LoadSidebar.php',
'OCA\\Files\\Exception\\TransferOwnershipException' => __DIR__ . '/..' . '/../lib/Exception/TransferOwnershipException.php',
'OCA\\Files\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
+ 'OCA\\Files\\Listener\\DeclarativeSettingsGetValueEventListener' => __DIR__ . '/..' . '/../lib/Listener/DeclarativeSettingsGetValueEventListener.php',
+ 'OCA\\Files\\Listener\\DeclarativeSettingsRegisterFormEventListener' => __DIR__ . '/..' . '/../lib/Listener/DeclarativeSettingsRegisterFormEventListener.php',
+ 'OCA\\Files\\Listener\\DeclarativeSettingsSetValueEventListener' => __DIR__ . '/..' . '/../lib/Listener/DeclarativeSettingsSetValueEventListener.php',
'OCA\\Files\\Listener\\LoadSearchPluginsListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSearchPluginsListener.php',
'OCA\\Files\\Listener\\LoadSidebarListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files\\Listener\\RenderReferenceEventListener' => __DIR__ . '/..' . '/../lib/Listener/RenderReferenceEventListener.php',
@@ -85,6 +88,7 @@ class ComposerStaticInitFiles
'OCA\\Files\\Service\\DirectEditingService' => __DIR__ . '/..' . '/../lib/Service/DirectEditingService.php',
'OCA\\Files\\Service\\LivePhotosService' => __DIR__ . '/..' . '/../lib/Service/LivePhotosService.php',
'OCA\\Files\\Service\\OwnershipTransferService' => __DIR__ . '/..' . '/../lib/Service/OwnershipTransferService.php',
+ 'OCA\\Files\\Service\\SettingsService' => __DIR__ . '/..' . '/../lib/Service/SettingsService.php',
'OCA\\Files\\Service\\TagService' => __DIR__ . '/..' . '/../lib/Service/TagService.php',
'OCA\\Files\\Service\\UserConfig' => __DIR__ . '/..' . '/../lib/Service/UserConfig.php',
'OCA\\Files\\Service\\ViewConfig' => __DIR__ . '/..' . '/../lib/Service/ViewConfig.php',
diff --git a/apps/files/lib/AppInfo/Application.php b/apps/files/lib/AppInfo/Application.php
index 5f57b2e2f34..b4fd04c9fbc 100644
--- a/apps/files/lib/AppInfo/Application.php
+++ b/apps/files/lib/AppInfo/Application.php
@@ -17,6 +17,9 @@ use OCA\Files\Controller\ApiController;
use OCA\Files\DirectEditingCapabilities;
use OCA\Files\Event\LoadSearchPlugins;
use OCA\Files\Event\LoadSidebar;
+use OCA\Files\Listener\DeclarativeSettingsGetValueEventListener;
+use OCA\Files\Listener\DeclarativeSettingsRegisterFormEventListener;
+use OCA\Files\Listener\DeclarativeSettingsSetValueEventListener;
use OCA\Files\Listener\LoadSearchPluginsListener;
use OCA\Files\Listener\LoadSidebarListener;
use OCA\Files\Listener\RenderReferenceEventListener;
@@ -46,6 +49,9 @@ use OCP\ISearch;
use OCP\IServerContainer;
use OCP\ITagManager;
use OCP\IUserSession;
+use OCP\Settings\Events\DeclarativeSettingsGetValueEvent;
+use OCP\Settings\Events\DeclarativeSettingsRegisterFormEvent;
+use OCP\Settings\Events\DeclarativeSettingsSetValueEvent;
use OCP\Share\IManager as IShareManager;
use OCP\Util;
use Psr\Container\ContainerInterface;
@@ -109,6 +115,9 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(BeforeNodeCopiedEvent::class, SyncLivePhotosListener::class);
$context->registerEventListener(NodeCopiedEvent::class, SyncLivePhotosListener::class);
$context->registerEventListener(LoadSearchPlugins::class, LoadSearchPluginsListener::class);
+ $context->registerEventListener(DeclarativeSettingsRegisterFormEvent::class, DeclarativeSettingsRegisterFormEventListener::class);
+ $context->registerEventListener(DeclarativeSettingsGetValueEvent::class, DeclarativeSettingsGetValueEventListener::class);
+ $context->registerEventListener(DeclarativeSettingsSetValueEvent::class, DeclarativeSettingsSetValueEventListener::class);
$context->registerSearchProvider(FilesSearchProvider::class);
diff --git a/apps/files/lib/Listener/DeclarativeSettingsGetValueEventListener.php b/apps/files/lib/Listener/DeclarativeSettingsGetValueEventListener.php
new file mode 100644
index 00000000000..b1d0ee3a395
--- /dev/null
+++ b/apps/files/lib/Listener/DeclarativeSettingsGetValueEventListener.php
@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Files\Listener;
+
+use OCA\Files\AppInfo\Application;
+use OCA\Files\Service\SettingsService;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\Settings\Events\DeclarativeSettingsGetValueEvent;
+
+/** @template-implements IEventListener<DeclarativeSettingsGetValueEvent> */
+class DeclarativeSettingsGetValueEventListener implements IEventListener {
+
+ public function __construct(
+ private SettingsService $service,
+ ) {
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof DeclarativeSettingsGetValueEvent)) {
+ return;
+ }
+
+ if ($event->getApp() !== Application::APP_ID) {
+ return;
+ }
+
+ $event->setValue(
+ match($event->getFieldId()) {
+ 'windows_support' => $this->service->hasFilesWindowsSupport(),
+ }
+ );
+ }
+}
diff --git a/apps/files/lib/Listener/DeclarativeSettingsRegisterFormEventListener.php b/apps/files/lib/Listener/DeclarativeSettingsRegisterFormEventListener.php
new file mode 100644
index 00000000000..51832e89ecb
--- /dev/null
+++ b/apps/files/lib/Listener/DeclarativeSettingsRegisterFormEventListener.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Files\Listener;
+
+use OCA\Files\AppInfo\Application;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\IL10N;
+use OCP\Settings\DeclarativeSettingsTypes;
+use OCP\Settings\Events\DeclarativeSettingsRegisterFormEvent;
+
+/** @template-implements IEventListener<DeclarativeSettingsRegisterFormEvent> */
+class DeclarativeSettingsRegisterFormEventListener implements IEventListener {
+
+ public function __construct(
+ private IL10N $l,
+ ) {
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof DeclarativeSettingsRegisterFormEvent)) {
+ return;
+ }
+
+ $event->registerSchema(Application::APP_ID, [
+ 'id' => 'files-filename-support',
+ 'priority' => 10,
+ 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN,
+ 'section_id' => 'server',
+ 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL,
+ 'title' => $this->l->t('Files compatibility'),
+ 'description' => $this->l->t('Allow to restrict filenames to ensure files can be synced with all clients. By default all filenames valid on POSIX (e.g. Linux or macOS) are allowed.'),
+
+ 'fields' => [
+ [
+ 'id' => 'windows_support',
+ 'title' => $this->l->t('Enforce Windows compatibility'),
+ 'description' => $this->l->t('This will block filenames not valid on Windows systems, like using reserved names or special characters. But this will not enforce compatibility of case sensitivity.'),
+ 'type' => DeclarativeSettingsTypes::CHECKBOX,
+ 'default' => false,
+ ],
+ ],
+ ]);
+ }
+}
diff --git a/apps/files/lib/Listener/DeclarativeSettingsSetValueEventListener.php b/apps/files/lib/Listener/DeclarativeSettingsSetValueEventListener.php
new file mode 100644
index 00000000000..43d01563c26
--- /dev/null
+++ b/apps/files/lib/Listener/DeclarativeSettingsSetValueEventListener.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Files\Listener;
+
+use OCA\Files\AppInfo\Application;
+use OCA\Files\Service\SettingsService;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\Settings\Events\DeclarativeSettingsSetValueEvent;
+
+/** @template-implements IEventListener<DeclarativeSettingsSetValueEvent> */
+class DeclarativeSettingsSetValueEventListener implements IEventListener {
+
+ public function __construct(
+ private SettingsService $service,
+ ) {
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof DeclarativeSettingsSetValueEvent)) {
+ return;
+ }
+
+ if ($event->getApp() !== Application::APP_ID) {
+ return;
+ }
+
+ switch ($event->getFieldId()) {
+ case 'windows_support':
+ $this->service->setFilesWindowsSupport((bool) $event->getValue());
+ $event->stopPropagation();
+ break;
+ }
+ }
+}
diff --git a/apps/files/lib/Service/SettingsService.php b/apps/files/lib/Service/SettingsService.php
new file mode 100644
index 00000000000..d07e907a5f6
--- /dev/null
+++ b/apps/files/lib/Service/SettingsService.php
@@ -0,0 +1,63 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\Files\Service;
+
+use OC\Files\FilenameValidator;
+use OCP\IConfig;
+use Psr\Log\LoggerInterface;
+
+class SettingsService {
+
+ protected const WINDOWS_EXTENSION = [
+ ' ',
+ '.',
+ ];
+
+ protected const WINDOWS_BASENAMES = [
+ 'con', 'prn', 'aux', 'nul', 'com0', 'com1', 'com2', 'com3', 'com4', 'com5',
+ 'com6', 'com7', 'com8', 'com9', 'com¹', 'com²', 'com³', 'lpt0', 'lpt1', 'lpt2',
+ 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9', 'lpt¹', 'lpt²', 'lpt³',
+ ];
+
+ protected const WINDOWS_CHARACTERS = [
+ '<', '>', ':',
+ '"', '|', '?',
+ '*',
+ ];
+
+ public function __construct(
+ private IConfig $config,
+ private FilenameValidator $filenameValidator,
+ private LoggerInterface $logger,
+ ) {
+ }
+
+ public function hasFilesWindowsSupport(): bool {
+ return empty(array_diff(self::WINDOWS_BASENAMES, $this->filenameValidator->getForbiddenBasenames()))
+ && empty(array_diff(self::WINDOWS_CHARACTERS, $this->filenameValidator->getForbiddenCharacters()))
+ && empty(array_diff(self::WINDOWS_EXTENSION, $this->filenameValidator->getForbiddenExtensions()));
+ }
+
+ public function setFilesWindowsSupport(bool $enabled = true): void {
+ if ($enabled) {
+ $basenames = array_unique(array_merge(self::WINDOWS_BASENAMES, $this->filenameValidator->getForbiddenBasenames()));
+ $characters = array_unique(array_merge(self::WINDOWS_CHARACTERS, $this->filenameValidator->getForbiddenCharacters()));
+ $extensions = array_unique(array_merge(self::WINDOWS_EXTENSION, $this->filenameValidator->getForbiddenExtensions()));
+ } else {
+ $basenames = array_unique(array_values(array_diff($this->filenameValidator->getForbiddenBasenames(), self::WINDOWS_BASENAMES)));
+ $characters = array_unique(array_values(array_diff($this->filenameValidator->getForbiddenCharacters(), self::WINDOWS_CHARACTERS)));
+ $extensions = array_unique(array_values(array_diff($this->filenameValidator->getForbiddenExtensions(), self::WINDOWS_EXTENSION)));
+ }
+ $values = [
+ 'forbidden_filename_basenames' => empty($basenames) ? null : $basenames,
+ 'forbidden_filename_characters' => empty($characters) ? null : $characters,
+ 'forbidden_filename_extensions' => empty($extensions) ? null : $extensions,
+ ];
+ $this->config->setSystemValues($values);
+ }
+}
diff --git a/apps/files/lib/Settings/PersonalSettings.php b/apps/files/lib/Settings/PersonalSettings.php
index c70a7171c94..484e4bde2b8 100644
--- a/apps/files/lib/Settings/PersonalSettings.php
+++ b/apps/files/lib/Settings/PersonalSettings.php
@@ -14,6 +14,7 @@ use OCP\Settings\ISettings;
class PersonalSettings implements ISettings {
public function getForm(): TemplateResponse {
+ \OCP\Util::addScript(Application::APP_ID, 'settings-personal');
return new TemplateResponse(Application::APP_ID, 'settings-personal');
}
diff --git a/apps/files/templates/settings-personal.php b/apps/files/templates/settings-personal.php
index 06ea218f3b0..0ca3846d699 100644
--- a/apps/files/templates/settings-personal.php
+++ b/apps/files/templates/settings-personal.php
@@ -4,9 +4,6 @@
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
-script(\OCA\Files\AppInfo\Application::APP_ID, 'personal-settings');
-
?>
<div id="files-personal-settings" class="section">
</div>