[FEATURE] Add symfony dependency injection for core and extbase
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / MaintenanceController.php
index 5e23bc9..25032a6 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-declare(strict_types=1);
+declare(strict_types = 1);
 namespace TYPO3\CMS\Install\Controller;
 
 /*
@@ -17,8 +17,10 @@ namespace TYPO3\CMS\Install\Controller;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
-use TYPO3\CMS\Core\Core\Bootstrap;
+use TYPO3\CMS\Core\Configuration\ConfigurationManager;
 use TYPO3\CMS\Core\Core\ClassLoadingInformation;
+use TYPO3\CMS\Core\Core\Environment;
+use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Schema\Exception\StatementException;
 use TYPO3\CMS\Core\Database\Schema\SchemaMigrator;
@@ -26,17 +28,19 @@ use TYPO3\CMS\Core\Database\Schema\SqlReader;
 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
 use TYPO3\CMS\Core\FormProtection\InstallToolFormProtection;
 use TYPO3\CMS\Core\Http\JsonResponse;
+use TYPO3\CMS\Core\Localization\Locales;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
 use TYPO3\CMS\Core\Service\OpcodeCacheService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Install\Service\ClearCacheService;
 use TYPO3\CMS\Install\Service\ClearTableService;
+use TYPO3\CMS\Install\Service\LanguagePackService;
 use TYPO3\CMS\Install\Service\Typo3tempFileService;
-use TYPO3\CMS\Saltedpasswords\Salt\SaltFactory;
 
 /**
  * Maintenance controller
+ * @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
  */
 class MaintenanceController extends AbstractController
 {
@@ -49,15 +53,6 @@ class MaintenanceController extends AbstractController
     public function cardsAction(ServerRequestInterface $request): ResponseInterface
     {
         $view = $this->initializeStandaloneView($request, 'Maintenance/Cards.html');
-        $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
-        $view->assignMultiple([
-            'clearAllCacheOpcodeCaches' => (new OpcodeCacheService())->getAllActive(),
-            'clearTablesClearToken' => $formProtection->generateToken('installTool', 'clearTablesClear'),
-            'clearTypo3tempFilesStats' => (new Typo3tempFileService())->getDirectoryStatistics(),
-            'clearTypo3tempFilesToken' => $formProtection->generateToken('installTool', 'clearTypo3tempFiles'),
-            'createAdminToken' => $formProtection->generateToken('installTool', 'createAdmin'),
-            'databaseAnalyzerExecuteToken' => $formProtection->generateToken('installTool', 'databaseAnalyzerExecute'),
-        ]);
         return new JsonResponse([
             'success' => true,
             'html' => $view->render(),
@@ -74,7 +69,7 @@ class MaintenanceController extends AbstractController
         GeneralUtility::makeInstance(ClearCacheService::class)->clearAll();
         GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
         $messageQueue = (new FlashMessageQueue('install'))->enqueue(
-            new FlashMessage('Successfully cleared all caches and all available opcode caches.')
+            new FlashMessage('Successfully cleared all caches and all available opcode caches.', 'Caches cleared')
         );
         return new JsonResponse([
             'success' => true,
@@ -83,7 +78,35 @@ class MaintenanceController extends AbstractController
     }
 
     /**
-     * Clear Processed Files
+     * Clear typo3temp files statistics action
+     *
+     * @param ServerRequestInterface $request
+     * @return ResponseInterface
+     */
+    public function clearTypo3tempFilesStatsAction(ServerRequestInterface $request): ResponseInterface
+    {
+        $view = $this->initializeStandaloneView($request, 'Maintenance/ClearTypo3tempFiles.html');
+        $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
+        $view->assignMultiple([
+            'clearTypo3tempFilesToken' => $formProtection->generateToken('installTool', 'clearTypo3tempFiles'),
+        ]);
+        return new JsonResponse(
+            [
+                'success' => true,
+                'stats' => (new Typo3tempFileService())->getDirectoryStatistics(),
+                'html' => $view->render(),
+                'buttons' => [
+                    [
+                        'btnClass' => 'btn-default t3js-clearTypo3temp-stats',
+                        'text' => 'Scan again',
+                    ],
+                ],
+            ]
+        );
+    }
+
+    /**
+     * Clear typo3temp/assets or FAL processed Files
      *
      * @param ServerRequestInterface $request
      * @return ResponseInterface
@@ -93,20 +116,23 @@ class MaintenanceController extends AbstractController
         $messageQueue = new FlashMessageQueue('install');
         $typo3tempFileService = new Typo3tempFileService();
         $folder = $request->getParsedBody()['install']['folder'];
-        if ($folder === '_processed_') {
-            $failedDeletions = $typo3tempFileService->clearProcessedFiles();
+        // storageUid is an optional post param if FAL storages should be cleaned
+        $storageUid = $request->getParsedBody()['install']['storageUid'] ?? null;
+        if ($storageUid === null) {
+            $typo3tempFileService->clearAssetsFolder($folder);
+            $messageQueue->enqueue(new FlashMessage('Cleared files in "' . $folder . '" folder'));
+        } else {
+            $storageUid = (int)$storageUid;
+            $failedDeletions = $typo3tempFileService->clearProcessedFiles($storageUid);
             if ($failedDeletions) {
                 $messageQueue->enqueue(new FlashMessage(
-                    'Failed to delete ' . $failedDeletions . ' processed files. See TYPO3 log (by default typo3temp/var/logs/typo3_*.log)',
+                    'Failed to delete ' . $failedDeletions . ' processed files. See TYPO3 log (by default typo3temp/var/log/typo3_*.log)',
                     '',
                     FlashMessage::ERROR
                 ));
             } else {
                 $messageQueue->enqueue(new FlashMessage('Cleared processed files'));
             }
-        } else {
-            $typo3tempFileService->clearAssetsFolder($folder);
-            $messageQueue->enqueue(new FlashMessage('Cleared files in "' . $folder . '" folder'));
         }
         return new JsonResponse([
             'success' => true,
@@ -122,16 +148,15 @@ class MaintenanceController extends AbstractController
     public function dumpAutoloadAction(): ResponseInterface
     {
         $messageQueue = new FlashMessageQueue('install');
-        if (Bootstrap::usesComposerClassLoading()) {
+        if (Environment::isComposerMode()) {
             $messageQueue->enqueue(new FlashMessage(
-                '',
                 'Skipped generating additional class loading information in composer mode.',
+                '',
                 FlashMessage::NOTICE
             ));
         } else {
             ClassLoadingInformation::dumpClassLoadingInformation();
             $messageQueue->enqueue(new FlashMessage(
-                '',
                 'Successfully dumped class loading information for extensions.'
             ));
         }
@@ -142,18 +167,46 @@ class MaintenanceController extends AbstractController
     }
 
     /**
+     * Get main database analyzer modal HTML
+     *
+     * @param ServerRequestInterface $request
+     * @return ResponseInterface
+     */
+    public function databaseAnalyzerAction(ServerRequestInterface $request): ResponseInterface
+    {
+        $view = $this->initializeStandaloneView($request, 'Maintenance/DatabaseAnalyzer.html');
+        $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
+        $view->assignMultiple([
+            'databaseAnalyzerExecuteToken' => $formProtection->generateToken('installTool', 'databaseAnalyzerExecute'),
+        ]);
+        return new JsonResponse([
+            'success' => true,
+            'html' => $view->render(),
+            'buttons' => [
+                [
+                    'btnClass' => 'btn-default t3js-databaseAnalyzer-analyze',
+                    'text' => 'Run database compare again',
+                ], [
+                    'btnClass' => 'btn-warning t3js-databaseAnalyzer-execute',
+                    'text' => 'Apply selected changes',
+                ],
+            ],
+        ]);
+    }
+
+    /**
      * Analyze current database situation
      *
+     * @param ServerRequestInterface $request
      * @return ResponseInterface
      */
-    public function databaseAnalyzerAnalyzeAction(): ResponseInterface
+    public function databaseAnalyzerAnalyzeAction(ServerRequestInterface $request): ResponseInterface
     {
-        $this->loadExtLocalconfDatabaseAndExtTables();
+        $container = $this->loadExtLocalconfDatabaseAndExtTables();
         $messageQueue = new FlashMessageQueue('install');
-
         $suggestions = [];
         try {
-            $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
+            $sqlReader = $container->get(SqlReader::class);
             $sqlStatements = $sqlReader->getCreateTableStatementArray($sqlReader->getTablesDefinitionString());
             $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
             $addCreateChange = $schemaMigrationService->getUpdateSuggestions($sqlStatements);
@@ -283,14 +336,9 @@ class MaintenanceController extends AbstractController
                 }
                 $suggestions[] = $suggestion;
             }
-
-            $messageQueue->enqueue(new FlashMessage(
-                '',
-                'Analyzed current database'
-            ));
         } catch (StatementException $e) {
             $messageQueue->enqueue(new FlashMessage(
-                '',
+                $e->getMessage(),
                 'Database analysis failed',
                 FlashMessage::ERROR
             ));
@@ -310,7 +358,7 @@ class MaintenanceController extends AbstractController
      */
     public function databaseAnalyzerExecuteAction(ServerRequestInterface $request): ResponseInterface
     {
-        $this->loadExtLocalconfDatabaseAndExtTables();
+        $container = $this->loadExtLocalconfDatabaseAndExtTables();
         $messageQueue = new FlashMessageQueue('install');
         $selectedHashes = $request->getParsedBody()['install']['hashes'] ?? [];
         if (empty($selectedHashes)) {
@@ -320,7 +368,7 @@ class MaintenanceController extends AbstractController
                 FlashMessage::WARNING
             ));
         } else {
-            $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
+            $sqlReader = $container->get(SqlReader::class);
             $sqlStatements = $sqlReader->getCreateTableStatementArray($sqlReader->getTablesDefinitionString());
             $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
             $statementHashesToPerform = array_flip($selectedHashes);
@@ -334,7 +382,7 @@ class MaintenanceController extends AbstractController
                 ));
             }
             $messageQueue->enqueue(new FlashMessage(
-                '',
+                'Executed database updates',
                 'Executed database updates'
             ));
         }
@@ -347,13 +395,26 @@ class MaintenanceController extends AbstractController
     /**
      * Clear table overview statistics action
      *
+     * @param ServerRequestInterface $request
      * @return ResponseInterface
      */
-    public function clearTablesStatsAction(): ResponseInterface
+    public function clearTablesStatsAction(ServerRequestInterface $request): ResponseInterface
     {
+        $view = $this->initializeStandaloneView($request, 'Maintenance/ClearTables.html');
+        $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
+        $view->assignMultiple([
+            'clearTablesClearToken' => $formProtection->generateToken('installTool', 'clearTablesClear'),
+        ]);
         return new JsonResponse([
             'success' => true,
             'stats' => (new ClearTableService())->getTableStatistics(),
+            'html' => $view->render(),
+            'buttons' => [
+                [
+                    'btnClass' => 'btn-default t3js-clearTables-stats',
+                    'text' => 'Scan again',
+                ],
+            ],
         ]);
     }
 
@@ -362,6 +423,7 @@ class MaintenanceController extends AbstractController
      *
      * @param ServerRequestInterface $request
      * @return ResponseInterface
+     * @throws \RuntimeException
      */
     public function clearTablesClearAction(ServerRequestInterface $request): ResponseInterface
     {
@@ -381,6 +443,30 @@ class MaintenanceController extends AbstractController
             'status' => $messageQueue
         ]);
     }
