diff --git a/typo3/sysext/backend/Classes/Controller/SiteConfigurationController.php b/typo3/sysext/backend/Classes/Controller/SiteConfigurationController.php index 9eea8a9ae0bb08faf0c4a82ff5db46ee2679fe33..83b40ffed1de967c0d91595dd13dfc895009363c 100644 --- a/typo3/sysext/backend/Classes/Controller/SiteConfigurationController.php +++ b/typo3/sysext/backend/Classes/Controller/SiteConfigurationController.php @@ -34,6 +34,7 @@ use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Configuration\Exception\SiteConfigurationWriteException; use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction; use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction; @@ -72,6 +73,7 @@ class SiteConfigurationController private readonly FormDataCompiler $formDataCompiler, private readonly PageRenderer $pageRenderer, private readonly SiteConfiguration $siteConfiguration, + private readonly SiteWriter $siteWriter, ) {} /** @@ -402,13 +404,12 @@ class SiteConfigurationController ); // Persist the configuration - $siteConfigurationManager = GeneralUtility::makeInstance(SiteConfiguration::class); try { if (!$isNewConfiguration && $currentIdentifier !== $siteIdentifier) { - $siteConfigurationManager->rename($currentIdentifier, $siteIdentifier); + $this->siteWriter->rename($currentIdentifier, $siteIdentifier); $this->getBackendUser()->writelog(Type::SITE, SiteAction::RENAME, SystemLogErrorClassification::MESSAGE, 0, 'Site configuration \'%s\' was renamed to \'%s\'.', [$currentIdentifier, $siteIdentifier], 'site'); } - $siteConfigurationManager->write($siteIdentifier, $newSiteConfiguration, true); + $this->siteWriter->write($siteIdentifier, $newSiteConfiguration, true); if ($isNewConfiguration) { $this->getBackendUser()->writelog(Type::SITE, SiteAction::CREATE, SystemLogErrorClassification::MESSAGE, 0, 'Site configuration \'%s\' was created.', [$siteIdentifier], 'site'); } else { @@ -656,7 +657,7 @@ class SiteConfigurationController } try { // Verify site does exist, method throws if not - GeneralUtility::makeInstance(SiteConfiguration::class)->delete($siteIdentifier); + $this->siteWriter->delete($siteIdentifier); $this->getBackendUser()->writelog(Type::SITE, SiteAction::DELETE, SystemLogErrorClassification::MESSAGE, 0, 'Site configuration \'%s\' was deleted.', [$siteIdentifier], 'site'); } catch (SiteConfigurationWriteException $e) { $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $e->getMessage(), '', ContextualFeedbackSeverity::WARNING, true); diff --git a/typo3/sysext/core/Classes/Configuration/Event/SiteConfigurationChangedEvent.php b/typo3/sysext/core/Classes/Configuration/Event/SiteConfigurationChangedEvent.php new file mode 100644 index 0000000000000000000000000000000000000000..264052de11bda2cf3277aacafab0e61af6002484 --- /dev/null +++ b/typo3/sysext/core/Classes/Configuration/Event/SiteConfigurationChangedEvent.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Configuration\Event; + +/** + * @internal + */ +final readonly class SiteConfigurationChangedEvent +{ + public function __construct( + public string $siteIdentifier, + ) {} +} diff --git a/typo3/sysext/core/Classes/Configuration/SiteConfiguration.php b/typo3/sysext/core/Classes/Configuration/SiteConfiguration.php index fc3f084ad31dd199681e5c631094b2ccde44f8c7..91a5eeb13838ae4c21b97cfcab525d524d43e274 100644 --- a/typo3/sysext/core/Classes/Configuration/SiteConfiguration.php +++ b/typo3/sysext/core/Classes/Configuration/SiteConfiguration.php @@ -18,22 +18,19 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Configuration; use Psr\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\Finder\Finder; use Symfony\Component\Yaml\Yaml; +use TYPO3\CMS\Core\Attribute\AsEventListener; use TYPO3\CMS\Core\Cache\Event\CacheWarmupEvent; use TYPO3\CMS\Core\Cache\Exception\InvalidDataException; use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend; -use TYPO3\CMS\Core\Configuration\Event\SiteConfigurationBeforeWriteEvent; +use TYPO3\CMS\Core\Configuration\Event\SiteConfigurationChangedEvent; use TYPO3\CMS\Core\Configuration\Event\SiteConfigurationLoadedEvent; -use TYPO3\CMS\Core\Configuration\Exception\SiteConfigurationWriteException; -use TYPO3\CMS\Core\Configuration\Loader\Exception\YamlPlaceholderException; use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader; -use TYPO3\CMS\Core\Configuration\Loader\YamlPlaceholderGuard; -use TYPO3\CMS\Core\Exception\SiteNotFoundException; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Site\Entity\Site; use TYPO3\CMS\Core\Site\Entity\SiteSettings; -use TYPO3\CMS\Core\Utility\ArrayUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -82,8 +79,10 @@ class SiteConfiguration implements SingletonInterface protected $firstLevelCache; public function __construct( + #[Autowire('%env(TYPO3:configPath)%/sites')] protected string $configPath, protected EventDispatcherInterface $eventDispatcher, + #[Autowire(service: 'cache.core')] protected PhpFrontend $cache ) {} @@ -100,33 +99,6 @@ class SiteConfiguration implements SingletonInterface return $this->resolveAllExistingSites($useCache); } - /** - * Creates a site configuration with one language "English" which is the de-facto default language for TYPO3 in general. - * - * @throws SiteConfigurationWriteException - */ - public function createNewBasicSite(string $identifier, int $rootPageId, string $base): void - { - // Create a default site configuration called "main" as best practice - $this->write($identifier, [ - 'rootPageId' => $rootPageId, - 'base' => $base, - 'languages' => [ - 0 => [ - 'title' => 'English', - 'enabled' => true, - 'languageId' => 0, - 'base' => '/', - 'locale' => 'en_US.UTF-8', - 'navigationTitle' => 'English', - 'flag' => 'us', - ], - ], - 'errorHandling' => [], - 'routes' => [], - ]); - } - /** * Resolve all site objects which have been found in the filesystem. * @@ -277,163 +249,13 @@ class SiteConfiguration implements SingletonInterface return []; } - public function writeSettings(string $siteIdentifier, array $settings): void - { - $fileName = $this->configPath . '/' . $siteIdentifier . '/' . $this->settingsFileName; - $yamlFileContents = Yaml::dump($settings, 99, 2); - if (!GeneralUtility::writeFile($fileName, $yamlFileContents)) { - throw new SiteConfigurationWriteException('Unable to write site settings in sites/' . $siteIdentifier . '/' . $this->configFileName, 1590487411); - } - } - - /** - * Add or update a site configuration - * - * @param bool $protectPlaceholders whether to disallow introducing new placeholders - * @todo enforce $protectPlaceholders with TYPO3 v13.0 - * @throws SiteConfigurationWriteException - */ - public function write(string $siteIdentifier, array $configuration, bool $protectPlaceholders = false): void + #[AsEventListener(event: SiteConfigurationChangedEvent::class)] + public function siteConfigurationChanged() { - $folder = $this->configPath . '/' . $siteIdentifier; - $fileName = $folder . '/' . $this->configFileName; - $newConfiguration = $configuration; - if (!file_exists($folder)) { - GeneralUtility::mkdir_deep($folder); - if ($protectPlaceholders && $newConfiguration !== []) { - $newConfiguration = $this->protectPlaceholders([], $newConfiguration); - } - } elseif (file_exists($fileName)) { - $loader = GeneralUtility::makeInstance(YamlFileLoader::class); - // load without any processing to have the unprocessed base to modify - $newConfiguration = $loader->load(GeneralUtility::fixWindowsFilePath($fileName), 0); - // load the processed configuration to diff changed values - $processed = $loader->load(GeneralUtility::fixWindowsFilePath($fileName)); - // find properties that were modified via GUI - $newModified = array_replace_recursive( - self::findRemoved($processed, $configuration), - self::findModified($processed, $configuration) - ); - if ($protectPlaceholders && $newModified !== []) { - $newModified = $this->protectPlaceholders($newConfiguration, $newModified); - } - // change _only_ the modified keys, leave the original non-changed areas alone - ArrayUtility::mergeRecursiveWithOverrule($newConfiguration, $newModified); - } - $event = $this->eventDispatcher->dispatch(new SiteConfigurationBeforeWriteEvent($siteIdentifier, $newConfiguration)); - $newConfiguration = $this->sortConfiguration($event->getConfiguration()); - $yamlFileContents = Yaml::dump($newConfiguration, 99, 2); - if (!GeneralUtility::writeFile($fileName, $yamlFileContents)) { - throw new SiteConfigurationWriteException('Unable to write site configuration in sites/' . $siteIdentifier . '/' . $this->configFileName, 1590487011); - } $this->firstLevelCache = null; - $this->cache->remove($this->cacheIdentifier); - } - - /** - * Renames a site identifier (and moves the folder) - * - * @throws SiteConfigurationWriteException - */ - public function rename(string $currentIdentifier, string $newIdentifier): void - { - if (!rename($this->configPath . '/' . $currentIdentifier, $this->configPath . '/' . $newIdentifier)) { - throw new SiteConfigurationWriteException('Unable to rename folder sites/' . $currentIdentifier, 1522491300); - } - $this->cache->remove($this->cacheIdentifier); - $this->firstLevelCache = null; - } - - /** - * Removes the config.yaml file of a site configuration. - * Also clears the cache. - * - * @throws SiteNotFoundException|SiteConfigurationWriteException - */ - public function delete(string $siteIdentifier): void - { - $sites = $this->getAllExistingSites(); - if (!isset($sites[$siteIdentifier])) { - throw new SiteNotFoundException('Site configuration named ' . $siteIdentifier . ' not found.', 1522866183); - } - $fileName = $this->configPath . '/' . $siteIdentifier . '/' . $this->configFileName; - if (!file_exists($fileName)) { - throw new SiteNotFoundException('Site configuration file ' . $this->configFileName . ' within the site ' . $siteIdentifier . ' not found.', 1522866184); - } - if (!unlink($fileName)) { - throw new SiteConfigurationWriteException('Unable to delete folder sites/' . $siteIdentifier, 1596462020); - } - $this->cache->remove($this->cacheIdentifier); - $this->firstLevelCache = null; - } - - /** - * Detects placeholders that have been introduced and handles* them. - * (*) currently throws an exception, but could be purged or escaped as well - * - * @param array<string, mixed> $existingConfiguration - * @param array<string, mixed> $modifiedConfiguration - * @return array<string, mixed> sanitized configuration (currently not used, exception thrown before) - * @throws SiteConfigurationWriteException - */ - protected function protectPlaceholders(array $existingConfiguration, array $modifiedConfiguration): array - { - try { - return GeneralUtility::makeInstance(YamlPlaceholderGuard::class, $existingConfiguration) - ->process($modifiedConfiguration); - } catch (YamlPlaceholderException $exception) { - throw new SiteConfigurationWriteException($exception->getMessage(), 1670361271, $exception); - } - } - - protected function sortConfiguration(array $newConfiguration): array - { - ksort($newConfiguration); - if (isset($newConfiguration['imports'])) { - $imports = $newConfiguration['imports']; - unset($newConfiguration['imports']); - $newConfiguration['imports'] = $imports; - } - return $newConfiguration; - } - - protected static function findModified(array $currentConfiguration, array $newConfiguration): array - { - $differences = []; - foreach ($newConfiguration as $key => $value) { - if (!isset($currentConfiguration[$key]) || $currentConfiguration[$key] !== $newConfiguration[$key]) { - if (!isset($newConfiguration[$key]) && isset($currentConfiguration[$key])) { - $differences[$key] = '__UNSET'; - } elseif (isset($currentConfiguration[$key]) - && is_array($newConfiguration[$key]) - && is_array($currentConfiguration[$key]) - ) { - $differences[$key] = self::findModified($currentConfiguration[$key], $newConfiguration[$key]); - } else { - $differences[$key] = $value; - } - } - } - return $differences; - } - - protected static function findRemoved(array $currentConfiguration, array $newConfiguration): array - { - $removed = []; - foreach ($currentConfiguration as $key => $value) { - if (!isset($newConfiguration[$key])) { - $removed[$key] = '__UNSET'; - } elseif (isset($currentConfiguration[$key]) && is_array($currentConfiguration[$key]) && is_array($newConfiguration[$key])) { - $removedInRecursion = self::findRemoved($currentConfiguration[$key], $newConfiguration[$key]); - if (!empty($removedInRecursion)) { - $removed[$key] = $removedInRecursion; - } - } - } - - return $removed; } + #[AsEventListener('typo3-core/site-configuration')] public function warmupCaches(CacheWarmupEvent $event): void { if ($event->hasGroup('system')) { diff --git a/typo3/sysext/core/Classes/Configuration/SiteWriter.php b/typo3/sysext/core/Classes/Configuration/SiteWriter.php new file mode 100644 index 0000000000000000000000000000000000000000..98e951e34f6e2d21003030f6d7dd1523c9e581a3 --- /dev/null +++ b/typo3/sysext/core/Classes/Configuration/SiteWriter.php @@ -0,0 +1,247 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Configuration; + +use Psr\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Yaml\Yaml; +use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend; +use TYPO3\CMS\Core\Configuration\Event\SiteConfigurationBeforeWriteEvent; +use TYPO3\CMS\Core\Configuration\Event\SiteConfigurationChangedEvent; +use TYPO3\CMS\Core\Configuration\Exception\SiteConfigurationWriteException; +use TYPO3\CMS\Core\Configuration\Loader\Exception\YamlPlaceholderException; +use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader; +use TYPO3\CMS\Core\Configuration\Loader\YamlPlaceholderGuard; +use TYPO3\CMS\Core\Exception\SiteNotFoundException; +use TYPO3\CMS\Core\Utility\ArrayUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +/** + * Writes Site objects into site configuration files. + * + * @internal + */ +class SiteWriter +{ + /** + * Config yaml file name. + * + * @internal + */ + protected string $configFileName = 'config.yaml'; + + /** + * YAML file name with all settings. + * + * @internal + * @todo remove, move usages to SiteSettingsFactory + */ + protected string $settingsFileName = 'settings.yaml'; + + /** + * Identifier to store all configuration data in the core cache. + * + * @internal + */ + protected string $cacheIdentifier = 'sites-configuration'; + + public function __construct( + protected string $configPath, + protected EventDispatcherInterface $eventDispatcher, + protected PhpFrontend $cache + ) {} + + /** + * Creates a site configuration with one language "English" which is the de-facto default language for TYPO3 in general. + * + * @throws SiteConfigurationWriteException + */ + public function createNewBasicSite(string $identifier, int $rootPageId, string $base): void + { + // Create a default site configuration called "main" as best practice + $this->write($identifier, [ + 'rootPageId' => $rootPageId, + 'base' => $base, + 'languages' => [ + 0 => [ + 'title' => 'English', + 'enabled' => true, + 'languageId' => 0, + 'base' => '/', + 'locale' => 'en_US.UTF-8', + 'navigationTitle' => 'English', + 'flag' => 'us', + ], + ], + 'errorHandling' => [], + 'routes' => [], + ]); + } + + public function writeSettings(string $siteIdentifier, array $settings): void + { + $fileName = $this->configPath . '/' . $siteIdentifier . '/' . $this->settingsFileName; + $yamlFileContents = Yaml::dump($settings, 99, 2); + if (!GeneralUtility::writeFile($fileName, $yamlFileContents)) { + throw new SiteConfigurationWriteException('Unable to write site settings in sites/' . $siteIdentifier . '/' . $this->configFileName, 1590487411); + } + } + + /** + * Add or update a site configuration + * + * @param bool $protectPlaceholders whether to disallow introducing new placeholders + * @todo enforce $protectPlaceholders with TYPO3 v13.0 + * @throws SiteConfigurationWriteException + */ + public function write(string $siteIdentifier, array $configuration, bool $protectPlaceholders = false): void + { + $folder = $this->configPath . '/' . $siteIdentifier; + $fileName = $folder . '/' . $this->configFileName; + $newConfiguration = $configuration; + if (!file_exists($folder)) { + GeneralUtility::mkdir_deep($folder); + if ($protectPlaceholders && $newConfiguration !== []) { + $newConfiguration = $this->protectPlaceholders([], $newConfiguration); + } + } elseif (file_exists($fileName)) { + $loader = GeneralUtility::makeInstance(YamlFileLoader::class); + // load without any processing to have the unprocessed base to modify + $newConfiguration = $loader->load(GeneralUtility::fixWindowsFilePath($fileName), 0); + // load the processed configuration to diff changed values + $processed = $loader->load(GeneralUtility::fixWindowsFilePath($fileName)); + // find properties that were modified via GUI + $newModified = array_replace_recursive( + self::findRemoved($processed, $configuration), + self::findModified($processed, $configuration) + ); + if ($protectPlaceholders && $newModified !== []) { + $newModified = $this->protectPlaceholders($newConfiguration, $newModified); + } + // change _only_ the modified keys, leave the original non-changed areas alone + ArrayUtility::mergeRecursiveWithOverrule($newConfiguration, $newModified); + } + $event = $this->eventDispatcher->dispatch(new SiteConfigurationBeforeWriteEvent($siteIdentifier, $newConfiguration)); + $newConfiguration = $this->sortConfiguration($event->getConfiguration()); + $yamlFileContents = Yaml::dump($newConfiguration, 99, 2); + if (!GeneralUtility::writeFile($fileName, $yamlFileContents)) { + throw new SiteConfigurationWriteException('Unable to write site configuration in sites/' . $siteIdentifier . '/' . $this->configFileName, 1590487011); + } + $this->cache->remove($this->cacheIdentifier); + $this->eventDispatcher->dispatch(new SiteConfigurationChangedEvent($siteIdentifier)); + } + + /** + * Renames a site identifier (and moves the folder) + * + * @throws SiteConfigurationWriteException + */ + public function rename(string $currentIdentifier, string $newIdentifier): void + { + if (!rename($this->configPath . '/' . $currentIdentifier, $this->configPath . '/' . $newIdentifier)) { + throw new SiteConfigurationWriteException('Unable to rename folder sites/' . $currentIdentifier, 1522491300); + } + $this->cache->remove($this->cacheIdentifier); + $this->eventDispatcher->dispatch(new SiteConfigurationChangedEvent($newIdentifier)); + } + + /** + * Removes the config.yaml file of a site configuration. + * Also clears the cache. + * + * @throws SiteNotFoundException|SiteConfigurationWriteException + */ + public function delete(string $siteIdentifier): void + { + $fileName = $this->configPath . '/' . $siteIdentifier . '/' . $this->configFileName; + if (!file_exists($fileName)) { + throw new SiteNotFoundException('Site configuration file ' . $this->configFileName . ' within the site ' . $siteIdentifier . ' not found.', 1522866184); + } + if (!unlink($fileName)) { + throw new SiteConfigurationWriteException('Unable to delete folder sites/' . $siteIdentifier, 1596462020); + } + $this->cache->remove($this->cacheIdentifier); + $this->eventDispatcher->dispatch(new SiteConfigurationChangedEvent($siteIdentifier)); + } + + /** + * Detects placeholders that have been introduced and handles* them. + * (*) currently throws an exception, but could be purged or escaped as well + * + * @param array<string, mixed> $existingConfiguration + * @param array<string, mixed> $modifiedConfiguration + * @return array<string, mixed> sanitized configuration (currently not used, exception thrown before) + * @throws SiteConfigurationWriteException + */ + protected function protectPlaceholders(array $existingConfiguration, array $modifiedConfiguration): array + { + try { + return GeneralUtility::makeInstance(YamlPlaceholderGuard::class, $existingConfiguration) + ->process($modifiedConfiguration); + } catch (YamlPlaceholderException $exception) { + throw new SiteConfigurationWriteException($exception->getMessage(), 1670361271, $exception); + } + } + + protected function sortConfiguration(array $newConfiguration): array + { + ksort($newConfiguration); + if (isset($newConfiguration['imports'])) { + $imports = $newConfiguration['imports']; + unset($newConfiguration['imports']); + $newConfiguration['imports'] = $imports; + } + return $newConfiguration; + } + + protected static function findModified(array $currentConfiguration, array $newConfiguration): array + { + $differences = []; + foreach ($newConfiguration as $key => $value) { + if (!isset($currentConfiguration[$key]) || $currentConfiguration[$key] !== $newConfiguration[$key]) { + if (!isset($newConfiguration[$key]) && isset($currentConfiguration[$key])) { + $differences[$key] = '__UNSET'; + } elseif (isset($currentConfiguration[$key]) + && is_array($newConfiguration[$key]) + && is_array($currentConfiguration[$key]) + ) { + $differences[$key] = self::findModified($currentConfiguration[$key], $newConfiguration[$key]); + } else { + $differences[$key] = $value; + } + } + } + return $differences; + } + + protected static function findRemoved(array $currentConfiguration, array $newConfiguration): array + { + $removed = []; + foreach ($currentConfiguration as $key => $value) { + if (!isset($newConfiguration[$key])) { + $removed[$key] = '__UNSET'; + } elseif (isset($currentConfiguration[$key]) && is_array($currentConfiguration[$key]) && is_array($newConfiguration[$key])) { + $removedInRecursion = self::findRemoved($currentConfiguration[$key], $newConfiguration[$key]); + if (!empty($removedInRecursion)) { + $removed[$key] = $removedInRecursion; + } + } + } + + return $removed; + } +} diff --git a/typo3/sysext/core/Classes/Hooks/CreateSiteConfiguration.php b/typo3/sysext/core/Classes/Hooks/CreateSiteConfiguration.php index 882eb61b965b83a2eca19f7a110283dd4bf962c8..aaa137d96105b8b3d71d132d5b9bf8f15dd67ba8 100644 --- a/typo3/sysext/core/Classes/Hooks/CreateSiteConfiguration.php +++ b/typo3/sysext/core/Classes/Hooks/CreateSiteConfiguration.php @@ -19,7 +19,7 @@ namespace TYPO3\CMS\Core\Hooks; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Configuration\Exception\SiteConfigurationWriteException; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\Domain\Repository\PageRepository; @@ -87,11 +87,11 @@ class CreateSiteConfiguration $siteIdentifier = $entryPoint . '-' . md5((string)$pageId); if (!$this->siteExistsByRootPageId($pageId)) { - $siteConfiguration = GeneralUtility::makeInstance(SiteConfiguration::class); + $siteWriter = GeneralUtility::makeInstance(SiteWriter::class); $normalizedParams = $this->getNormalizedParams(); $basePrefix = Environment::isCli() ? $normalizedParams->getSitePath() : $normalizedParams->getSiteUrl(); try { - $siteConfiguration->createNewBasicSite( + $siteWriter->createNewBasicSite( $siteIdentifier, $pageId, $basePrefix . $entryPoint diff --git a/typo3/sysext/core/Classes/ServiceProvider.php b/typo3/sysext/core/Classes/ServiceProvider.php index 44cf4125dca30cbe5809acf168eefedf225bc62d..e24c81bcf79053db5599c4ec5c71aa376866eaee 100644 --- a/typo3/sysext/core/Classes/ServiceProvider.php +++ b/typo3/sysext/core/Classes/ServiceProvider.php @@ -59,7 +59,7 @@ class ServiceProvider extends AbstractServiceProvider Database\DriverMiddlewareService::class => self::getDriverMiddlewaresService(...), Charset\CharsetConverter::class => self::getCharsetConverter(...), Configuration\Loader\YamlFileLoader::class => self::getYamlFileLoader(...), - Configuration\SiteConfiguration::class => self::getSiteConfiguration(...), + Configuration\SiteWriter::class => self::getSiteWriter(...), Command\ListCommand::class => self::getListCommand(...), HelpCommand::class => self::getHelpCommand(...), Command\CacheFlushCommand::class => self::getCacheFlushCommand(...), @@ -184,9 +184,9 @@ class ServiceProvider extends AbstractServiceProvider return self::new($container, Configuration\Loader\YamlFileLoader::class); } - public static function getSiteConfiguration(ContainerInterface $container): Configuration\SiteConfiguration + public static function getSiteWriter(ContainerInterface $container): Configuration\SiteWriter { - return self::new($container, Configuration\SiteConfiguration::class, [ + return self::new($container, Configuration\SiteWriter::class, [ Environment::getConfigPath() . '/sites', $container->get(EventDispatcherInterface::class), $container->get('cache.core'), @@ -269,7 +269,6 @@ class ServiceProvider extends AbstractServiceProvider ); $cacheWarmers = [ - Configuration\SiteConfiguration::class, Http\MiddlewareStackResolver::class, Imaging\IconRegistry::class, Package\PackageManager::class, diff --git a/typo3/sysext/core/Configuration/Services.yaml b/typo3/sysext/core/Configuration/Services.yaml index 138be5a334626967dfe5720e17f0c2bc06e71f76..55091672a1bc17624643fafda80a477d72a1f9fc 100644 --- a/typo3/sysext/core/Configuration/Services.yaml +++ b/typo3/sysext/core/Configuration/Services.yaml @@ -56,11 +56,6 @@ services: TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools: public: true - TYPO3\CMS\Core\Configuration\SiteConfiguration: - arguments: - $cache: '@cache.core' - $configPath: '%env(TYPO3:configPath)%/sites' - TYPO3\CMS\Core\Package\UnitTestPackageManager: autoconfigure: false diff --git a/typo3/sysext/core/Tests/Functional/Routing/Aspect/PersistedAliasMapperTest.php b/typo3/sysext/core/Tests/Functional/Routing/Aspect/PersistedAliasMapperTest.php index bc8f79d14366d1faabb49bf992b621e3bfb210a1..568cec75a7da8dab6b8074b39dfb2895fa8fd010 100644 --- a/typo3/sysext/core/Tests/Functional/Routing/Aspect/PersistedAliasMapperTest.php +++ b/typo3/sysext/core/Tests/Functional/Routing/Aspect/PersistedAliasMapperTest.php @@ -20,7 +20,7 @@ namespace TYPO3\CMS\Core\Tests\Functional\Routing\Aspect; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use Psr\EventDispatcher\EventDispatcherInterface; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Context\DateTimeAspect; use TYPO3\CMS\Core\Context\UserAspect; @@ -309,7 +309,7 @@ final class PersistedAliasMapperTest extends FunctionalTestCase $cache = $this->get('cache.core'); $eventDispatcher = $this->get(EventDispatcherInterface::class); GeneralUtility::rmdir($path . '/' . $site->getIdentifier(), true); - GeneralUtility::makeInstance(SiteConfiguration::class, $path, $eventDispatcher, $cache)->write($site->getIdentifier(), $site->getConfiguration()); + GeneralUtility::makeInstance(SiteWriter::class, $path, $eventDispatcher, $cache)->write($site->getIdentifier(), $site->getConfiguration()); } catch (\Exception $exception) { self::markTestSkipped($exception->getMessage()); } diff --git a/typo3/sysext/core/Tests/Functional/Routing/Aspect/PersistedPatternMapperTest.php b/typo3/sysext/core/Tests/Functional/Routing/Aspect/PersistedPatternMapperTest.php index 66e9169c843dd107c58b18279f3c9557ab902fca..568f1bb68e32a9814f6206ccf9f15c460f4f601b 100644 --- a/typo3/sysext/core/Tests/Functional/Routing/Aspect/PersistedPatternMapperTest.php +++ b/typo3/sysext/core/Tests/Functional/Routing/Aspect/PersistedPatternMapperTest.php @@ -20,7 +20,7 @@ namespace TYPO3\CMS\Core\Tests\Functional\Routing\Aspect; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use Psr\EventDispatcher\EventDispatcherInterface; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Context\DateTimeAspect; use TYPO3\CMS\Core\Context\UserAspect; @@ -317,7 +317,7 @@ final class PersistedPatternMapperTest extends FunctionalTestCase $cache = $this->get('cache.core'); $eventDispatcher = $this->get(EventDispatcherInterface::class); GeneralUtility::rmdir($path . '/' . $site->getIdentifier(), true); - GeneralUtility::makeInstance(SiteConfiguration::class, $path, $eventDispatcher, $cache)->write($site->getIdentifier(), $site->getConfiguration()); + GeneralUtility::makeInstance(SiteWriter::class, $path, $eventDispatcher, $cache)->write($site->getIdentifier(), $site->getConfiguration()); } catch (\Exception $exception) { self::markTestSkipped($exception->getMessage()); } diff --git a/typo3/sysext/core/Tests/Functional/SiteHandling/SiteBasedTestTrait.php b/typo3/sysext/core/Tests/Functional/SiteHandling/SiteBasedTestTrait.php index 0b8c55a1fb0fc223a319a3824d5c76449c67f4ba..81a6ab09e5a37f741a3c1bc8c2894c15dbef870d 100644 --- a/typo3/sysext/core/Tests/Functional/SiteHandling/SiteBasedTestTrait.php +++ b/typo3/sysext/core/Tests/Functional/SiteHandling/SiteBasedTestTrait.php @@ -18,6 +18,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Functional\SiteHandling; use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Tests\Functional\Fixtures\Frontend\PhpError; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Internal\AbstractInstruction; @@ -59,11 +60,11 @@ trait SiteBasedTestTrait if (!empty($errorHandling)) { $configuration['errorHandling'] = $errorHandling; } - $siteConfiguration = $this->get(SiteConfiguration::class); + $siteWriter = $this->get(SiteWriter::class); try { // ensure no previous site configuration influences the test GeneralUtility::rmdir($this->instancePath . '/typo3conf/sites/' . $identifier, true); - $siteConfiguration->write($identifier, $configuration); + $siteWriter->write($identifier, $configuration); } catch (\Exception $exception) { $this->markTestSkipped($exception->getMessage()); } @@ -74,10 +75,11 @@ trait SiteBasedTestTrait array $overrides ): void { $siteConfiguration = $this->get(SiteConfiguration::class); + $siteWriter = $this->get(SiteWriter::class); $configuration = $siteConfiguration->load($identifier); $configuration = array_merge($configuration, $overrides); try { - $siteConfiguration->write($identifier, $configuration); + $siteWriter->write($identifier, $configuration); } catch (\Exception $exception) { $this->markTestSkipped($exception->getMessage()); } diff --git a/typo3/sysext/core/Tests/Unit/Configuration/SiteConfigurationTest.php b/typo3/sysext/core/Tests/Unit/Configuration/SiteConfigurationTest.php index eb00bcc3cd0b774b649b6d7fcc573b043eb1ca84..c02f46f10b817d1cb590acfa714dee90ad95f495 100644 --- a/typo3/sysext/core/Tests/Unit/Configuration/SiteConfigurationTest.php +++ b/typo3/sysext/core/Tests/Unit/Configuration/SiteConfigurationTest.php @@ -17,12 +17,9 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Unit\Configuration; -use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use Symfony\Component\Yaml\Yaml; use TYPO3\CMS\Core\Cache\Frontend\NullFrontend; -use TYPO3\CMS\Core\Configuration\Exception\SiteConfigurationWriteException; -use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader; use TYPO3\CMS\Core\Configuration\SiteConfiguration; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\EventDispatcher\NoopEventDispatcher; @@ -80,110 +77,4 @@ final class SiteConfigurationTest extends UnitTestCase self::assertSame(42, $currentSite->getRootPageId()); self::assertEquals(new Uri('https://example.com'), $currentSite->getBase()); } - - #[Test] - public function writeOnlyWritesModifiedKeys(): void - { - $identifier = 'testsite'; - GeneralUtility::mkdir_deep($this->fixturePath . '/' . $identifier); - $configFixture = __DIR__ . '/Fixtures/SiteConfigs/config1.yaml'; - $expected = __DIR__ . '/Fixtures/SiteConfigs/config1_expected.yaml'; - $siteConfig = $this->fixturePath . '/' . $identifier . '/config.yaml'; - copy($configFixture, $siteConfig); - - // load with resolved imports as the module does - $configuration = GeneralUtility::makeInstance(YamlFileLoader::class) - ->load( - GeneralUtility::fixWindowsFilePath($siteConfig), - YamlFileLoader::PROCESS_IMPORTS - ); - // modify something on base level - $configuration['base'] = 'https://example.net/'; - // modify something nested - $configuration['languages'][0]['title'] = 'English'; - // delete values - unset($configuration['someOtherValue'], $configuration['languages'][1]); - - $this->siteConfiguration->write($identifier, $configuration, true); - - // expect modified base but intact imports - self::assertFileEquals($expected, $siteConfig); - } - - #[Test] - public function writingOfNestedStructuresPreservesOrder(): void - { - $identifier = 'testsite'; - GeneralUtility::mkdir_deep($this->fixturePath . '/' . $identifier); - $configFixture = __DIR__ . '/Fixtures/SiteConfigs/config2.yaml'; - $expected = __DIR__ . '/Fixtures/SiteConfigs/config2_expected.yaml'; - $siteConfig = $this->fixturePath . '/' . $identifier . '/config.yaml'; - copy($configFixture, $siteConfig); - - // load with resolved imports as the module does - $configuration = GeneralUtility::makeInstance(YamlFileLoader::class) - ->load( - GeneralUtility::fixWindowsFilePath($siteConfig), - YamlFileLoader::PROCESS_IMPORTS - ); - // add new language - $languageConfig = [ - 'title' => 'English', - 'enabled' => true, - 'languageId' => '0', - 'base' => '/en', - 'locale' => 'en_US.utf8', - 'flag' => 'en', - 'navigationTitle' => 'English', - ]; - array_unshift($configuration['languages'], $languageConfig); - $this->siteConfiguration->write($identifier, $configuration, true); - - // expect modified base but intact imports - self::assertFileEquals($expected, $siteConfig); - } - - public static function writingPlaceholdersIsHandledDataProvider(): \Generator - { - yield 'unchanged' => [ - ['customProperty' => 'Using %env("existing")% variable'], - false, - ]; - yield 'removed placeholder variable' => [ - ['customProperty' => 'Not using any variable'], - false, - ]; - yield 'changed raw text only' => [ - ['customProperty' => 'Using %env("existing")% variable from system environment'], - false, - ]; - yield 'added new placeholder variable' => [ - ['customProperty' => 'Using %env("existing")% and %env("secret")% variable'], - true, - ]; - } - - #[DataProvider('writingPlaceholdersIsHandledDataProvider')] - #[Test] - public function writingPlaceholdersIsHandled(array $changes, bool $expectedException): void - { - if ($expectedException) { - $this->expectException(SiteConfigurationWriteException::class); - $this->expectExceptionCode(1670361271); - } - - $identifier = 'testsite'; - GeneralUtility::mkdir_deep($this->fixturePath . '/' . $identifier); - $configFixture = __DIR__ . '/Fixtures/SiteConfigs/config2.yaml'; - $siteConfig = $this->fixturePath . '/' . $identifier . '/config.yaml'; - copy($configFixture, $siteConfig); - // load with resolved imports as the module does - $configuration = GeneralUtility::makeInstance(YamlFileLoader::class) - ->load( - GeneralUtility::fixWindowsFilePath($siteConfig), - YamlFileLoader::PROCESS_IMPORTS - ); - $configuration = array_merge($configuration, $changes); - $this->siteConfiguration->write($identifier, $configuration, true); - } } diff --git a/typo3/sysext/core/Tests/Unit/Configuration/SiteWriterTest.php b/typo3/sysext/core/Tests/Unit/Configuration/SiteWriterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..df8cae6f2f2043f5cf4019a725d4e9098786640f --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Configuration/SiteWriterTest.php @@ -0,0 +1,164 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Tests\Unit\Configuration; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; +use TYPO3\CMS\Core\Cache\Frontend\NullFrontend; +use TYPO3\CMS\Core\Configuration\Exception\SiteConfigurationWriteException; +use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader; +use TYPO3\CMS\Core\Configuration\SiteWriter; +use TYPO3\CMS\Core\Core\Environment; +use TYPO3\CMS\Core\EventDispatcher\NoopEventDispatcher; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\TestingFramework\Core\Unit\UnitTestCase; + +final class SiteWriterTest extends UnitTestCase +{ + protected bool $resetSingletonInstances = true; + + protected ?SiteWriter $siteWriter; + + /** + * store temporarily used files here + * will be removed after each test + */ + protected ?string $fixturePath; + + protected function setUp(): void + { + parent::setUp(); + $basePath = Environment::getVarPath() . '/tests/unit'; + $this->fixturePath = $basePath . '/fixture/config/sites'; + if (!file_exists($this->fixturePath)) { + GeneralUtility::mkdir_deep($this->fixturePath); + } + $this->testFilesToDelete[] = $basePath; + $this->siteWriter = new SiteWriter( + $this->fixturePath, + new NoopEventDispatcher(), + new NullFrontend('test') + ); + } + + #[Test] + public function writeOnlyWritesModifiedKeys(): void + { + $identifier = 'testsite'; + GeneralUtility::mkdir_deep($this->fixturePath . '/' . $identifier); + $configFixture = __DIR__ . '/Fixtures/SiteConfigs/config1.yaml'; + $expected = __DIR__ . '/Fixtures/SiteConfigs/config1_expected.yaml'; + $siteConfig = $this->fixturePath . '/' . $identifier . '/config.yaml'; + copy($configFixture, $siteConfig); + + // load with resolved imports as the module does + $configuration = GeneralUtility::makeInstance(YamlFileLoader::class) + ->load( + GeneralUtility::fixWindowsFilePath($siteConfig), + YamlFileLoader::PROCESS_IMPORTS + ); + // modify something on base level + $configuration['base'] = 'https://example.net/'; + // modify something nested + $configuration['languages'][0]['title'] = 'English'; + // delete values + unset($configuration['someOtherValue'], $configuration['languages'][1]); + + $this->siteWriter->write($identifier, $configuration, true); + + // expect modified base but intact imports + self::assertFileEquals($expected, $siteConfig); + } + + #[Test] + public function writingOfNestedStructuresPreservesOrder(): void + { + $identifier = 'testsite'; + GeneralUtility::mkdir_deep($this->fixturePath . '/' . $identifier); + $configFixture = __DIR__ . '/Fixtures/SiteConfigs/config2.yaml'; + $expected = __DIR__ . '/Fixtures/SiteConfigs/config2_expected.yaml'; + $siteConfig = $this->fixturePath . '/' . $identifier . '/config.yaml'; + copy($configFixture, $siteConfig); + + // load with resolved imports as the module does + $configuration = GeneralUtility::makeInstance(YamlFileLoader::class) + ->load( + GeneralUtility::fixWindowsFilePath($siteConfig), + YamlFileLoader::PROCESS_IMPORTS + ); + // add new language + $languageConfig = [ + 'title' => 'English', + 'enabled' => true, + 'languageId' => '0', + 'base' => '/en', + 'locale' => 'en_US.utf8', + 'flag' => 'en', + 'navigationTitle' => 'English', + ]; + array_unshift($configuration['languages'], $languageConfig); + $this->siteWriter->write($identifier, $configuration, true); + + // expect modified base but intact imports + self::assertFileEquals($expected, $siteConfig); + } + + public static function writingPlaceholdersIsHandledDataProvider(): \Generator + { + yield 'unchanged' => [ + ['customProperty' => 'Using %env("existing")% variable'], + false, + ]; + yield 'removed placeholder variable' => [ + ['customProperty' => 'Not using any variable'], + false, + ]; + yield 'changed raw text only' => [ + ['customProperty' => 'Using %env("existing")% variable from system environment'], + false, + ]; + yield 'added new placeholder variable' => [ + ['customProperty' => 'Using %env("existing")% and %env("secret")% variable'], + true, + ]; + } + + #[DataProvider('writingPlaceholdersIsHandledDataProvider')] + #[Test] + public function writingPlaceholdersIsHandled(array $changes, bool $expectedException): void + { + if ($expectedException) { + $this->expectException(SiteConfigurationWriteException::class); + $this->expectExceptionCode(1670361271); + } + + $identifier = 'testsite'; + GeneralUtility::mkdir_deep($this->fixturePath . '/' . $identifier); + $configFixture = __DIR__ . '/Fixtures/SiteConfigs/config2.yaml'; + $siteConfig = $this->fixturePath . '/' . $identifier . '/config.yaml'; + copy($configFixture, $siteConfig); + // load with resolved imports as the module does + $configuration = GeneralUtility::makeInstance(YamlFileLoader::class) + ->load( + GeneralUtility::fixWindowsFilePath($siteConfig), + YamlFileLoader::PROCESS_IMPORTS + ); + $configuration = array_merge($configuration, $changes); + $this->siteWriter->write($identifier, $configuration, true); + } +} diff --git a/typo3/sysext/impexp/Classes/Initialization/ImportSiteConfigurationsOnPackageInitialization.php b/typo3/sysext/impexp/Classes/Initialization/ImportSiteConfigurationsOnPackageInitialization.php index 73854190c009416af262e84890c2eed53d491b2a..50142bbcec2710c462872d78375d41e04d20c11b 100644 --- a/typo3/sysext/impexp/Classes/Initialization/ImportSiteConfigurationsOnPackageInitialization.php +++ b/typo3/sysext/impexp/Classes/Initialization/ImportSiteConfigurationsOnPackageInitialization.php @@ -23,6 +23,7 @@ use Symfony\Component\Finder\Finder; use TYPO3\CMS\Core\Attribute\AsEventListener; use TYPO3\CMS\Core\Configuration\Exception\SiteConfigurationWriteException; use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Package\Event\PackageInitializationEvent; use TYPO3\CMS\Core\Registry; @@ -39,6 +40,7 @@ final class ImportSiteConfigurationsOnPackageInitialization implements LoggerAwa public function __construct( private readonly Registry $registry, private readonly SiteConfiguration $siteConfiguration, + private readonly SiteWriter $siteWriter, ) {} #[AsEventListener(after: ImportContentOnPackageInitialization::class)] @@ -98,7 +100,7 @@ final class ImportSiteConfigurationsOnPackageInitialization implements LoggerAwa $configuration = $this->siteConfiguration->load($siteIdentifier); $configuration['rootPageId'] = $importedPageId; try { - $this->siteConfiguration->write($siteIdentifier, $configuration); + $this->siteWriter->write($siteIdentifier, $configuration); } catch (SiteConfigurationWriteException $e) { $this->logger->warning( sprintf( diff --git a/typo3/sysext/install/Classes/Service/SetupService.php b/typo3/sysext/install/Classes/Service/SetupService.php index 72d64aacf70bc0e94a099d88840411574258036e..40577f466d4670300859bc34923112f805b1219d 100644 --- a/typo3/sysext/install/Classes/Service/SetupService.php +++ b/typo3/sysext/install/Classes/Service/SetupService.php @@ -20,7 +20,7 @@ namespace TYPO3\CMS\Install\Service; use TYPO3\CMS\Core\Configuration\ConfigurationManager; use TYPO3\CMS\Core\Configuration\Exception\SiteConfigurationWriteException; use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Crypto\PasswordHashing\Argon2idPasswordHash; use TYPO3\CMS\Core\Crypto\PasswordHashing\Argon2iPasswordHash; use TYPO3\CMS\Core\Crypto\PasswordHashing\BcryptPasswordHash; @@ -42,7 +42,7 @@ readonly class SetupService { public function __construct( private ConfigurationManager $configurationManager, - private SiteConfiguration $siteConfiguration, + private SiteWriter $siteWriter, private YamlFileLoader $yamlFileLoader, ) {} @@ -58,7 +58,7 @@ readonly class SetupService public function createSiteConfiguration(string $identifier, int $rootPageId, string $siteUrl): void { // Create a default site configuration called "main" as best practice - $this->siteConfiguration->createNewBasicSite($identifier, $rootPageId, $siteUrl); + $this->siteWriter->createNewBasicSite($identifier, $rootPageId, $siteUrl); } /** diff --git a/typo3/sysext/install/Classes/ServiceProvider.php b/typo3/sysext/install/Classes/ServiceProvider.php index 13e7f2783f1aad2757e64f532d7ef665dd15c16c..45acdc612da63c344b94d078e8efa28c059642ab 100644 --- a/typo3/sysext/install/Classes/ServiceProvider.php +++ b/typo3/sysext/install/Classes/ServiceProvider.php @@ -21,7 +21,7 @@ use Psr\Container\ContainerInterface; use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Core\Configuration\ConfigurationManager; use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Console\CommandRegistry; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Crypto\HashService; @@ -236,7 +236,7 @@ class ServiceProvider extends AbstractServiceProvider { return new Service\SetupService( $container->get(ConfigurationManager::class), - $container->get(SiteConfiguration::class), + $container->get(SiteWriter::class), $container->get(YamlFileLoader::class) ); } diff --git a/typo3/sysext/install/Classes/Updates/MigrateSiteSettingsConfigUpdate.php b/typo3/sysext/install/Classes/Updates/MigrateSiteSettingsConfigUpdate.php index 861386c787e8b7ad0299f7933395cfe29c93907f..f0162e605b66f8b4f329cc05deb241db6e9e0a3c 100644 --- a/typo3/sysext/install/Classes/Updates/MigrateSiteSettingsConfigUpdate.php +++ b/typo3/sysext/install/Classes/Updates/MigrateSiteSettingsConfigUpdate.php @@ -19,6 +19,7 @@ namespace TYPO3\CMS\Install\Updates; use TYPO3\CMS\Core\Configuration\Exception\SiteConfigurationWriteException; use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Install\Attribute\UpgradeWizard; @@ -34,11 +35,13 @@ class MigrateSiteSettingsConfigUpdate implements UpgradeWizardInterface protected const SETTINGS_FILENAME = 'settings.yaml'; protected ?SiteConfiguration $siteConfiguration = null; + protected ?SiteWriter $siteWriter = null; protected array $sitePathsToMigrate = []; public function __construct() { $this->siteConfiguration = GeneralUtility::makeInstance(SiteConfiguration::class); + $this->siteWriter = GeneralUtility::makeInstance(SiteWriter::class); $this->sitePathsToMigrate = $this->getSitePathsToMigrate(); } @@ -58,7 +61,7 @@ class MigrateSiteSettingsConfigUpdate implements UpgradeWizardInterface { try { foreach ($this->sitePathsToMigrate as $siteIdentifier => $settings) { - $this->siteConfiguration->writeSettings($siteIdentifier, $settings); + $this->siteWriter->writeSettings($siteIdentifier, $settings); } } catch (SiteConfigurationWriteException $e) { return false; diff --git a/typo3/sysext/install/Tests/Functional/Updates/MigrateSiteSettingsConfigUpdateTest.php b/typo3/sysext/install/Tests/Functional/Updates/MigrateSiteSettingsConfigUpdateTest.php index 76e4ddba4b2828460c117870002b575581391878..96fb9cec1cbf55120603b688b4a4481a4c81ff23 100644 --- a/typo3/sysext/install/Tests/Functional/Updates/MigrateSiteSettingsConfigUpdateTest.php +++ b/typo3/sysext/install/Tests/Functional/Updates/MigrateSiteSettingsConfigUpdateTest.php @@ -18,9 +18,8 @@ declare(strict_types=1); namespace TYPO3\CMS\Install\Tests\Functional\Updates; use PHPUnit\Framework\Attributes\Test; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Core\Environment; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Install\Updates\MigrateSiteSettingsConfigUpdate; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; @@ -31,7 +30,7 @@ final class MigrateSiteSettingsConfigUpdateTest extends FunctionalTestCase { $siteconfigurationIdentifier = 'settings'; - GeneralUtility::makeInstance(SiteConfiguration::class)->write( + $this->get(SiteWriter::class)->write( $siteconfigurationIdentifier, [ 'rootPageId' => 1, @@ -66,7 +65,7 @@ final class MigrateSiteSettingsConfigUpdateTest extends FunctionalTestCase { $siteconfigurationIdentifier = 'withoutSettings'; - GeneralUtility::makeInstance(SiteConfiguration::class)->write( + $this->get(SiteWriter::class)->write( $siteconfigurationIdentifier, [ 'rootPageId' => 2, diff --git a/typo3/sysext/redirects/Tests/Functional/EventListener/AddPageTypeZeroSourceTest.php b/typo3/sysext/redirects/Tests/Functional/EventListener/AddPageTypeZeroSourceTest.php index c6add9614f44ca24acf16ea271922c4c6de7ec51..80092641180e3571a6c6fd797092804c91acfbf4 100644 --- a/typo3/sysext/redirects/Tests/Functional/EventListener/AddPageTypeZeroSourceTest.php +++ b/typo3/sysext/redirects/Tests/Functional/EventListener/AddPageTypeZeroSourceTest.php @@ -20,7 +20,7 @@ namespace TYPO3\CMS\Redirects\Tests\Functional\EventListener; use PHPUnit\Framework\Attributes\Test; use Psr\EventDispatcher\EventDispatcherInterface; use Symfony\Component\DependencyInjection\Container; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\EventDispatcher\ListenerProvider; use TYPO3\CMS\Core\Routing\InvalidRouteArgumentsException; @@ -250,7 +250,7 @@ final class AddPageTypeZeroSourceTest extends FunctionalTestCase protected function buildSite(array $configuration): void { - $siteConfiguration = GeneralUtility::makeInstance(SiteConfiguration::class); - $siteConfiguration->write('testing', $configuration); + $siteWriter = $this->get(SiteWriter::class); + $siteWriter->write('testing', $configuration); } } diff --git a/typo3/sysext/redirects/Tests/Functional/EventListener/AddPlainSlugReplacementSourceTest.php b/typo3/sysext/redirects/Tests/Functional/EventListener/AddPlainSlugReplacementSourceTest.php index 090b4ae1b18ab56a3a58459ca241c11d53809f33..a56bcffc591de81fa82db575f5c22c41adb2911b 100644 --- a/typo3/sysext/redirects/Tests/Functional/EventListener/AddPlainSlugReplacementSourceTest.php +++ b/typo3/sysext/redirects/Tests/Functional/EventListener/AddPlainSlugReplacementSourceTest.php @@ -20,7 +20,7 @@ namespace TYPO3\CMS\Redirects\Tests\Functional\EventListener; use PHPUnit\Framework\Attributes\Test; use Psr\EventDispatcher\EventDispatcherInterface; use Symfony\Component\DependencyInjection\Container; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Redirects\Event\SlugRedirectChangeItemCreatedEvent; @@ -80,7 +80,7 @@ final class AddPlainSlugReplacementSourceTest extends FunctionalTestCase 'base' => '/', 'settings' => $settings, ]; - $siteConfiguration = GeneralUtility::makeInstance(SiteConfiguration::class); - $siteConfiguration->write('testing', $configuration); + $siteWriter = $this->get(SiteWriter::class); + $siteWriter->write('testing', $configuration); } } diff --git a/typo3/sysext/redirects/Tests/Functional/RedirectUpdate/SlugRedirectChangeItemFactoryTest.php b/typo3/sysext/redirects/Tests/Functional/RedirectUpdate/SlugRedirectChangeItemFactoryTest.php index e66183d1e3dce5009b254b3cbbe1bfddc4607c58..084d5674a4b6419fcd953e241332c8d939b4adfc 100644 --- a/typo3/sysext/redirects/Tests/Functional/RedirectUpdate/SlugRedirectChangeItemFactoryTest.php +++ b/typo3/sysext/redirects/Tests/Functional/RedirectUpdate/SlugRedirectChangeItemFactoryTest.php @@ -19,9 +19,8 @@ namespace TYPO3\CMS\Redirects\Tests\Functional\RedirectUpdate; use PHPUnit\Framework\Attributes\Test; use Symfony\Component\DependencyInjection\Container; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\EventDispatcher\ListenerProvider; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Redirects\Event\SlugRedirectChangeItemCreatedEvent; use TYPO3\CMS\Redirects\RedirectUpdate\SlugRedirectChangeItem; use TYPO3\CMS\Redirects\RedirectUpdate\SlugRedirectChangeItemFactory; @@ -138,7 +137,7 @@ final class SlugRedirectChangeItemFactoryTest extends FunctionalTestCase 'base' => '/', 'settings' => $settings, ]; - $siteConfiguration = GeneralUtility::makeInstance(SiteConfiguration::class); - $siteConfiguration->write('testing', $configuration); + $siteWriter = $this->get(SiteWriter::class); + $siteWriter->write('testing', $configuration); } } diff --git a/typo3/sysext/redirects/Tests/Functional/Service/SlugServiceTest.php b/typo3/sysext/redirects/Tests/Functional/Service/SlugServiceTest.php index 160cfb46c0a38cc5de663a05521cdca8291341da..38b8947cc511fd15f9a1e1c9f4b0d65e2e1bf6f1 100644 --- a/typo3/sysext/redirects/Tests/Functional/Service/SlugServiceTest.php +++ b/typo3/sysext/redirects/Tests/Functional/Service/SlugServiceTest.php @@ -21,7 +21,7 @@ use PHPUnit\Framework\Attributes\Test; use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Log\NullLogger; use Symfony\Component\DependencyInjection\Container; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\DataHandling\Model\CorrelationId; use TYPO3\CMS\Core\Domain\Repository\PageRepository; @@ -563,8 +563,8 @@ final class SlugServiceTest extends FunctionalTestCase 'rootPageId' => 1, 'base' => '/', ]; - $siteConfiguration = $this->get(SiteConfiguration::class); - $siteConfiguration->write('testing', $configuration); + $siteWriter = $this->get(SiteWriter::class); + $siteWriter->write('testing', $configuration); } protected function buildBaseSiteInSubfolder(): void @@ -573,8 +573,8 @@ final class SlugServiceTest extends FunctionalTestCase 'rootPageId' => 1, 'base' => '/sub-folder', ]; - $siteConfiguration = $this->get(SiteConfiguration::class); - $siteConfiguration->write('testing', $configuration); + $siteWriter = $this->get(SiteWriter::class); + $siteWriter->write('testing', $configuration); } protected function buildBaseSiteWithLanguages(): void @@ -584,8 +584,8 @@ final class SlugServiceTest extends FunctionalTestCase 'base' => '/', 'languages' => $this->languages, ]; - $siteConfiguration = $this->get(SiteConfiguration::class); - $siteConfiguration->write('testing', $configuration); + $siteWriter = $this->get(SiteWriter::class); + $siteWriter->write('testing', $configuration); } protected function buildBaseSiteWithLanguagesInSubFolder(): void @@ -603,8 +603,8 @@ final class SlugServiceTest extends FunctionalTestCase 'base' => '/sub-folder', 'languages' => $languages, ]; - $siteConfiguration = $this->get(SiteConfiguration::class); - $siteConfiguration->write('testing', $configuration); + $siteWriter = $this->get(SiteWriter::class); + $siteWriter->write('testing', $configuration); } protected function createSubject(): void diff --git a/typo3/sysext/styleguide/Classes/TcaDataGenerator/AbstractGenerator.php b/typo3/sysext/styleguide/Classes/TcaDataGenerator/AbstractGenerator.php index d31fce1ead7decbebd79a455f770be65a0b2efca..d4f118a87c74250c0ff8a49b32cc7e17cacbbc65 100644 --- a/typo3/sysext/styleguide/Classes/TcaDataGenerator/AbstractGenerator.php +++ b/typo3/sysext/styleguide/Classes/TcaDataGenerator/AbstractGenerator.php @@ -18,7 +18,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Styleguide\TcaDataGenerator; use TYPO3\CMS\Backend\Utility\BackendUtility; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; @@ -46,9 +46,9 @@ abstract class AbstractGenerator $recordFinder = GeneralUtility::makeInstance(RecordFinder::class); // When the DataHandler created the page tree, a default site configuration has been added. Fetch, rename, update. $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByRootPageId($topPageUid); - $siteConfiguration = GeneralUtility::makeInstance(SiteConfiguration::class); + $siteWriter = GeneralUtility::makeInstance(SiteWriter::class); $siteIdentifier = 'styleguide-demo-' . $topPageUid; - $siteConfiguration->rename($site->getIdentifier(), $siteIdentifier); + $siteWriter->rename($site->getIdentifier(), $siteIdentifier); $highestLanguageId = $recordFinder->findHighestLanguageId(); $configuration = [ 'base' => $base . 'styleguide-demo-' . $topPageUid, @@ -138,7 +138,7 @@ abstract class AbstractGenerator ], ], ]; - $siteConfiguration->write($siteIdentifier, $configuration); + $siteWriter->write($siteIdentifier, $configuration); } /** diff --git a/typo3/sysext/styleguide/Classes/TcaDataGenerator/Generator.php b/typo3/sysext/styleguide/Classes/TcaDataGenerator/Generator.php index fb9c514bff25b36694bfc7df465109f01b7b8acb..e7c7cb46efebd3ea58d323e78dda6b6724074b02 100644 --- a/typo3/sysext/styleguide/Classes/TcaDataGenerator/Generator.php +++ b/typo3/sysext/styleguide/Classes/TcaDataGenerator/Generator.php @@ -17,7 +17,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Styleguide\TcaDataGenerator; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory; use TYPO3\CMS\Core\Crypto\Random; use TYPO3\CMS\Core\Database\ConnectionPool; @@ -200,7 +200,7 @@ final class Generator extends AbstractGenerator // Delete site configuration if ($topUids[0] ?? false) { $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByRootPageId($topUids[0]); - GeneralUtility::makeInstance(SiteConfiguration::class)->delete($site->getIdentifier()); + GeneralUtility::makeInstance(SiteWriter::class)->delete($site->getIdentifier()); } } diff --git a/typo3/sysext/styleguide/Classes/TcaDataGenerator/GeneratorFrontend.php b/typo3/sysext/styleguide/Classes/TcaDataGenerator/GeneratorFrontend.php index 38fd0947114b0101890495bbdb358c12b50fd523..b6196d4d8bf6eefce9d3336a0fc184ae7e953825 100644 --- a/typo3/sysext/styleguide/Classes/TcaDataGenerator/GeneratorFrontend.php +++ b/typo3/sysext/styleguide/Classes/TcaDataGenerator/GeneratorFrontend.php @@ -17,7 +17,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Styleguide\TcaDataGenerator; -use TYPO3\CMS\Core\Configuration\SiteConfiguration; +use TYPO3\CMS\Core\Configuration\SiteWriter; use TYPO3\CMS\Core\Exception\SiteNotFoundException; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -204,7 +204,7 @@ final class GeneratorFrontend extends AbstractGenerator if (!empty($rootUid)) { $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByRootPageId((int)$rootUid[0]); $identifier = $site->getIdentifier(); - GeneralUtility::makeInstance(SiteConfiguration::class)->delete($identifier); + GeneralUtility::makeInstance(SiteWriter::class)->delete($identifier); } } catch (SiteNotFoundException $e) { // Do not throw a thing if site config does not exist