From b89aad13d4859fb90f0a8dac923cf43ef01f6f8c Mon Sep 17 00:00:00 2001 From: Helmut Hummel <typo3@helhum.io> Date: Sun, 19 Nov 2023 17:28:26 +0100 Subject: [PATCH] [BUGFIX] Create same configuration in web and cli install process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code for the web installer and the cli installer is only partly aligned and shared, thus may easily diverge, leading to different installation results. The code to create system settings is now put into SetupService and is used for both installation methods, so that the cli install now also uses FactoryConfiguration.php. Additionally, the following cleanups are performed to prepare further cleanup of the installation process: * The cli setup does not set insecure trustedHostPattern any more. * Silent config and template file changes are removed from setup, as conceptually these do nothing on newly created config and template files. * New exceptions are created for config file generation instead of misusing a FAL-related exception. * Absolute paths are made relative to project path before being passed to exception. Resolves: #102403 Releases: main, 12.4 Change-Id: I069e9e9f4136bba56d630eaf09f676b6c5ea7904 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/81793 Tested-by: Benjamin Franzke <ben@bnf.dev> Reviewed-by: Stefan Bürk <stefan@buerk.tech> Reviewed-by: Benjamin Franzke <ben@bnf.dev> Tested-by: Stefan Bürk <stefan@buerk.tech> Tested-by: Oliver Hader <oliver.hader@typo3.org> Reviewed-by: Oliver Hader <oliver.hader@typo3.org> Tested-by: Helmut Hummel <typo3@helhum.io> Reviewed-by: Helmut Hummel <typo3@helhum.io> Tested-by: core-ci <typo3@b13.com> --- Build/Sources/TypeScript/install/installer.ts | 30 +------ .../install/Classes/Command/SetupCommand.php | 13 +--- .../Controller/InstallerController.php | 78 ++----------------- ...gurationDirectoryDoesNotExistException.php | 25 ++++++ ...onfigurationFileAlreadyExistsException.php | 25 ++++++ .../install/Classes/Service/SetupService.php | 66 ++++++++++++---- .../install/Classes/ServiceProvider.php | 6 +- .../Resources/Public/JavaScript/installer.js | 2 +- 8 files changed, 115 insertions(+), 130 deletions(-) create mode 100644 typo3/sysext/install/Classes/Service/Exception/ConfigurationDirectoryDoesNotExistException.php create mode 100644 typo3/sysext/install/Classes/Service/Exception/ConfigurationFileAlreadyExistsException.php diff --git a/Build/Sources/TypeScript/install/installer.ts b/Build/Sources/TypeScript/install/installer.ts index 6e72da364713..949b5ac2af70 100644 --- a/Build/Sources/TypeScript/install/installer.ts +++ b/Build/Sources/TypeScript/install/installer.ts @@ -224,7 +224,7 @@ class Installer { .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { - this.executeSilentConfigurationUpdate(); + this.checkDatabaseConnect(); } else { this.executeAdjustTrustedHostsPattern(); } @@ -235,33 +235,7 @@ class Installer { (new AjaxRequest(this.getUrl('executeAdjustTrustedHostsPattern'))) .get({ cache: 'no-cache' }) .then((): void => { - this.executeSilentConfigurationUpdate(); - }); - } - - private executeSilentConfigurationUpdate(): void { - (new AjaxRequest(this.getUrl('executeSilentConfigurationUpdate'))) - .get({ cache: 'no-cache' }) - .then(async (response: AjaxResponse): Promise<void> => { - const data = await response.resolve(); - if (data.success === true) { - this.executeSilentTemplateFileUpdate(); - } else { - this.executeSilentConfigurationUpdate(); - } - }); - } - - private executeSilentTemplateFileUpdate(): void { - (new AjaxRequest(this.getUrl('executeSilentTemplateFileUpdate'))) - .get({ cache: 'no-cache' }) - .then(async (response: AjaxResponse): Promise<void> => { - const data = await response.resolve(); - if (data.success === true) { - this.checkDatabaseConnect(); - } else { - this.executeSilentTemplateFileUpdate(); - } + this.checkDatabaseConnect(); }); } diff --git a/typo3/sysext/install/Classes/Command/SetupCommand.php b/typo3/sysext/install/Classes/Command/SetupCommand.php index 9874d17eb4ed..b62151c750b5 100644 --- a/typo3/sysext/install/Classes/Command/SetupCommand.php +++ b/typo3/sysext/install/Classes/Command/SetupCommand.php @@ -29,11 +29,9 @@ use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\Question; use TYPO3\CMS\Core\Configuration\ConfigurationManager; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Package\FailsafePackageManager; -use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException; use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Install\FolderStructure\DefaultFactory; +use TYPO3\CMS\Install\Service\Exception\ConfigurationFileAlreadyExistsException; use TYPO3\CMS\Install\Service\LateBootService; use TYPO3\CMS\Install\Service\SetupDatabaseService; use TYPO3\CMS\Install\Service\SetupService; @@ -59,7 +57,6 @@ class SetupCommand extends Command private readonly SetupService $setupService, private readonly ConfigurationManager $configurationManager, private readonly LateBootService $lateBootService, - private readonly FailsafePackageManager $packageManager, ) { parent::__construct($name); } @@ -209,16 +206,12 @@ EOT $questionHelper = $this->getHelper('question'); // Ensure all required files and folders exist - $serverType = $this->getServerType($questionHelper, $input, $output); - $folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class); - $folderStructureFactory->getStructure($serverType)->fix(); - // Ensure existing PackageStates.php for non-composer installation. - $this->packageManager->recreatePackageStatesFileIfMissing(true); + $this->setupService->createDirectoryStructure($this->getServerType($questionHelper, $input, $output)); try { $force = $input->getOption('force'); $this->setupService->prepareSystemSettings($force); - } catch (ExistingTargetFileNameException $exception) { + } catch (ConfigurationFileAlreadyExistsException) { $configOverwriteQuestion = new ConfirmationQuestion( 'Configuration already exists do you want to overwrite it [default: no] ? ', false diff --git a/typo3/sysext/install/Classes/Controller/InstallerController.php b/typo3/sysext/install/Classes/Controller/InstallerController.php index e0e81557cb62..2a313db9140b 100644 --- a/typo3/sysext/install/Classes/Controller/InstallerController.php +++ b/typo3/sysext/install/Classes/Controller/InstallerController.php @@ -23,8 +23,6 @@ use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Backend\Routing\RouteRedirect; use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Core\Configuration\ConfigurationManager; -use TYPO3\CMS\Core\Configuration\Exception\SettingsWriteException; -use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Crypto\HashService; use TYPO3\CMS\Core\Database\ConnectionPool; @@ -45,17 +43,12 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\View\FluidViewAdapter; use TYPO3\CMS\Core\View\ViewInterface; use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextFactory; -use TYPO3\CMS\Install\Configuration\FeatureManager; use TYPO3\CMS\Install\FolderStructure\DefaultFactory; use TYPO3\CMS\Install\Service\EnableFileService; -use TYPO3\CMS\Install\Service\Exception\ConfigurationChangedException; -use TYPO3\CMS\Install\Service\Exception\SilentConfigurationUpgradeReadonlyException; -use TYPO3\CMS\Install\Service\Exception\TemplateFileChangedException; +use TYPO3\CMS\Install\Service\Exception\ConfigurationDirectoryDoesNotExistException; use TYPO3\CMS\Install\Service\LateBootService; use TYPO3\CMS\Install\Service\SetupDatabaseService; use TYPO3\CMS\Install\Service\SetupService; -use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService; -use TYPO3\CMS\Install\Service\SilentTemplateFileUpgradeService; use TYPO3\CMS\Install\SystemEnvironment\Check; use TYPO3\CMS\Install\SystemEnvironment\SetupCheck; use TYPO3\CMS\Install\WebserverType; @@ -73,8 +66,6 @@ final class InstallerController public function __construct( private readonly LateBootService $lateBootService, - private readonly SilentConfigurationUpgradeService $silentConfigurationUpgradeService, - private readonly SilentTemplateFileUpgradeService $silentTemplateFileUpgradeService, private readonly ConfigurationManager $configurationManager, private readonly FailsafePackageManager $packageManager, private readonly VerifyHostHeader $verifyHostHeader, @@ -184,27 +175,18 @@ final class InstallerController */ public function executeEnvironmentAndFoldersAction(ServerRequestInterface $request): ResponseInterface { - $folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class); - $structureFacade = $folderStructureFactory->getStructure(WebserverType::fromRequest($request)); - $structureFixMessageQueue = $structureFacade->fix(); - $errorsFromStructure = $structureFixMessageQueue->getAllMessages(ContextualFeedbackSeverity::ERROR); - - if ($this->configurationManager->canWriteConfiguration()) { - $this->configurationManager->createLocalConfigurationFromFactoryConfiguration(); - // Create a PackageStates.php with all packages activated marked as "part of factory default" - $this->packageManager->recreatePackageStatesFileIfMissing(true); - $extensionConfiguration = new ExtensionConfiguration(); - $extensionConfiguration->synchronizeExtConfTemplateWithLocalConfigurationOfAllExtensions(); - + $errorsFromStructure = $this->setupService->createDirectoryStructure(WebserverType::fromRequest($request)); + try { + $this->setupService->prepareSystemSettings(); + } catch (ConfigurationDirectoryDoesNotExistException) { return new JsonResponse([ - 'success' => true, + 'success' => false, + 'status' => $errorsFromStructure, ]); } - $errorsFromStructure[] = new FlashMessage('Unable to write configuration file', 'Error', ContextualFeedbackSeverity::ERROR); return new JsonResponse([ - 'success' => false, - 'status' => $errorsFromStructure, + 'success' => true, ]); } @@ -237,44 +219,6 @@ final class InstallerController ]); } - /** - * Execute silent configuration update. May be called multiple times until success = true is returned. - * - * @return ResponseInterface success = true if no change has been done - */ - public function executeSilentConfigurationUpdateAction(): ResponseInterface - { - $success = true; - try { - $this->silentConfigurationUpgradeService->execute(); - } catch (ConfigurationChangedException) { - $success = false; - } catch (SettingsWriteException $e) { - throw new SilentConfigurationUpgradeReadonlyException(code: 1688464086, throwable: $e); - } - return new JsonResponse([ - 'success' => $success, - ]); - } - - /** - * Execute silent template files update. May be called multiple times until success = true is returned. - * - * @return ResponseInterface success = true if no change has been done - */ - public function executeSilentTemplateFileUpdateAction(): ResponseInterface - { - $success = true; - try { - $this->silentTemplateFileUpgradeService->execute(); - } catch (TemplateFileChangedException $e) { - $success = false; - } - return new JsonResponse([ - 'success' => $success, - ]); - } - /** * Check if database connect step needs to be shown */ @@ -606,10 +550,6 @@ final class InstallerController */ public function executeDefaultConfigurationAction(ServerRequestInterface $request): ResponseInterface { - $featureManager = new FeatureManager(); - // Get best matching configuration presets - $configurationValues = $featureManager->getBestMatchingConfigurationForAllFeatures(); - $container = $this->lateBootService->loadExtLocalconfDatabaseAndExtTables(); // Use the container here instead of makeInstance() to use the factory of the container for building the UriBuilder $uriBuilder = $container->get(UriBuilder::class); @@ -651,8 +591,6 @@ final class InstallerController // Mark upgrade wizards as done $this->setupDatabaseService->markWizardsDone($container); - $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($configurationValues); - $formProtection = $this->formProtectionFactory->createFromRequest($request); $formProtection->clean(); diff --git a/typo3/sysext/install/Classes/Service/Exception/ConfigurationDirectoryDoesNotExistException.php b/typo3/sysext/install/Classes/Service/Exception/ConfigurationDirectoryDoesNotExistException.php new file mode 100644 index 000000000000..ab575633e55e --- /dev/null +++ b/typo3/sysext/install/Classes/Service/Exception/ConfigurationDirectoryDoesNotExistException.php @@ -0,0 +1,25 @@ +<?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\Install\Service\Exception; + +use TYPO3\CMS\Install\Service\Exception; + +/** + * An exception thrown during setup, when the config dir was not created, but config file must be written + */ +class ConfigurationDirectoryDoesNotExistException extends Exception {} diff --git a/typo3/sysext/install/Classes/Service/Exception/ConfigurationFileAlreadyExistsException.php b/typo3/sysext/install/Classes/Service/Exception/ConfigurationFileAlreadyExistsException.php new file mode 100644 index 000000000000..3e31ac09e526 --- /dev/null +++ b/typo3/sysext/install/Classes/Service/Exception/ConfigurationFileAlreadyExistsException.php @@ -0,0 +1,25 @@ +<?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\Install\Service\Exception; + +use TYPO3\CMS\Install\Service\Exception; + +/** + * An exception thrown during setup, when the config file should be written, but already exists + */ +class ConfigurationFileAlreadyExistsException extends Exception {} diff --git a/typo3/sysext/install/Classes/Service/SetupService.php b/typo3/sysext/install/Classes/Service/SetupService.php index 699d9dec5895..05108785a877 100644 --- a/typo3/sysext/install/Classes/Service/SetupService.php +++ b/typo3/sysext/install/Classes/Service/SetupService.php @@ -19,8 +19,10 @@ namespace TYPO3\CMS\Install\Service; use TYPO3\CMS\Core\Configuration\ConfigurationManager; use TYPO3\CMS\Core\Configuration\Exception\SiteConfigurationWriteException; +use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader; use TYPO3\CMS\Core\Configuration\SiteWriter; +use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Crypto\PasswordHashing\Argon2idPasswordHash; use TYPO3\CMS\Core\Crypto\PasswordHashing\Argon2iPasswordHash; use TYPO3\CMS\Core\Crypto\PasswordHashing\BcryptPasswordHash; @@ -28,10 +30,16 @@ use TYPO3\CMS\Core\Crypto\PasswordHashing\InvalidPasswordHashException; use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashInterface; use TYPO3\CMS\Core\Crypto\Random; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException; +use TYPO3\CMS\Core\Messaging\FlashMessage; +use TYPO3\CMS\Core\Package\FailsafePackageManager; +use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Install\Command\BackendUserGroupType; use TYPO3\CMS\Install\Configuration\FeatureManager; +use TYPO3\CMS\Install\FolderStructure\DefaultFactory; +use TYPO3\CMS\Install\Service\Exception\ConfigurationDirectoryDoesNotExistException; +use TYPO3\CMS\Install\Service\Exception\ConfigurationFileAlreadyExistsException; +use TYPO3\CMS\Install\WebserverType; /** * Service class helping to manage parts of the setup process (set configuration, @@ -44,8 +52,20 @@ readonly class SetupService private ConfigurationManager $configurationManager, private SiteWriter $siteWriter, private YamlFileLoader $yamlFileLoader, + private FailsafePackageManager $packageManager, ) {} + /** + * @param WebserverType $webserverType + * @return FlashMessage[] + */ + public function createDirectoryStructure(WebserverType $webserverType): array + { + $folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class); + $structureFixMessageQueue = $folderStructureFactory->getStructure($webserverType)->fix(); + return $structureFixMessageQueue->getAllMessages(ContextualFeedbackSeverity::ERROR); + } + public function setSiteName(string $name): bool { return $this->configurationManager->setLocalConfigurationValueByPath('SYS/sitename', $name); @@ -126,34 +146,41 @@ readonly class SetupService } /** - * @throws ExistingTargetFileNameException + * @throws ConfigurationFileAlreadyExistsException + * @throws ConfigurationDirectoryDoesNotExistException */ public function prepareSystemSettings(bool $forceOverwrite = false): void { $configurationFileLocation = $this->configurationManager->getSystemConfigurationFileLocation(); - if (!$forceOverwrite && @is_file($configurationFileLocation)) { - throw new ExistingTargetFileNameException( - 'Configuration file ' . $configurationFileLocation . ' already exists!', - 1669747685, + $configDir = dirname($configurationFileLocation); + if (!is_dir($configDir)) { + throw new ConfigurationDirectoryDoesNotExistException( + 'Configuration directory ' . $this->makePathRelativeToProjectDirectory($configDir) . ' does not exist!', + 1700401774, ); } - - // @todo Remove once LocalConfiguration.php support was dropped. - // @todo Web installer creates default configuration based on default factory configuration. Recheck if we - // should use this here too instead of an empty array. - // Ugly hack to write system/settings.php, to avoid fallback to - // LocalConfiguration.php causing issues because it does not exist! - @unlink($configurationFileLocation); - $this->configurationManager->writeLocalConfiguration([]); + if (@is_file($configurationFileLocation)) { + if (!$forceOverwrite) { + throw new ConfigurationFileAlreadyExistsException( + 'Configuration file ' . $this->makePathRelativeToProjectDirectory($configurationFileLocation) . ' already exists!', + 1669747685, + ); + } + unlink($configurationFileLocation); + } + $this->configurationManager->createLocalConfigurationFromFactoryConfiguration(); + $randomKey = GeneralUtility::makeInstance(Random::class)->generateRandomHexString(96); + $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey); + $extensionConfiguration = new ExtensionConfiguration(); + $extensionConfiguration->synchronizeExtConfTemplateWithLocalConfigurationOfAllExtensions(); // Get best matching configuration presets $featureManager = new FeatureManager(); $configurationValues = $featureManager->getBestMatchingConfigurationForAllFeatures(); $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($configurationValues); - $randomKey = GeneralUtility::makeInstance(Random::class)->generateRandomHexString(96); - $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey); - $this->configurationManager->setLocalConfigurationValueByPath('SYS/trustedHostsPattern', '.*.*'); + // In non Composer mode, create a PackageStates.php with all packages activated marked as "part of factory default" + $this->packageManager->recreatePackageStatesFileIfMissing(true); } public function createSite(): string @@ -316,4 +343,9 @@ For each website you need a TypoScript record on the main page of your website ( ); } } + + private function makePathRelativeToProjectDirectory(string $absolutePath): string + { + return str_replace(Environment::getProjectPath(), '', $absolutePath); + } } diff --git a/typo3/sysext/install/Classes/ServiceProvider.php b/typo3/sysext/install/Classes/ServiceProvider.php index 2a512cf16d89..332e45be82ff 100644 --- a/typo3/sysext/install/Classes/ServiceProvider.php +++ b/typo3/sysext/install/Classes/ServiceProvider.php @@ -238,7 +238,8 @@ class ServiceProvider extends AbstractServiceProvider return new Service\SetupService( $container->get(ConfigurationManager::class), $container->get(SiteWriter::class), - $container->get(YamlFileLoader::class) + $container->get(YamlFileLoader::class), + $container->get(FailsafePackageManager::class), ); } @@ -295,8 +296,6 @@ class ServiceProvider extends AbstractServiceProvider { return new Controller\InstallerController( $container->get(Service\LateBootService::class), - $container->get(Service\SilentConfigurationUpgradeService::class), - $container->get(Service\SilentTemplateFileUpgradeService::class), $container->get(ConfigurationManager::class), $container->get(FailsafePackageManager::class), $container->get(VerifyHostHeader::class), @@ -413,7 +412,6 @@ class ServiceProvider extends AbstractServiceProvider $container->get(Service\SetupService::class), $container->get(ConfigurationManager::class), $container->get(LateBootService::class), - $container->get(FailsafePackageManager::class), ); } diff --git a/typo3/sysext/install/Resources/Public/JavaScript/installer.js b/typo3/sysext/install/Resources/Public/JavaScript/installer.js index a42058587f9b..2e86c46901b4 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/installer.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/installer.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import PasswordStrength from"@typo3/install/module/password-strength.js";import{InfoBox}from"@typo3/install/renderable/info-box.js";import"@typo3/backend/element/icon-element.js";import{selector}from"@typo3/core/literals.js";import"@typo3/backend/element/progress-bar-element.js";var Identifiers;!function(e){e.body=".t3js-body",e.moduleContent=".t3js-module-content",e.mainContent=".t3js-installer-content",e.progressBar=".t3js-installer-progress",e.databaseConnectOutput=".t3js-installer-databaseConnect-output",e.databaseSelectOutput=".t3js-installer-databaseSelect-output",e.databaseDataOutput=".t3js-installer-databaseData-output"}(Identifiers||(Identifiers={}));class Installer{constructor(){this.initializeEvents(),DocumentService.ready().then((()=>{this.initialize()}))}initializeEvents(){new RegularEvent("click",(e=>{e.preventDefault(),this.showEnvironmentAndFolders()})).delegateTo(document,".t3js-installer-environmentFolders-retry"),new RegularEvent("click",(e=>{e.preventDefault(),this.executeEnvironmentAndFolders()})).delegateTo(document,".t3js-installer-environmentFolders-execute"),new RegularEvent("click",(e=>{e.preventDefault(),this.executeDatabaseConnect()})).delegateTo(document,".t3js-installer-databaseConnect-execute"),new RegularEvent("click",(e=>{e.preventDefault(),this.executeDatabaseSelect()})).delegateTo(document,".t3js-installer-databaseSelect-execute"),new RegularEvent("click",(e=>{e.preventDefault(),this.executeDatabaseData()})).delegateTo(document,".t3js-installer-databaseData-execute"),new RegularEvent("click",(e=>{e.preventDefault(),this.executeDefaultConfiguration()})).delegateTo(document,".t3js-installer-defaultConfiguration-execute"),new RegularEvent("click",((e,t)=>{e.preventDefault();const a=document.querySelector(t.dataset.toggleTarget);"invisible"===t.dataset.toggleState?(t.dataset.toggleState="visible",a.setAttribute("type","text")):(t.dataset.toggleState="invisible",a.setAttribute("type","password"))})).delegateTo(document,".t3-install-form-password-toggle"),new RegularEvent("change",((e,t)=>{const a=t.value;document.querySelectorAll(".t3-install-driver-data").forEach((e=>e.setAttribute("hidden",""))),document.querySelectorAll(".t3-install-driver-data input").forEach((e=>e.setAttribute("disabled","disabled"))),document.querySelectorAll(selector`#${a} input`).forEach((e=>e.removeAttribute("disabled"))),document.querySelector("#"+a)?.removeAttribute("hidden")})).delegateTo(document,"#t3js-connect-database-driver")}initialize(){this.setProgress(0),this.getMainLayout()}getUrl(e){let t=location.href;return t=t.replace(location.search,""),void 0!==e&&(t=t+"?install[action]="+e),t}setProgress(e){const t=document.querySelector(Identifiers.progressBar);null!==t&&0!==e&&(t.value=e,t.label=`Step ${e} of 5 completed`)}getMainLayout(){new AjaxRequest(this.getUrl("mainLayout")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();document.querySelector(Identifiers.body).innerHTML=t.html,this.checkInstallerAvailable()}))}checkInstallerAvailable(){new AjaxRequest(this.getUrl("checkInstallerAvailable")).get({cache:"no-cache"}).then((async e=>{(await e.resolve()).success?this.checkEnvironmentAndFolders():this.showInstallerNotAvailable()}))}showInstallerNotAvailable(){const e=document.querySelector(Identifiers.mainContent);new AjaxRequest(this.getUrl("showInstallerNotAvailable")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&(e.innerHTML=a.html)}))}checkEnvironmentAndFolders(){this.setProgress(1),new AjaxRequest(this.getUrl("checkEnvironmentAndFolders")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkTrustedHostsPattern():this.showEnvironmentAndFolders()}))}showEnvironmentAndFolders(){const e=document.querySelector(Identifiers.mainContent);new AjaxRequest(this.getUrl("showEnvironmentAndFolders")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();if(!0===a.success){e.innerHTML=a.html;const t=document.querySelector(".t3js-installer-environment-details");let s=!1;Array.isArray(a.environmentStatusErrors)&&a.environmentStatusErrors.forEach((e=>{s=!0,t.append(InfoBox.create(e.severity,e.title,e.message))})),Array.isArray(a.environmentStatusWarnings)&&a.environmentStatusWarnings.forEach((e=>{s=!0,t.append(InfoBox.create(e.severity,e.title,e.message))})),Array.isArray(a.structureErrors)&&a.structureErrors.forEach((e=>{s=!0,t.append(InfoBox.create(e.severity,e.title,e.message))})),s?(t.removeAttribute("hidden"),document.querySelectorAll(".t3js-installer-environmentFolders-bad").forEach((e=>e.removeAttribute("hidden")))):document.querySelectorAll(".t3js-installer-environmentFolders-good").forEach((e=>e.removeAttribute("hidden")))}}))}executeEnvironmentAndFolders(){new AjaxRequest(this.getUrl("executeEnvironmentAndFolders")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success&&this.checkTrustedHostsPattern()}))}checkTrustedHostsPattern(){new AjaxRequest(this.getUrl("checkTrustedHostsPattern")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.executeSilentConfigurationUpdate():this.executeAdjustTrustedHostsPattern()}))}executeAdjustTrustedHostsPattern(){new AjaxRequest(this.getUrl("executeAdjustTrustedHostsPattern")).get({cache:"no-cache"}).then((()=>{this.executeSilentConfigurationUpdate()}))}executeSilentConfigurationUpdate(){new AjaxRequest(this.getUrl("executeSilentConfigurationUpdate")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.executeSilentTemplateFileUpdate():this.executeSilentConfigurationUpdate()}))}executeSilentTemplateFileUpdate(){new AjaxRequest(this.getUrl("executeSilentTemplateFileUpdate")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseConnect():this.executeSilentTemplateFileUpdate()}))}checkDatabaseConnect(){this.setProgress(2),new AjaxRequest(this.getUrl("checkDatabaseConnect")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseSelect():this.showDatabaseConnect()}))}showDatabaseConnect(){const e=document.querySelector(Identifiers.mainContent);new AjaxRequest(this.getUrl("showDatabaseConnect")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&(e.innerHTML=a.html,document.querySelector("#t3js-connect-database-driver").dispatchEvent(new Event("change",{bubbles:!0})),PasswordStrength.initialize(document.querySelector(".t3-install-form-password-strength")))}))}executeDatabaseConnect(){const e=document.querySelector(Identifiers.databaseConnectOutput),t={"install[action]":"executeDatabaseConnect","install[token]":document.querySelector(Identifiers.moduleContent).dataset.installerDatabaseConnectExecuteToken};for(const[e,a]of new FormData(document.querySelector(Identifiers.body+" form")))t[e]=a.toString();new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.checkDatabaseSelect():Array.isArray(a.status)&&(e.replaceChildren(),a.status.forEach((t=>{e.append(InfoBox.create(t.severity,t.title,t.message))})))}))}checkDatabaseSelect(){this.setProgress(3),new AjaxRequest(this.getUrl("checkDatabaseSelect")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseData():this.showDatabaseSelect()}))}showDatabaseSelect(){const e=document.querySelector(Identifiers.mainContent);new AjaxRequest(this.getUrl("showDatabaseSelect")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&(e.innerHTML=a.html)}))}executeDatabaseSelect(){const e=document.querySelector(Identifiers.databaseSelectOutput),t={"install[action]":"executeDatabaseSelect","install[token]":document.querySelector(Identifiers.moduleContent).dataset.installerDatabaseSelectExecuteToken};for(const[e,a]of new FormData(document.querySelector(Identifiers.body+" form")))t[e]=a.toString();new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.checkDatabaseRequirements():Array.isArray(a.status)&&a.status.forEach((t=>{e.replaceChildren(InfoBox.create(t.severity,t.title,t.message))}))}))}checkDatabaseRequirements(){const e=document.querySelector(Identifiers.databaseSelectOutput),t={"install[action]":"checkDatabaseRequirements","install[token]":document.querySelector(Identifiers.moduleContent).dataset.installerDatabaseCheckRequirementsExecuteToken};for(const[e,a]of new FormData(document.querySelector(Identifiers.body+" form")))t[e]=a.toString();new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.checkDatabaseData():Array.isArray(a.status)&&(e.replaceChildren(),a.status.forEach((t=>{e.append(InfoBox.create(t.severity,t.title,t.message))})))}))}checkDatabaseData(){this.setProgress(4),new AjaxRequest(this.getUrl("checkDatabaseData")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.showDefaultConfiguration():this.showDatabaseData()}))}showDatabaseData(){const e=document.querySelector(Identifiers.mainContent);new AjaxRequest(this.getUrl("showDatabaseData")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&(e.innerHTML=a.html,PasswordStrength.initialize(document.querySelector(".t3-install-form-password-strength")))}))}executeDatabaseData(){const e=document.querySelector(Identifiers.databaseDataOutput),t={"install[action]":"executeDatabaseData","install[token]":document.querySelector(Identifiers.moduleContent).dataset.installerDatabaseDataExecuteToken};for(const[e,a]of new FormData(document.querySelector(Identifiers.body+" form")))t[e]=a.toString();const a=document.createElement("typo3-backend-progress-bar");e.replaceChildren(a),new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.showDefaultConfiguration():Array.isArray(a.status)&&(e.replaceChildren(),a.status.forEach((t=>{e.append(InfoBox.create(t.severity,t.title,t.message))})))}))}showDefaultConfiguration(){const e=document.querySelector(Identifiers.mainContent);this.setProgress(5),new AjaxRequest(this.getUrl("showDefaultConfiguration")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&(e.innerHTML=a.html)}))}executeDefaultConfiguration(){const e={"install[action]":"executeDefaultConfiguration","install[token]":document.querySelector(Identifiers.moduleContent).dataset.installerDefaultConfigurationExecuteToken};for(const[t,a]of new FormData(document.querySelector(Identifiers.body+" form")))e[t]=a.toString();new AjaxRequest(this.getUrl()).post(e).then((async e=>{const t=await e.resolve();top.location.href=t.redirect}))}}export default new Installer; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import PasswordStrength from"@typo3/install/module/password-strength.js";import{InfoBox}from"@typo3/install/renderable/info-box.js";import"@typo3/backend/element/icon-element.js";import{selector}from"@typo3/core/literals.js";import"@typo3/backend/element/progress-bar-element.js";var Identifiers;!function(e){e.body=".t3js-body",e.moduleContent=".t3js-module-content",e.mainContent=".t3js-installer-content",e.progressBar=".t3js-installer-progress",e.databaseConnectOutput=".t3js-installer-databaseConnect-output",e.databaseSelectOutput=".t3js-installer-databaseSelect-output",e.databaseDataOutput=".t3js-installer-databaseData-output"}(Identifiers||(Identifiers={}));class Installer{constructor(){this.initializeEvents(),DocumentService.ready().then((()=>{this.initialize()}))}initializeEvents(){new RegularEvent("click",(e=>{e.preventDefault(),this.showEnvironmentAndFolders()})).delegateTo(document,".t3js-installer-environmentFolders-retry"),new RegularEvent("click",(e=>{e.preventDefault(),this.executeEnvironmentAndFolders()})).delegateTo(document,".t3js-installer-environmentFolders-execute"),new RegularEvent("click",(e=>{e.preventDefault(),this.executeDatabaseConnect()})).delegateTo(document,".t3js-installer-databaseConnect-execute"),new RegularEvent("click",(e=>{e.preventDefault(),this.executeDatabaseSelect()})).delegateTo(document,".t3js-installer-databaseSelect-execute"),new RegularEvent("click",(e=>{e.preventDefault(),this.executeDatabaseData()})).delegateTo(document,".t3js-installer-databaseData-execute"),new RegularEvent("click",(e=>{e.preventDefault(),this.executeDefaultConfiguration()})).delegateTo(document,".t3js-installer-defaultConfiguration-execute"),new RegularEvent("click",((e,t)=>{e.preventDefault();const a=document.querySelector(t.dataset.toggleTarget);"invisible"===t.dataset.toggleState?(t.dataset.toggleState="visible",a.setAttribute("type","text")):(t.dataset.toggleState="invisible",a.setAttribute("type","password"))})).delegateTo(document,".t3-install-form-password-toggle"),new RegularEvent("change",((e,t)=>{const a=t.value;document.querySelectorAll(".t3-install-driver-data").forEach((e=>e.setAttribute("hidden",""))),document.querySelectorAll(".t3-install-driver-data input").forEach((e=>e.setAttribute("disabled","disabled"))),document.querySelectorAll(selector`#${a} input`).forEach((e=>e.removeAttribute("disabled"))),document.querySelector("#"+a)?.removeAttribute("hidden")})).delegateTo(document,"#t3js-connect-database-driver")}initialize(){this.setProgress(0),this.getMainLayout()}getUrl(e){let t=location.href;return t=t.replace(location.search,""),void 0!==e&&(t=t+"?install[action]="+e),t}setProgress(e){const t=document.querySelector(Identifiers.progressBar);null!==t&&0!==e&&(t.value=e,t.label=`Step ${e} of 5 completed`)}getMainLayout(){new AjaxRequest(this.getUrl("mainLayout")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();document.querySelector(Identifiers.body).innerHTML=t.html,this.checkInstallerAvailable()}))}checkInstallerAvailable(){new AjaxRequest(this.getUrl("checkInstallerAvailable")).get({cache:"no-cache"}).then((async e=>{(await e.resolve()).success?this.checkEnvironmentAndFolders():this.showInstallerNotAvailable()}))}showInstallerNotAvailable(){const e=document.querySelector(Identifiers.mainContent);new AjaxRequest(this.getUrl("showInstallerNotAvailable")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&(e.innerHTML=a.html)}))}checkEnvironmentAndFolders(){this.setProgress(1),new AjaxRequest(this.getUrl("checkEnvironmentAndFolders")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkTrustedHostsPattern():this.showEnvironmentAndFolders()}))}showEnvironmentAndFolders(){const e=document.querySelector(Identifiers.mainContent);new AjaxRequest(this.getUrl("showEnvironmentAndFolders")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();if(!0===a.success){e.innerHTML=a.html;const t=document.querySelector(".t3js-installer-environment-details");let s=!1;Array.isArray(a.environmentStatusErrors)&&a.environmentStatusErrors.forEach((e=>{s=!0,t.append(InfoBox.create(e.severity,e.title,e.message))})),Array.isArray(a.environmentStatusWarnings)&&a.environmentStatusWarnings.forEach((e=>{s=!0,t.append(InfoBox.create(e.severity,e.title,e.message))})),Array.isArray(a.structureErrors)&&a.structureErrors.forEach((e=>{s=!0,t.append(InfoBox.create(e.severity,e.title,e.message))})),s?(t.removeAttribute("hidden"),document.querySelectorAll(".t3js-installer-environmentFolders-bad").forEach((e=>e.removeAttribute("hidden")))):document.querySelectorAll(".t3js-installer-environmentFolders-good").forEach((e=>e.removeAttribute("hidden")))}}))}executeEnvironmentAndFolders(){new AjaxRequest(this.getUrl("executeEnvironmentAndFolders")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success&&this.checkTrustedHostsPattern()}))}checkTrustedHostsPattern(){new AjaxRequest(this.getUrl("checkTrustedHostsPattern")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseConnect():this.executeAdjustTrustedHostsPattern()}))}executeAdjustTrustedHostsPattern(){new AjaxRequest(this.getUrl("executeAdjustTrustedHostsPattern")).get({cache:"no-cache"}).then((()=>{this.checkDatabaseConnect()}))}checkDatabaseConnect(){this.setProgress(2),new AjaxRequest(this.getUrl("checkDatabaseConnect")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseSelect():this.showDatabaseConnect()}))}showDatabaseConnect(){const e=document.querySelector(Identifiers.mainContent);new AjaxRequest(this.getUrl("showDatabaseConnect")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&(e.innerHTML=a.html,document.querySelector("#t3js-connect-database-driver").dispatchEvent(new Event("change",{bubbles:!0})),PasswordStrength.initialize(document.querySelector(".t3-install-form-password-strength")))}))}executeDatabaseConnect(){const e=document.querySelector(Identifiers.databaseConnectOutput),t={"install[action]":"executeDatabaseConnect","install[token]":document.querySelector(Identifiers.moduleContent).dataset.installerDatabaseConnectExecuteToken};for(const[e,a]of new FormData(document.querySelector(Identifiers.body+" form")))t[e]=a.toString();new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.checkDatabaseSelect():Array.isArray(a.status)&&(e.replaceChildren(),a.status.forEach((t=>{e.append(InfoBox.create(t.severity,t.title,t.message))})))}))}checkDatabaseSelect(){this.setProgress(3),new AjaxRequest(this.getUrl("checkDatabaseSelect")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseData():this.showDatabaseSelect()}))}showDatabaseSelect(){const e=document.querySelector(Identifiers.mainContent);new AjaxRequest(this.getUrl("showDatabaseSelect")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&(e.innerHTML=a.html)}))}executeDatabaseSelect(){const e=document.querySelector(Identifiers.databaseSelectOutput),t={"install[action]":"executeDatabaseSelect","install[token]":document.querySelector(Identifiers.moduleContent).dataset.installerDatabaseSelectExecuteToken};for(const[e,a]of new FormData(document.querySelector(Identifiers.body+" form")))t[e]=a.toString();new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.checkDatabaseRequirements():Array.isArray(a.status)&&a.status.forEach((t=>{e.replaceChildren(InfoBox.create(t.severity,t.title,t.message))}))}))}checkDatabaseRequirements(){const e=document.querySelector(Identifiers.databaseSelectOutput),t={"install[action]":"checkDatabaseRequirements","install[token]":document.querySelector(Identifiers.moduleContent).dataset.installerDatabaseCheckRequirementsExecuteToken};for(const[e,a]of new FormData(document.querySelector(Identifiers.body+" form")))t[e]=a.toString();new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.checkDatabaseData():Array.isArray(a.status)&&(e.replaceChildren(),a.status.forEach((t=>{e.append(InfoBox.create(t.severity,t.title,t.message))})))}))}checkDatabaseData(){this.setProgress(4),new AjaxRequest(this.getUrl("checkDatabaseData")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.showDefaultConfiguration():this.showDatabaseData()}))}showDatabaseData(){const e=document.querySelector(Identifiers.mainContent);new AjaxRequest(this.getUrl("showDatabaseData")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&(e.innerHTML=a.html,PasswordStrength.initialize(document.querySelector(".t3-install-form-password-strength")))}))}executeDatabaseData(){const e=document.querySelector(Identifiers.databaseDataOutput),t={"install[action]":"executeDatabaseData","install[token]":document.querySelector(Identifiers.moduleContent).dataset.installerDatabaseDataExecuteToken};for(const[e,a]of new FormData(document.querySelector(Identifiers.body+" form")))t[e]=a.toString();const a=document.createElement("typo3-backend-progress-bar");e.replaceChildren(a),new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.showDefaultConfiguration():Array.isArray(a.status)&&(e.replaceChildren(),a.status.forEach((t=>{e.append(InfoBox.create(t.severity,t.title,t.message))})))}))}showDefaultConfiguration(){const e=document.querySelector(Identifiers.mainContent);this.setProgress(5),new AjaxRequest(this.getUrl("showDefaultConfiguration")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&(e.innerHTML=a.html)}))}executeDefaultConfiguration(){const e={"install[action]":"executeDefaultConfiguration","install[token]":document.querySelector(Identifiers.moduleContent).dataset.installerDefaultConfigurationExecuteToken};for(const[t,a]of new FormData(document.querySelector(Identifiers.body+" form")))e[t]=a.toString();new AjaxRequest(this.getUrl()).post(e).then((async e=>{const t=await e.resolve();top.location.href=t.redirect}))}}export default new Installer; \ No newline at end of file -- GitLab