+    /**
+     * Create Admin Get Data action
+     *
+     * @param ServerRequestInterface $request
+     * @return ResponseInterface
+     */
+    public function createAdminGetDataAction(ServerRequestInterface $request): ResponseInterface
+    {
+        $view = $this->initializeStandaloneView($request, 'Maintenance/CreateAdmin.html');
+        $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
+        $view->assignMultiple([
+            'createAdminToken' => $formProtection->generateToken('installTool', 'createAdmin'),
+        ]);
+        return new JsonResponse([
+            'success' => true,
+            'html' => $view->render(),
+            'buttons' => [
+                [
+                    'btnClass' => 'btn-default t3js-createAdmin-create',
+                    'text' => 'Create administrator user',
+                ],
+            ],
+        ]);
+    }
 
     /**
      * Create a backend administrator from given username and password
@@ -393,7 +479,10 @@ class MaintenanceController extends AbstractController
         $username = preg_replace('/\\s/i', '', $request->getParsedBody()['install']['userName']);
         $password = $request->getParsedBody()['install']['userPassword'];
         $passwordCheck = $request->getParsedBody()['install']['userPasswordCheck'];
+        $isSystemMaintainer = ((bool)$request->getParsedBody()['install']['userSystemMaintainer'] == '1') ? true : false;
+
         $messages = new FlashMessageQueue('install');
+
         if (strlen($username) < 1) {
             $messages->enqueue(new FlashMessage(
                 'No valid username given.',
@@ -427,8 +516,8 @@ class MaintenanceController extends AbstractController
                     FlashMessage::ERROR
                 ));
             } else {
-                $saltFactory = SaltFactory::getSaltingInstance(null, 'BE');
-                $hashedPassword = $saltFactory->getHashedPassword($password);
+                $hashInstance = GeneralUtility::makeInstance(PasswordHashFactory::class)->getDefaultHashInstance('BE');
+                $hashedPassword = $hashInstance->getHashedPassword($password);
                 $adminUserFields = [
                     'username' => $username,
                     'password' => $hashedPassword,
@@ -437,6 +526,26 @@ class MaintenanceController extends AbstractController
                     'crdate' => $GLOBALS['EXEC_TIME']
                 ];
                 $connectionPool->getConnectionForTable('be_users')->insert('be_users', $adminUserFields);
+
+                if ($isSystemMaintainer) {
+
+                    // Get the new admin user uid juste created
+                    $newAdminUserUid = (int)$connectionPool->getConnectionForTable('be_users')->lastInsertId('be_users');
+
+                    // Get the list of the existing systemMaintainer
+                    $existingSystemMaintainersList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? [];
+
+                    // Add the new admin user to the existing systemMaintainer list
+                    $newSystemMaintainersList = $existingSystemMaintainersList;
+                    $newSystemMaintainersList[] = $newAdminUserUid;
+
+                    // Update the LocalConfiguration.php file with the new list
+                    $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
+                    $configurationManager->setLocalConfigurationValuesByPathValuePairs(
+                        ['SYS/systemMaintainers' => $newSystemMaintainersList]
+                    );
+                }
+
                 $messages->enqueue(new FlashMessage(
                     '',
                     'Administrator created with username "' . $username . '".'
@@ -450,6 +559,218 @@ class MaintenanceController extends AbstractController
     }
 
     /**
+     * Entry action of language packs module gets
+     * * list of available languages with details like active or not and last update
+     * * list of loaded extensions
+     *
+     * @param ServerRequestInterface $request
+     * @return ResponseInterface
+     */
+    public function languagePacksGetDataAction(ServerRequestInterface $request): ResponseInterface
+    {
+        $view = $this->initializeStandaloneView($request, 'Maintenance/LanguagePacks.html');
+        $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
+        $view->assignMultiple([
+            'languagePacksActivateLanguageToken' => $formProtection->generateToken('installTool', 'languagePacksActivateLanguage'),
+            'languagePacksDeactivateLanguageToken' => $formProtection->generateToken('installTool', 'languagePacksDeactivateLanguage'),
+            'languagePacksUpdatePackToken' => $formProtection->generateToken('installTool', 'languagePacksUpdatePack'),
+            'languagePacksUpdateIsoTimesToken' => $formProtection->generateToken('installTool', 'languagePacksUpdateIsoTimes'),
+        ]);
+        // This action needs TYPO3_CONF_VARS for full GeneralUtility::getUrl() config
+        $this->loadExtLocalconfDatabaseAndExtTables();
+        $languagePacksService = GeneralUtility::makeInstance(LanguagePackService::class);
+        $languagePacksService->updateMirrorBaseUrl();
+        $extensions = $languagePacksService->getExtensionLanguagePackDetails();
+        return new JsonResponse([
+            'success' => true,
+            'languages' => $languagePacksService->getLanguageDetails(),
+            'extensions' => $extensions,
+            'activeLanguages' => $languagePacksService->getActiveLanguages(),
+            'activeExtensions' => array_column($extensions, 'key'),
+            'html' => $view->render(),
+        ]);
+    }
+
+    /**
+     * Activate a language and any possible dependency it may have
+     *
+     * @param ServerRequestInterface $request
+     * @return ResponseInterface
+     */
+    public function languagePacksActivateLanguageAction(ServerRequestInterface $request): ResponseInterface
+    {
+        $messageQueue = new FlashMessageQueue('install');
+        $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
+        $locales = GeneralUtility::makeInstance(Locales::class);
+        $availableLanguages = $languagePackService->getAvailableLanguages();
+        $activeLanguages = $languagePackService->getActiveLanguages();
+        $iso = $request->getParsedBody()['install']['iso'];
+        $activateArray = [];
+        foreach ($availableLanguages as $availableIso => $name) {
+            if ($availableIso === $iso && !in_array($availableIso, $activeLanguages, true)) {
+                $activateArray[] = $iso;
+                $dependencies = $locales->getLocaleDependencies($availableIso);
+                if (!empty($dependencies)) {
+                    foreach ($dependencies as $dependency) {
+                        if (!in_array($dependency, $activeLanguages, true)) {
+                            $activateArray[] = $dependency;
+                        }
+                    }
+                }
+            }
+        }
+        if (!empty($activateArray)) {
+            $activeLanguages = array_merge($activeLanguages, $activateArray);
+            sort($activeLanguages);
+            $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
+            $configurationManager->setLocalConfigurationValueByPath(
+                'EXTCONF/lang',
+                ['availableLanguages' => $activeLanguages]
+            );
+            $activationArray = [];
+            foreach ($activateArray as $activateIso) {
+                $activationArray[] = $availableLanguages[$activateIso] . ' (' . $activateIso . ')';
+            }
+            $messageQueue->enqueue(
+                new FlashMessage(
+                    'These languages have been activated: ' . implode(', ', $activationArray)
+                )
+            );
+        } else {
+            $messageQueue->enqueue(
+                new FlashMessage('Language with ISO code "' . $iso . '" not found or already active.', '', FlashMessage::ERROR)
+            );
+        }
+        return new JsonResponse([
+            'success' => true,
+            'status' => $messageQueue,
+        ]);
+    }
+
+    /**
+     * Deactivate a language if no other active language depends on it
+     *
+     * @param ServerRequestInterface $request
+     * @return ResponseInterface
+     * @throws \RuntimeException
+     */
+    public function languagePacksDeactivateLanguageAction(ServerRequestInterface $request): ResponseInterface
+    {
+        $messageQueue = new FlashMessageQueue('install');
+        $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
+        $locales = GeneralUtility::makeInstance(Locales::class);
+        $availableLanguages = $languagePackService->getAvailableLanguages();
+        $activeLanguages = $languagePackService->getActiveLanguages();
+        $iso = $request->getParsedBody()['install']['iso'];
+        if (empty($iso)) {
+            throw new \RuntimeException('No iso code given', 1520109807);
+        }
+        $otherActiveLanguageDependencies = [];
+        foreach ($activeLanguages as $activeLanguage) {
+            if ($activeLanguage === $iso) {
+                continue;
+            }
+            $dependencies = $locales->getLocaleDependencies($activeLanguage);
+            if (in_array($iso, $dependencies, true)) {
+                $otherActiveLanguageDependencies[] = $activeLanguage;
+            }
+        }
+        if (!empty($otherActiveLanguageDependencies)) {
+            // Error: Must disable dependencies first
+            $dependentArray = [];
+            foreach ($otherActiveLanguageDependencies as $dependency) {
+                $dependentArray[] = $availableLanguages[$dependency] . ' (' . $dependency . ')';
+            }
+            $messageQueue->enqueue(
+                new FlashMessage(
+                    'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" can not be deactivated. These'
+                    . ' other languages depend on it and need to be deactivated before:'
+                    . implode(', ', $dependentArray),
+                    '',
+                    FlashMessage::ERROR
+                )
+            );
+        } else {
+            if (in_array($iso, $activeLanguages, true)) {
+                // Deactivate this language
+                $newActiveLanguages = [];
+                foreach ($activeLanguages as $activeLanguage) {
+                    if ($activeLanguage === $iso) {
+                        continue;
+                    }
+                    $newActiveLanguages[] = $activeLanguage;
+                }
+                $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
+                $configurationManager->setLocalConfigurationValueByPath(
+                    'EXTCONF/lang',
+                    ['availableLanguages' => $newActiveLanguages]
+                );
+                $messageQueue->enqueue(
+                    new FlashMessage(
+                        'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" has been deactivated'
+                    )
+                );
+            } else {
+                $messageQueue->enqueue(
+                    new FlashMessage(
+                        'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" has not been deactivated',
+                        '',
+                        FlashMessage::ERROR
+                    )
+                );
+            }
+        }
+        return new JsonResponse([
+            'success' => true,
+            'status' => $messageQueue,
+        ]);
+    }
+
+    /**
+     * Update a pack of one extension and one language
+     *
+     * @param ServerRequestInterface $request
+     * @return ResponseInterface
+     * @throws \RuntimeException
+     */
+    public function languagePacksUpdatePackAction(ServerRequestInterface $request): ResponseInterface
+    {
+        $this->loadExtLocalconfDatabaseAndExtTables();
+        $iso = $request->getParsedBody()['install']['iso'];
+        $key = $request->getParsedBody()['install']['extension'];
+        $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
+        return new JsonResponse([
+            'success' => true,
+            'packResult' => $languagePackService->languagePackDownload($key, $iso)
+        ]);
+    }
+
+    /**
+     * Set "last updated" time in registry for fully updated language packs.
+     *
+     * @param ServerRequestInterface $request
+     * @return ResponseInterface
+     */
+    public function languagePacksUpdateIsoTimesAction(ServerRequestInterface $request): ResponseInterface
+    {
+        $isos = $request->getParsedBody()['install']['isos'];
+        $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
+        $languagePackService->setLastUpdatedIsoCode($isos);
+
+        // The cache manager is already instantiated in the install tool
+        // with some hacked settings to disable caching of extbase and fluid.
+        // We want a "fresh" object here to operate on a different cache setup.
+        // cacheManager implements SingletonInterface, so the only way to get a "fresh"
+        // instance is by circumventing makeInstance and/or the objectManager and
+        // using new directly!
+        $cacheManager = new \TYPO3\CMS\Core\Cache\CacheManager();
+        $cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
+        $cacheManager->getCache('l10n')->flush();
+
+        return new JsonResponse(['success' => true]);
+    }
+
+    /**
      * Set 'uc' field of all backend users to empty string
      *
      * @return ResponseInterface
@@ -463,8 +784,8 @@ class MaintenanceController extends AbstractController
             ->execute();
         $messageQueue = new FlashMessageQueue('install');
         $messageQueue->enqueue(new FlashMessage(
-            '',
-            'Reset all backend users preferences'
+            'Preferences of all backend users have been reset',
+            'Reset preferences of all backend users'
         ));
         return new JsonResponse([
             'success' => true,