diff options
author | Ferdinand Thiessen <opensource@fthiessen.de> | 2024-07-16 12:39:29 +0200 |
---|---|---|
committer | Ferdinand Thiessen <opensource@fthiessen.de> | 2024-07-17 16:17:14 +0200 |
commit | 0936af996013ab07fe845fb852b80b3ab1d536f0 (patch) | |
tree | 35af0615e55836c53c149b2884e2cde10d8409b5 | |
parent | 19ba872e9aae98686ad61acd543f9945d8577797 (diff) | |
download | nextcloud-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>
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> |