[BUGFIX] Clean up install tool cardsAction
[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 return new JsonResponse([
56 'success' => true,
57 'html' => $view->render(),
58 ]);
59 }
60
61 /**
62 * Clear cache framework and opcode caches
63 *
64 * @return ResponseInterface
65 */
66 public function cacheClearAllAction(): ResponseInterface
67 {
68 GeneralUtility::makeInstance(ClearCacheService::class)->clearAll();
69 GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
70 $messageQueue = (new FlashMessageQueue('install'))->enqueue(
71 new FlashMessage('Successfully cleared all caches and all available opcode caches.', 'Caches cleared')
72 );
73 return new JsonResponse([
74 'success' => true,
75 'status' => $messageQueue,
76 ]);
77 }
78
79 /**
80 * Clear typo3temp files statistics action
81 *
82 * @param $request ServerRequestInterface
83 * @return ResponseInterface
84 */
85 public function clearTypo3tempFilesStatsAction(ServerRequestInterface $request): ResponseInterface
86 {
87 $view = $this->initializeStandaloneView($request, 'Maintenance/ClearTypo3tempFiles.html');
88 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
89 $view->assignMultiple([
90 'clearTypo3tempFilesToken' => $formProtection->generateToken('installTool', 'clearTypo3tempFiles'),
91 ]);
92 return new JsonResponse(
93 [
94 'success' => true,
95 'stats' => (new Typo3tempFileService())->getDirectoryStatistics(),
96 'html' => $view->render(),
97 ]
98 );
99 }
100
101 /**
102 * Clear Processed Files
103 *
104 * @param ServerRequestInterface $request
105 * @return ResponseInterface
106 */
107 public function clearTypo3tempFilesAction(ServerRequestInterface $request): ResponseInterface
108 {
109 $messageQueue = new FlashMessageQueue('install');
110 $typo3tempFileService = new Typo3tempFileService();
111 $folder = $request->getParsedBody()['install']['folder'];
112 if ($folder === '_processed_') {
113 $failedDeletions = $typo3tempFileService->clearProcessedFiles();
114 if ($failedDeletions) {
115 $messageQueue->enqueue(new FlashMessage(
116 'Failed to delete ' . $failedDeletions . ' processed files. See TYPO3 log (by default typo3temp/var/log/typo3_*.log)',
117 '',
118 FlashMessage::ERROR
119 ));
120 } else {
121 $messageQueue->enqueue(new FlashMessage('Cleared processed files'));
122 }
123 } else {
124 $typo3tempFileService->clearAssetsFolder($folder);
125 $messageQueue->enqueue(new FlashMessage('Cleared files in "' . $folder . '" folder'));
126 }
127 return new JsonResponse([
128 'success' => true,
129 'status' => $messageQueue,
130 ]);
131 }
132
133 /**
134 * Dump autoload information
135 *
136 * @return ResponseInterface
137 */
138 public function dumpAutoloadAction(): ResponseInterface
139 {
140 $messageQueue = new FlashMessageQueue('install');
141 if (Environment::isComposerMode()) {
142 $messageQueue->enqueue(new FlashMessage(
143 'Skipped generating additional class loading information in composer mode.',
144 '',
145 FlashMessage::NOTICE
146 ));
147 } else {
148 ClassLoadingInformation::dumpClassLoadingInformation();
149 $messageQueue->enqueue(new FlashMessage(
150 'Successfully dumped class loading information for extensions.'
151 ));
152 }
153 return new JsonResponse([
154 'success' => true,
155 'status' => $messageQueue,
156 ]);
157 }
158
159 /**
160 * Analyze current database situation
161 *
162 * @param $request ServerRequestInterface
163 * @return ResponseInterface
164 */
165 public function databaseAnalyzerAnalyzeAction(ServerRequestInterface $request): ResponseInterface
166 {
167 $view = $this->initializeStandaloneView($request, 'Maintenance/DatabaseAnalyzer.html');
168 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
169 $view->assignMultiple([
170 'databaseAnalyzerExecuteToken' => $formProtection->generateToken('installTool', 'databaseAnalyzerExecute'),
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 'html' => $view->render(),
324 ]);
325 }
326
327 /**
328 * Apply selected database changes
329 *
330 * @param ServerRequestInterface $request
331 * @return ResponseInterface
332 */
333 public function databaseAnalyzerExecuteAction(ServerRequestInterface $request): ResponseInterface
334 {
335 $this->loadExtLocalconfDatabaseAndExtTables();
336 $messageQueue = new FlashMessageQueue('install');
337 $selectedHashes = $request->getParsedBody()['install']['hashes'] ?? [];
338 if (empty($selectedHashes)) {
339 $messageQueue->enqueue(new FlashMessage(
340 '',
341 'No database changes selected',
342 FlashMessage::WARNING
343 ));
344 } else {
345 $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
346 $sqlStatements = $sqlReader->getCreateTableStatementArray($sqlReader->getTablesDefinitionString());
347 $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
348 $statementHashesToPerform = array_flip($selectedHashes);
349 $results = $schemaMigrationService->migrate($sqlStatements, $statementHashesToPerform);
350 // Create error flash messages if any
351 foreach ($results as $errorMessage) {
352 $messageQueue->enqueue(new FlashMessage(
353 'Error: ' . $errorMessage,
354 'Database update failed',
355 FlashMessage::ERROR
356 ));
357 }
358 $messageQueue->enqueue(new FlashMessage(
359 'Executed database updates',
360 'Executed database updates'
361 ));
362 }
363 return new JsonResponse([
364 'success' => true,
365 'status' => $messageQueue,
366 ]);
367 }
368
369 /**
370 * Clear table overview statistics action
371 *
372 * @param $request ServerRequestInterface
373 * @return ResponseInterface
374 */
375 public function clearTablesStatsAction(ServerRequestInterface $request): ResponseInterface
376 {
377 $view = $this->initializeStandaloneView($request, 'Maintenance/ClearTables.html');
378 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
379 $view->assignMultiple([
380 'clearTablesClearToken' => $formProtection->generateToken('installTool', 'clearTablesClear'),
381 ]);
382 return new JsonResponse([
383 'success' => true,
384 'stats' => (new ClearTableService())->getTableStatistics(),
385 'html' => $view->render(),
386 ]);
387 }
388
389 /**
390 * Truncate a specific table
391 *
392 * @param ServerRequestInterface $request
393 * @return ResponseInterface
394 * @throws \RuntimeException
395 */
396 public function clearTablesClearAction(ServerRequestInterface $request): ResponseInterface
397 {
398 $table = $request->getParsedBody()['install']['table'];
399 if (empty($table)) {
400 throw new \RuntimeException(
401 'No table name given',
402 1501944076
403 );
404 }
405 (new ClearTableService())->clearSelectedTable($table);
406 $messageQueue = (new FlashMessageQueue('install'))->enqueue(
407 new FlashMessage('Cleared table')
408 );
409 return new JsonResponse([
410 'success' => true,
411 'status' => $messageQueue
412 ]);
413 }
414 /**
415 * Create Admin Get Data action
416 *
417 * @param $request ServerRequestInterface
418 * @return ResponseInterface
419 */
420 public function createAdminGetDataAction(ServerRequestInterface $request): ResponseInterface
421 {
422 $view = $this->initializeStandaloneView($request, 'Maintenance/CreateAdmin.html');
423 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
424 $view->assignMultiple([
425 'createAdminToken' => $formProtection->generateToken('installTool', 'createAdmin'),
426 ]);
427 return new JsonResponse([
428 'success' => true,
429 'html' => $view->render(),
430 ]);
431 }
432
433 /**
434 * Create a backend administrator from given username and password
435 *
436 * @param ServerRequestInterface $request
437 * @return ResponseInterface
438 */
439 public function createAdminAction(ServerRequestInterface $request): ResponseInterface
440 {
441 $username = preg_replace('/\\s/i', '', $request->getParsedBody()['install']['userName']);
442 $password = $request->getParsedBody()['install']['userPassword'];
443 $passwordCheck = $request->getParsedBody()['install']['userPasswordCheck'];
444 $isSystemMaintainer = ((bool)$request->getParsedBody()['install']['userSystemMaintainer'] == '1') ? true : false;
445
446 $messages = new FlashMessageQueue('install');
447
448 if (strlen($username) < 1) {
449 $messages->enqueue(new FlashMessage(
450 'No valid username given.',
451 'Administrator user not created',
452 FlashMessage::ERROR
453 ));
454 } elseif ($password !== $passwordCheck) {
455 $messages->enqueue(new FlashMessage(
456 'Passwords do not match.',
457 'Administrator user not created',
458 FlashMessage::ERROR
459 ));
460 } elseif (strlen($password) < 8) {
461 $messages->enqueue(new FlashMessage(
462 'Password must be at least eight characters long.',
463 'Administrator user not created',
464 FlashMessage::ERROR
465 ));
466 } else {
467 $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
468 $userExists = $connectionPool->getConnectionForTable('be_users')
469 ->count(
470 'uid',
471 'be_users',
472 ['username' => $username]
473 );
474 if ($userExists) {
475 $messages->enqueue(new FlashMessage(
476 'A user with username "' . $username . '" exists already.',
477 'Administrator user not created',
478 FlashMessage::ERROR
479 ));
480 } else {
481 $saltFactory = SaltFactory::getSaltingInstance(null, 'BE');
482 $hashedPassword = $saltFactory->getHashedPassword($password);
483 $adminUserFields = [
484 'username' => $username,
485 'password' => $hashedPassword,
486 'admin' => 1,
487 'tstamp' => $GLOBALS['EXEC_TIME'],
488 'crdate' => $GLOBALS['EXEC_TIME']
489 ];
490 $connectionPool->getConnectionForTable('be_users')->insert('be_users', $adminUserFields);
491
492 if ($isSystemMaintainer) {
493
494 // Get the new admin user uid juste created
495 $newAdminUserUid = (int)$connectionPool->getConnectionForTable('be_users')->lastInsertId('be_users');
496
497 // Get the list of the existing systemMaintainer
498 $existingSystemMaintainersList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? [];
499
500 // Add the new admin user to the existing systemMaintainer list
501 $newSystemMaintainersList = $existingSystemMaintainersList;
502 $newSystemMaintainersList[] = $newAdminUserUid;
503
504 // Update the LocalConfiguration.php file with the new list
505 $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
506 $configurationManager->setLocalConfigurationValuesByPathValuePairs(
507 ['SYS/systemMaintainers' => $newSystemMaintainersList]
508 );
509 }
510
511 $messages->enqueue(new FlashMessage(
512 '',
513 'Administrator created with username "' . $username . '".'
514 ));
515 }
516 }
517 return new JsonResponse([
518 'success' => true,
519 'status' => $messages,
520 ]);
521 }
522
523 /**
524 * Entry action of language packs module gets
525 * * list of available languages with details like active or not and last update
526 * * list of loaded extensions
527 *
528 * @param $request ServerRequestInterface
529 * @return ResponseInterface
530 */
531 public function languagePacksGetDataAction(ServerRequestInterface $request): ResponseInterface
532 {
533 $view = $this->initializeStandaloneView($request, 'Maintenance/LanguagePacks.html');
534 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
535 $view->assignMultiple([
536 'languagePacksActivateLanguageToken' => $formProtection->generateToken('installTool', 'languagePacksActivateLanguage'),
537 'languagePacksDeactivateLanguageToken' => $formProtection->generateToken('installTool', 'languagePacksDeactivateLanguage'),
538 'languagePacksUpdatePackToken' => $formProtection->generateToken('installTool', 'languagePacksUpdatePack'),
539 'languagePacksUpdateIsoTimesToken' => $formProtection->generateToken('installTool', 'languagePacksUpdateIsoTimes'),
540 ]);
541 // This action needs TYPO3_CONF_VARS for full GeneralUtility::getUrl() config
542 $this->loadExtLocalconfDatabaseAndExtTables();
543 $languagePacksService = GeneralUtility::makeInstance(LanguagePackService::class);
544 $languagePacksService->updateMirrorBaseUrl();
545 $extensions = $languagePacksService->getExtensionLanguagePackDetails();
546 return new JsonResponse([
547 'success' => true,
548 'languages' => $languagePacksService->getLanguageDetails(),
549 'extensions' => $extensions,
550 'activeLanguages' => $languagePacksService->getActiveLanguages(),
551 'activeExtensions' => array_column($extensions, 'key'),
552 'html' => $view->render(),
553 ]);
554 }
555
556 /**
557 * Activate a language and any possible dependency it may have
558 *
559 * @param ServerRequestInterface $request
560 * @return ResponseInterface
561 */
562 public function languagePacksActivateLanguageAction(ServerRequestInterface $request): ResponseInterface
563 {
564 $messageQueue = new FlashMessageQueue('install');
565 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
566 $locales = GeneralUtility::makeInstance(Locales::class);
567 $availableLanguages = $languagePackService->getAvailableLanguages();
568 $activeLanguages = $languagePackService->getActiveLanguages();
569 $iso = $request->getParsedBody()['install']['iso'];
570 $activateArray = [];
571 foreach ($availableLanguages as $availableIso => $name) {
572 if ($availableIso === $iso && !in_array($availableIso, $activeLanguages, true)) {
573 $activateArray[] = $iso;
574 $dependencies = $locales->getLocaleDependencies($availableIso);
575 if (!empty($dependencies)) {
576 foreach ($dependencies as $dependency) {
577 if (!in_array($dependency, $activeLanguages, true)) {
578 $activateArray[] = $dependency;
579 }
580 }
581 }
582 }
583 }
584 if (!empty($activateArray)) {
585 $activeLanguages = array_merge($activeLanguages, $activateArray);
586 sort($activeLanguages);
587 $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
588 $configurationManager->setLocalConfigurationValueByPath(
589 'EXTCONF/lang',
590 ['availableLanguages' => $activeLanguages]
591 );
592 $activationArray = [];
593 foreach ($activateArray as $activateIso) {
594 $activationArray[] = $availableLanguages[$activateIso] . ' (' . $activateIso . ')';
595 }
596 $messageQueue->enqueue(
597 new FlashMessage(
598 'These languages have been activated: ' . implode(', ', $activationArray)
599 )
600 );
601 } else {
602 $messageQueue->enqueue(
603 new FlashMessage('Language with ISO code "' . $iso . '" not found or already active.', '', FlashMessage::ERROR)
604 );
605 }
606 return new JsonResponse([
607 'success' => true,
608 'status' => $messageQueue,
609 ]);
610 }
611
612 /**
613 * Deactivate a language if no other active language depends on it
614 *
615 * @param ServerRequestInterface $request
616 * @return ResponseInterface
617 * @throws \RuntimeException
618 */
619 public function languagePacksDeactivateLanguageAction(ServerRequestInterface $request): ResponseInterface
620 {
621 $messageQueue = new FlashMessageQueue('install');
622 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
623 $locales = GeneralUtility::makeInstance(Locales::class);
624 $availableLanguages = $languagePackService->getAvailableLanguages();
625 $activeLanguages = $languagePackService->getActiveLanguages();
626 $iso = $request->getParsedBody()['install']['iso'];
627 if (empty($iso)) {
628 throw new \RuntimeException('No iso code given', 1520109807);
629 }
630 $otherActiveLanguageDependencies = [];
631 foreach ($activeLanguages as $activeLanguage) {
632 if ($activeLanguage === $iso) {
633 continue;
634 }
635 $dependencies = $locales->getLocaleDependencies($activeLanguage);
636 if (in_array($iso, $dependencies, true)) {
637 $otherActiveLanguageDependencies[] = $activeLanguage;
638 }
639 }
640 if (!empty($otherActiveLanguageDependencies)) {
641 // Error: Must disable dependencies first
642 $dependentArray = [];
643 foreach ($otherActiveLanguageDependencies as $dependency) {
644 $dependentArray[] = $availableLanguages[$dependency] . ' (' . $dependency . ')';
645 }
646 $messageQueue->enqueue(
647 new FlashMessage(
648 'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" can not be deactivated. These'
649 . ' other languages depend on it and need to be deactivated before:'
650 . implode(', ', $dependentArray),
651 '',
652 FlashMessage::ERROR
653 )
654 );
655 } else {
656 if (in_array($iso, $activeLanguages, true)) {
657 // Deactivate this language
658 $newActiveLanguages = [];
659 foreach ($activeLanguages as $activeLanguage) {
660 if ($activeLanguage === $iso) {
661 continue;
662 }
663 $newActiveLanguages[] = $activeLanguage;
664 }
665 $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
666 $configurationManager->setLocalConfigurationValueByPath(
667 'EXTCONF/lang',
668 ['availableLanguages' => $newActiveLanguages]
669 );
670 $messageQueue->enqueue(
671 new FlashMessage(
672 'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" has been deactivated'
673 )
674 );
675 } else {
676 $messageQueue->enqueue(
677 new FlashMessage(
678 'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" has not been deactivated',
679 '',
680 FlashMessage::ERROR
681 )
682 );
683 }
684 }
685 return new JsonResponse([
686 'success' => true,
687 'status' => $messageQueue,
688 ]);
689 }
690
691 /**
692 * Update a pack of one extension and one language
693 *
694 * @param ServerRequestInterface $request
695 * @return ResponseInterface
696 * @throws \RuntimeException
697 */
698 public function languagePacksUpdatePackAction(ServerRequestInterface $request): ResponseInterface
699 {
700 $iso = $request->getParsedBody()['install']['iso'];
701 $key = $request->getParsedBody()['install']['extension'];
702 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
703 return new JsonResponse([
704 'success' => true,
705 'packResult' => $languagePackService->languagePackDownload($key, $iso)
706 ]);
707 }
708
709 /**
710 * Set "last updated" time in registry for fully updated language packs.
711 *
712 * @param ServerRequestInterface $request
713 * @return ResponseInterface
714 */
715 public function languagePacksUpdateIsoTimesAction(ServerRequestInterface $request): ResponseInterface
716 {
717 $isos = $request->getParsedBody()['install']['isos'];
718 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
719 $languagePackService->setLastUpdatedIsoCode($isos);
720
721 // The cache manager is already instantiated in the install tool
722 // with some hacked settings to disable caching of extbase and fluid.
723 // We want a "fresh" object here to operate on a different cache setup.
724 // cacheManager implements SingletonInterface, so the only way to get a "fresh"
725 // instance is by circumventing makeInstance and/or the objectManager and
726 // using new directly!
727 $cacheManager = new \TYPO3\CMS\Core\Cache\CacheManager();
728 $cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
729 $cacheManager->getCache('l10n')->flush();
730
731 return new JsonResponse(['success' => true]);
732 }
733
734 /**
735 * Set 'uc' field of all backend users to empty string
736 *
737 * @return ResponseInterface
738 */
739 public function resetBackendUserUcAction(): ResponseInterface
740 {
741 GeneralUtility::makeInstance(ConnectionPool::class)
742 ->getQueryBuilderForTable('be_users')
743 ->update('be_users')
744 ->set('uc', '')
745 ->execute();
746 $messageQueue = new FlashMessageQueue('install');
747 $messageQueue->enqueue(new FlashMessage(
748 'All backend users preferences has been reseted',
749 'Reset all backend users preferences'
750 ));
751 return new JsonResponse([
752 'success' => true,
753 'status' => $messageQueue
754 ]);
755 }
756 }