[BUGFIX] Database compare must show errors
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / MaintenanceController.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Install\Controller;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use TYPO3\CMS\Core\Configuration\ConfigurationManager;
21 use TYPO3\CMS\Core\Core\ClassLoadingInformation;
22 use TYPO3\CMS\Core\Core\Environment;
23 use TYPO3\CMS\Core\Database\ConnectionPool;
24 use TYPO3\CMS\Core\Database\Schema\Exception\StatementException;
25 use TYPO3\CMS\Core\Database\Schema\SchemaMigrator;
26 use TYPO3\CMS\Core\Database\Schema\SqlReader;
27 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
28 use TYPO3\CMS\Core\FormProtection\InstallToolFormProtection;
29 use TYPO3\CMS\Core\Http\JsonResponse;
30 use TYPO3\CMS\Core\Localization\Locales;
31 use TYPO3\CMS\Core\Messaging\FlashMessage;
32 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
33 use TYPO3\CMS\Core\Service\OpcodeCacheService;
34 use TYPO3\CMS\Core\Utility\GeneralUtility;
35 use TYPO3\CMS\Install\Service\ClearCacheService;
36 use TYPO3\CMS\Install\Service\ClearTableService;
37 use TYPO3\CMS\Install\Service\LanguagePackService;
38 use TYPO3\CMS\Install\Service\Typo3tempFileService;
39 use TYPO3\CMS\Saltedpasswords\Salt\SaltFactory;
40
41 /**
42 * Maintenance controller
43 */
44 class MaintenanceController extends AbstractController
45 {
46 /**
47 * Main "show the cards" view
48 *
49 * @param ServerRequestInterface $request
50 * @return ResponseInterface
51 */
52 public function cardsAction(ServerRequestInterface $request): ResponseInterface
53 {
54 $view = $this->initializeStandaloneView($request, 'Maintenance/Cards.html');
55 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
56 $view->assignMultiple([
57 'clearAllCacheOpcodeCaches' => (new OpcodeCacheService())->getAllActive(),
58 'clearTablesClearToken' => $formProtection->generateToken('installTool', 'clearTablesClear'),
59 'clearTypo3tempFilesToken' => $formProtection->generateToken('installTool', 'clearTypo3tempFiles'),
60 'createAdminToken' => $formProtection->generateToken('installTool', 'createAdmin'),
61 'databaseAnalyzerExecuteToken' => $formProtection->generateToken('installTool', 'databaseAnalyzerExecute'),
62 'languagePacksActivateLanguageToken' => $formProtection->generateToken('installTool', 'languagePacksActivateLanguage'),
63 'languagePacksDeactivateLanguageToken' => $formProtection->generateToken('installTool', 'languagePacksDeactivateLanguage'),
64 'languagePacksUpdatePackToken' => $formProtection->generateToken('installTool', 'languagePacksUpdatePack'),
65 'languagePacksUpdateIsoTimesToken' => $formProtection->generateToken('installTool', 'languagePacksUpdateIsoTimes'),
66 ]);
67 return new JsonResponse([
68 'success' => true,
69 'html' => $view->render(),
70 ]);
71 }
72
73 /**
74 * Clear cache framework and opcode caches
75 *
76 * @return ResponseInterface
77 */
78 public function cacheClearAllAction(): ResponseInterface
79 {
80 GeneralUtility::makeInstance(ClearCacheService::class)->clearAll();
81 GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
82 $messageQueue = (new FlashMessageQueue('install'))->enqueue(
83 new FlashMessage('Successfully cleared all caches and all available opcode caches.')
84 );
85 return new JsonResponse([
86 'success' => true,
87 'status' => $messageQueue,
88 ]);
89 }
90
91 /**
92 * Clear typo3temp files statistics action
93 *
94 * @return ResponseInterface
95 */
96 public function clearTypo3tempFilesStatsAction(): ResponseInterface
97 {
98 return new JsonResponse(
99 [
100 'success' => true,
101 'stats' => (new Typo3tempFileService())->getDirectoryStatistics(),
102 ]
103 );
104 }
105
106 /**
107 * Clear Processed Files
108 *
109 * @param ServerRequestInterface $request
110 * @return ResponseInterface
111 */
112 public function clearTypo3tempFilesAction(ServerRequestInterface $request): ResponseInterface
113 {
114 $messageQueue = new FlashMessageQueue('install');
115 $typo3tempFileService = new Typo3tempFileService();
116 $folder = $request->getParsedBody()['install']['folder'];
117 if ($folder === '_processed_') {
118 $failedDeletions = $typo3tempFileService->clearProcessedFiles();
119 if ($failedDeletions) {
120 $messageQueue->enqueue(new FlashMessage(
121 'Failed to delete ' . $failedDeletions . ' processed files. See TYPO3 log (by default typo3temp/var/logs/typo3_*.log)',
122 '',
123 FlashMessage::ERROR
124 ));
125 } else {
126 $messageQueue->enqueue(new FlashMessage('Cleared processed files'));
127 }
128 } else {
129 $typo3tempFileService->clearAssetsFolder($folder);
130 $messageQueue->enqueue(new FlashMessage('Cleared files in "' . $folder . '" folder'));
131 }
132 return new JsonResponse([
133 'success' => true,
134 'status' => $messageQueue,
135 ]);
136 }
137
138 /**
139 * Dump autoload information
140 *
141 * @return ResponseInterface
142 */
143 public function dumpAutoloadAction(): ResponseInterface
144 {
145 $messageQueue = new FlashMessageQueue('install');
146 if (Environment::isComposerMode()) {
147 $messageQueue->enqueue(new FlashMessage(
148 '',
149 'Skipped generating additional class loading information in composer mode.',
150 FlashMessage::NOTICE
151 ));
152 } else {
153 ClassLoadingInformation::dumpClassLoadingInformation();
154 $messageQueue->enqueue(new FlashMessage(
155 '',
156 'Successfully dumped class loading information for extensions.'
157 ));
158 }
159 return new JsonResponse([
160 'success' => true,
161 'status' => $messageQueue,
162 ]);
163 }
164
165 /**
166 * Analyze current database situation
167 *
168 * @return ResponseInterface
169 */
170 public function databaseAnalyzerAnalyzeAction(): ResponseInterface
171 {
172 $this->loadExtLocalconfDatabaseAndExtTables();
173 $messageQueue = new FlashMessageQueue('install');
174
175 $suggestions = [];
176 try {
177 $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
178 $sqlStatements = $sqlReader->getCreateTableStatementArray($sqlReader->getTablesDefinitionString());
179 $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
180 $addCreateChange = $schemaMigrationService->getUpdateSuggestions($sqlStatements);
181
182 // Aggregate the per-connection statements into one flat array
183 $addCreateChange = array_merge_recursive(...array_values($addCreateChange));
184 if (!empty($addCreateChange['create_table'])) {
185 $suggestion = [
186 'key' => 'addTable',
187 'label' => 'Add tables',
188 'enabled' => true,
189 'children' => [],
190 ];
191 foreach ($addCreateChange['create_table'] as $hash => $statement) {
192 $suggestion['children'][] = [
193 'hash' => $hash,
194 'statement' => $statement,
195 ];
196 }
197 $suggestions[] = $suggestion;
198 }
199 if (!empty($addCreateChange['add'])) {
200 $suggestion = [
201 'key' => 'addField',
202 'label' => 'Add fields to tables',
203 'enabled' => true,
204 'children' => [],
205 ];
206 foreach ($addCreateChange['add'] as $hash => $statement) {
207 $suggestion['children'][] = [
208 'hash' => $hash,
209 'statement' => $statement,
210 ];
211 }
212 $suggestions[] = $suggestion;
213 }
214 if (!empty($addCreateChange['change'])) {
215 $suggestion = [
216 'key' => 'change',
217 'label' => 'Change fields',
218 'enabled' => false,
219 'children' => [],
220 ];
221 foreach ($addCreateChange['change'] as $hash => $statement) {
222 $child = [
223 'hash' => $hash,
224 'statement' => $statement,
225 ];
226 if (isset($addCreateChange['change_currentValue'][$hash])) {
227 $child['current'] = $addCreateChange['change_currentValue'][$hash];
228 }
229 $suggestion['children'][] = $child;
230 }
231 $suggestions[] = $suggestion;
232 }
233
234 // Difference from current to expected
235 $dropRename = $schemaMigrationService->getUpdateSuggestions($sqlStatements, true);
236
237 // Aggregate the per-connection statements into one flat array
238 $dropRename = array_merge_recursive(...array_values($dropRename));
239 if (!empty($dropRename['change_table'])) {
240 $suggestion = [
241 'key' => 'renameTableToUnused',
242 'label' => 'Remove tables (rename with prefix)',
243 'enabled' => false,
244 'children' => [],
245 ];
246 foreach ($dropRename['change_table'] as $hash => $statement) {
247 $child = [
248 'hash' => $hash,
249 'statement' => $statement,
250 ];
251 if (!empty($dropRename['tables_count'][$hash])) {
252 $child['rowCount'] = $dropRename['tables_count'][$hash];
253 }
254 $suggestion['children'][] = $child;
255 }
256 $suggestions[] = $suggestion;
257 }
258 if (!empty($dropRename['change'])) {
259 $suggestion = [
260 'key' => 'renameTableFieldToUnused',
261 'label' => 'Remove unused fields (rename with prefix)',
262 'enabled' => false,
263 'children' => [],
264 ];
265 foreach ($dropRename['change'] as $hash => $statement) {
266 $suggestion['children'][] = [
267 'hash' => $hash,
268 'statement' => $statement,
269 ];
270 }
271 $suggestions[] = $suggestion;
272 }
273 if (!empty($dropRename['drop'])) {
274 $suggestion = [
275 'key' => 'deleteField',
276 'label' => 'Drop fields (really!)',
277 'enabled' => false,
278 'children' => [],
279 ];
280 foreach ($dropRename['drop'] as $hash => $statement) {
281 $suggestion['children'][] = [
282 'hash' => $hash,
283 'statement' => $statement,
284 ];
285 }
286 $suggestions[] = $suggestion;
287 }
288 if (!empty($dropRename['drop_table'])) {
289 $suggestion = [
290 'key' => 'deleteTable',
291 'label' => 'Drop tables (really!)',
292 'enabled' => false,
293 'children' => [],
294 ];
295 foreach ($dropRename['drop_table'] as $hash => $statement) {
296 $child = [
297 'hash' => $hash,
298 'statement' => $statement,
299 ];
300 if (!empty($dropRename['tables_count'][$hash])) {
301 $child['rowCount'] = $dropRename['tables_count'][$hash];
302 }
303 $suggestion['children'][] = $child;
304 }
305 $suggestions[] = $suggestion;
306 }
307
308 $messageQueue->enqueue(new FlashMessage(
309 '',
310 'Analyzed current database'
311 ));
312 } catch (StatementException $e) {
313 $messageQueue->enqueue(new FlashMessage(
314 $e->getMessage(),
315 'Database analysis failed',
316 FlashMessage::ERROR
317 ));
318 }
319 return new JsonResponse([
320 'success' => true,
321 'status' => $messageQueue,
322 'suggestions' => $suggestions,
323 ]);
324 }
325
326 /**
327 * Apply selected database changes
328 *
329 * @param ServerRequestInterface $request
330 * @return ResponseInterface
331 */
332 public function databaseAnalyzerExecuteAction(ServerRequestInterface $request): ResponseInterface
333 {
334 $this->loadExtLocalconfDatabaseAndExtTables();
335 $messageQueue = new FlashMessageQueue('install');
336 $selectedHashes = $request->getParsedBody()['install']['hashes'] ?? [];
337 if (empty($selectedHashes)) {
338 $messageQueue->enqueue(new FlashMessage(
339 '',
340 'No database changes selected',
341 FlashMessage::WARNING
342 ));
343 } else {
344 $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
345 $sqlStatements = $sqlReader->getCreateTableStatementArray($sqlReader->getTablesDefinitionString());
346 $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
347 $statementHashesToPerform = array_flip($selectedHashes);
348 $results = $schemaMigrationService->migrate($sqlStatements, $statementHashesToPerform);
349 // Create error flash messages if any
350 foreach ($results as $errorMessage) {
351 $messageQueue->enqueue(new FlashMessage(
352 'Error: ' . $errorMessage,
353 'Database update failed',
354 FlashMessage::ERROR
355 ));
356 }
357 $messageQueue->enqueue(new FlashMessage(
358 '',
359 'Executed database updates'
360 ));
361 }
362 return new JsonResponse([
363 'success' => true,
364 'status' => $messageQueue,
365 ]);
366 }
367
368 /**
369 * Clear table overview statistics action
370 *
371 * @return ResponseInterface
372 */
373 public function clearTablesStatsAction(): ResponseInterface
374 {
375 return new JsonResponse([
376 'success' => true,
377 'stats' => (new ClearTableService())->getTableStatistics(),
378 ]);
379 }
380
381 /**
382 * Truncate a specific table
383 *
384 * @param ServerRequestInterface $request
385 * @return ResponseInterface
386 */
387 public function clearTablesClearAction(ServerRequestInterface $request): ResponseInterface
388 {
389 $table = $request->getParsedBody()['install']['table'];
390 if (empty($table)) {
391 throw new \RuntimeException(
392 'No table name given',
393 1501944076
394 );
395 }
396 (new ClearTableService())->clearSelectedTable($table);
397 $messageQueue = (new FlashMessageQueue('install'))->enqueue(
398 new FlashMessage('Cleared table')
399 );
400 return new JsonResponse([
401 'success' => true,
402 'status' => $messageQueue
403 ]);
404 }
405
406 /**
407 * Create a backend administrator from given username and password
408 *
409 * @param ServerRequestInterface $request
410 * @return ResponseInterface
411 */
412 public function createAdminAction(ServerRequestInterface $request): ResponseInterface
413 {
414 $username = preg_replace('/\\s/i', '', $request->getParsedBody()['install']['userName']);
415 $password = $request->getParsedBody()['install']['userPassword'];
416 $passwordCheck = $request->getParsedBody()['install']['userPasswordCheck'];
417 $isSystemMaintainer = ((bool)$request->getParsedBody()['install']['userSystemMaintainer'] == '1') ? true : false;
418
419 $messages = new FlashMessageQueue('install');
420
421 if (strlen($username) < 1) {
422 $messages->enqueue(new FlashMessage(
423 'No valid username given.',
424 'Administrator user not created',
425 FlashMessage::ERROR
426 ));
427 } elseif ($password !== $passwordCheck) {
428 $messages->enqueue(new FlashMessage(
429 'Passwords do not match.',
430 'Administrator user not created',
431 FlashMessage::ERROR
432 ));
433 } elseif (strlen($password) < 8) {
434 $messages->enqueue(new FlashMessage(
435 'Password must be at least eight characters long.',
436 'Administrator user not created',
437 FlashMessage::ERROR
438 ));
439 } else {
440 $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
441 $userExists = $connectionPool->getConnectionForTable('be_users')
442 ->count(
443 'uid',
444 'be_users',
445 ['username' => $username]
446 );
447 if ($userExists) {
448 $messages->enqueue(new FlashMessage(
449 'A user with username "' . $username . '" exists already.',
450 'Administrator user not created',
451 FlashMessage::ERROR
452 ));
453 } else {
454 $saltFactory = SaltFactory::getSaltingInstance(null, 'BE');
455 $hashedPassword = $saltFactory->getHashedPassword($password);
456 $adminUserFields = [
457 'username' => $username,
458 'password' => $hashedPassword,
459 'admin' => 1,
460 'tstamp' => $GLOBALS['EXEC_TIME'],
461 'crdate' => $GLOBALS['EXEC_TIME']
462 ];
463 $connectionPool->getConnectionForTable('be_users')->insert('be_users', $adminUserFields);
464
465 if ($isSystemMaintainer) {
466
467 // Get the new admin user uid juste created
468 $newAdminUserUid = (int)$connectionPool->getConnectionForTable('be_users')->lastInsertId('be_users');
469
470 // Get the list of the existing systemMaintainer
471 $existingSystemMaintainersList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? [];
472
473 // Add the new admin user to the existing systemMaintainer list
474 $newSystemMaintainersList = $existingSystemMaintainersList;
475 $newSystemMaintainersList[] = $newAdminUserUid;
476
477 // Update the LocalConfiguration.php file with the new list
478 $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
479 $configurationManager->setLocalConfigurationValuesByPathValuePairs(
480 ['SYS/systemMaintainers' => $newSystemMaintainersList]
481 );
482 }
483
484 $messages->enqueue(new FlashMessage(
485 '',
486 'Administrator created with username "' . $username . '".'
487 ));
488 }
489 }
490 return new JsonResponse([
491 'success' => true,
492 'status' => $messages,
493 ]);
494 }
495
496 /**
497 * Entry action of language packs module gets
498 * * list of available languages with details like active or not and last update
499 * * list of loaded extensions
500 *
501 * @return ResponseInterface
502 */
503 public function languagePacksGetDataAction(): ResponseInterface
504 {
505 // This action needs TYPO3_CONF_VARS for full GeneralUtility::getUrl() config
506 $this->loadExtLocalconfDatabaseAndExtTables();
507 $languagePacksService = GeneralUtility::makeInstance(LanguagePackService::class);
508 $languagePacksService->updateMirrorBaseUrl();
509 $extensions = $languagePacksService->getExtensionLanguagePackDetails();
510 return new JsonResponse([
511 'success' => true,
512 'languages' => $languagePacksService->getLanguageDetails(),
513 'extensions' => $extensions,
514 'activeLanguages' => $languagePacksService->getActiveLanguages(),
515 'activeExtensions' => array_column($extensions, 'key'),
516 ]);
517 }
518
519 /**
520 * Activate a language and any possible dependency it may have
521 *
522 * @param ServerRequestInterface $request
523 * @return ResponseInterface
524 */
525 public function languagePacksActivateLanguageAction(ServerRequestInterface $request): ResponseInterface
526 {
527 $messageQueue = new FlashMessageQueue('install');
528 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
529 $locales = GeneralUtility::makeInstance(Locales::class);
530 $availableLanguages = $languagePackService->getAvailableLanguages();
531 $activeLanguages = $languagePackService->getActiveLanguages();
532 $iso = $request->getParsedBody()['install']['iso'];
533 $activateArray = [];
534 foreach ($availableLanguages as $availableIso => $name) {
535 if ($availableIso === $iso && !in_array($availableIso, $activeLanguages, true)) {
536 $activateArray[] = $iso;
537 $dependencies = $locales->getLocaleDependencies($availableIso);
538 if (!empty($dependencies)) {
539 foreach ($dependencies as $dependency) {
540 if (!in_array($dependency, $activeLanguages, true)) {
541 $activateArray[] = $dependency;
542 }
543 }
544 }
545 }
546 }
547 if (!empty($activateArray)) {
548 $activeLanguages = array_merge($activeLanguages, $activateArray);
549 sort($activeLanguages);
550 $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
551 $configurationManager->setLocalConfigurationValueByPath(
552 'EXTCONF/lang',
553 ['availableLanguages' => $activeLanguages]
554 );
555 $activationArray = [];
556 foreach ($activateArray as $activateIso) {
557 $activationArray[] = $availableLanguages[$activateIso] . ' (' . $activateIso . ')';
558 }
559 $messageQueue->enqueue(
560 new FlashMessage(
561 'These languages have been activated: ' . implode(', ', $activationArray)
562 )
563 );
564 } else {
565 $messageQueue->enqueue(
566 new FlashMessage('Language with ISO code "' . $iso . '" not found or already active.', '', FlashMessage::ERROR)
567 );
568 }
569 return new JsonResponse([
570 'success' => true,
571 'status' => $messageQueue,
572 ]);
573 }
574
575 /**
576 * Deactivate a language if no other active language depends on it
577 *
578 * @param ServerRequestInterface $request
579 * @return ResponseInterface
580 * @throws \RuntimeException
581 */
582 public function languagePacksDeactivateLanguageAction(ServerRequestInterface $request): ResponseInterface
583 {
584 $messageQueue = new FlashMessageQueue('install');
585 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
586 $locales = GeneralUtility::makeInstance(Locales::class);
587 $availableLanguages = $languagePackService->getAvailableLanguages();
588 $activeLanguages = $languagePackService->getActiveLanguages();
589 $iso = $request->getParsedBody()['install']['iso'];
590 if (empty($iso)) {
591 throw new \RuntimeException('No iso code given', 1520109807);
592 }
593 $otherActiveLanguageDependencies = [];
594 foreach ($activeLanguages as $activeLanguage) {
595 if ($activeLanguage === $iso) {
596 continue;
597 }
598 $dependencies = $locales->getLocaleDependencies($activeLanguage);
599 if (in_array($iso, $dependencies, true)) {
600 $otherActiveLanguageDependencies[] = $activeLanguage;
601 }
602 }
603 if (!empty($otherActiveLanguageDependencies)) {
604 // Error: Must disable dependencies first
605 $dependentArray = [];
606 foreach ($otherActiveLanguageDependencies as $dependency) {
607 $dependentArray[] = $availableLanguages[$dependency] . ' (' . $dependency . ')';
608 }
609 $messageQueue->enqueue(
610 new FlashMessage(
611 'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" can not be deactivated. These'
612 . ' other languages depend on it and need to be deactivated before:'
613 . implode(', ', $dependentArray),
614 '',
615 FlashMessage::ERROR
616 )
617 );
618 } else {
619 if (in_array($iso, $activeLanguages, true)) {
620 // Deactivate this language
621 $newActiveLanguages = [];
622 foreach ($activeLanguages as $activeLanguage) {
623 if ($activeLanguage === $iso) {
624 continue;
625 }
626 $newActiveLanguages[] = $activeLanguage;
627 }
628 $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
629 $configurationManager->setLocalConfigurationValueByPath(
630 'EXTCONF/lang',
631 ['availableLanguages' => $newActiveLanguages]
632 );
633 $messageQueue->enqueue(
634 new FlashMessage(
635 'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" has been deactivated'
636 )
637 );
638 } else {
639 $messageQueue->enqueue(
640 new FlashMessage(
641 'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" has not been deactivated',
642 '',
643 FlashMessage::ERROR
644 )
645 );
646 }
647 }
648 return new JsonResponse([
649 'success' => true,
650 'status' => $messageQueue,
651 ]);
652 }
653
654 /**
655 * Update a pack of one extension and one language
656 *
657 * @param ServerRequestInterface $request
658 * @return ResponseInterface
659 * @throws \RuntimeException
660 */
661 public function languagePacksUpdatePackAction(ServerRequestInterface $request): ResponseInterface
662 {
663 $iso = $request->getParsedBody()['install']['iso'];
664 $key = $request->getParsedBody()['install']['extension'];
665 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
666 return new JsonResponse([
667 'success' => true,
668 'packResult' => $languagePackService->languagePackDownload($key, $iso)
669 ]);
670 }
671
672 /**
673 * Set "last updated" time in registry for fully updated language packs.
674 *
675 * @param ServerRequestInterface $request
676 * @return ResponseInterface
677 */
678 public function languagePacksUpdateIsoTimesAction(ServerRequestInterface $request): ResponseInterface
679 {
680 $isos = $request->getParsedBody()['install']['isos'];
681 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
682 $languagePackService->setLastUpdatedIsoCode($isos);
683 return new JsonResponse(['success' => true]);
684 }
685
686 /**
687 * Set 'uc' field of all backend users to empty string
688 *
689 * @return ResponseInterface
690 */
691 public function resetBackendUserUcAction(): ResponseInterface
692 {
693 GeneralUtility::makeInstance(ConnectionPool::class)
694 ->getQueryBuilderForTable('be_users')
695 ->update('be_users')
696 ->set('uc', '')
697 ->execute();
698 $messageQueue = new FlashMessageQueue('install');
699 $messageQueue->enqueue(new FlashMessage(
700 '',
701 'Reset all backend users preferences'
702 ));
703 return new JsonResponse([
704 'success' => true,
705 'status' => $messageQueue
706 ]);
707 }
708 }