[BUGFIX] Improve install tool temporary assets removal
[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\Crypto\PasswordHashing\PasswordHashFactory;
24 use TYPO3\CMS\Core\Database\ConnectionPool;
25 use TYPO3\CMS\Core\Database\Schema\Exception\StatementException;
26 use TYPO3\CMS\Core\Database\Schema\SchemaMigrator;
27 use TYPO3\CMS\Core\Database\Schema\SqlReader;
28 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
29 use TYPO3\CMS\Core\FormProtection\InstallToolFormProtection;
30 use TYPO3\CMS\Core\Http\JsonResponse;
31 use TYPO3\CMS\Core\Localization\Locales;
32 use TYPO3\CMS\Core\Messaging\FlashMessage;
33 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
34 use TYPO3\CMS\Core\Service\OpcodeCacheService;
35 use TYPO3\CMS\Core\Utility\GeneralUtility;
36 use TYPO3\CMS\Install\Service\ClearCacheService;
37 use TYPO3\CMS\Install\Service\ClearTableService;
38 use TYPO3\CMS\Install\Service\LanguagePackService;
39 use TYPO3\CMS\Install\Service\Typo3tempFileService;
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 ServerRequestInterface $request
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 typo3temp/assets or FAL 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 // storageUid is an optional post param if FAL storages should be cleaned
113 $storageUid = $request->getParsedBody()['install']['storageUid'] ?? null;
114 if ($storageUid === null) {
115 $typo3tempFileService->clearAssetsFolder($folder);
116 $messageQueue->enqueue(new FlashMessage('Cleared files in "' . $folder . '" folder'));
117 } else {
118 $storageUid = (int)$storageUid;
119 $failedDeletions = $typo3tempFileService->clearProcessedFiles($storageUid);
120 if ($failedDeletions) {
121 $messageQueue->enqueue(new FlashMessage(
122 'Failed to delete ' . $failedDeletions . ' processed files. See TYPO3 log (by default typo3temp/var/log/typo3_*.log)',
123 '',
124 FlashMessage::ERROR
125 ));
126 } else {
127 $messageQueue->enqueue(new FlashMessage('Cleared processed files'));
128 }
129 }
130 return new JsonResponse([
131 'success' => true,
132 'status' => $messageQueue,
133 ]);
134 }
135
136 /**
137 * Dump autoload information
138 *
139 * @return ResponseInterface
140 */
141 public function dumpAutoloadAction(): ResponseInterface
142 {
143 $messageQueue = new FlashMessageQueue('install');
144 if (Environment::isComposerMode()) {
145 $messageQueue->enqueue(new FlashMessage(
146 'Skipped generating additional class loading information in composer mode.',
147 '',
148 FlashMessage::NOTICE
149 ));
150 } else {
151 ClassLoadingInformation::dumpClassLoadingInformation();
152 $messageQueue->enqueue(new FlashMessage(
153 'Successfully dumped class loading information for extensions.'
154 ));
155 }
156 return new JsonResponse([
157 'success' => true,
158 'status' => $messageQueue,
159 ]);
160 }
161
162 /**
163 * Analyze current database situation
164 *
165 * @param ServerRequestInterface $request
166 * @return ResponseInterface
167 */
168 public function databaseAnalyzerAnalyzeAction(ServerRequestInterface $request): ResponseInterface
169 {
170 $view = $this->initializeStandaloneView($request, 'Maintenance/DatabaseAnalyzer.html');
171 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
172 $view->assignMultiple([
173 'databaseAnalyzerExecuteToken' => $formProtection->generateToken('installTool', 'databaseAnalyzerExecute'),
174 ]);
175 $this->loadExtLocalconfDatabaseAndExtTables();
176 $messageQueue = new FlashMessageQueue('install');
177
178 $suggestions = [];
179 try {
180 $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
181 $sqlStatements = $sqlReader->getCreateTableStatementArray($sqlReader->getTablesDefinitionString());
182 $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
183 $addCreateChange = $schemaMigrationService->getUpdateSuggestions($sqlStatements);
184
185 // Aggregate the per-connection statements into one flat array
186 $addCreateChange = array_merge_recursive(...array_values($addCreateChange));
187 if (!empty($addCreateChange['create_table'])) {
188 $suggestion = [
189 'key' => 'addTable',
190 'label' => 'Add tables',
191 'enabled' => true,
192 'children' => [],
193 ];
194 foreach ($addCreateChange['create_table'] as $hash => $statement) {
195 $suggestion['children'][] = [
196 'hash' => $hash,
197 'statement' => $statement,
198 ];
199 }
200 $suggestions[] = $suggestion;
201 }
202 if (!empty($addCreateChange['add'])) {
203 $suggestion = [
204 'key' => 'addField',
205 'label' => 'Add fields to tables',
206 'enabled' => true,
207 'children' => [],
208 ];
209 foreach ($addCreateChange['add'] as $hash => $statement) {
210 $suggestion['children'][] = [
211 'hash' => $hash,
212 'statement' => $statement,
213 ];
214 }
215 $suggestions[] = $suggestion;
216 }
217 if (!empty($addCreateChange['change'])) {
218 $suggestion = [
219 'key' => 'change',
220 'label' => 'Change fields',
221 'enabled' => false,
222 'children' => [],
223 ];
224 foreach ($addCreateChange['change'] as $hash => $statement) {
225 $child = [
226 'hash' => $hash,
227 'statement' => $statement,
228 ];
229 if (isset($addCreateChange['change_currentValue'][$hash])) {
230 $child['current'] = $addCreateChange['change_currentValue'][$hash];
231 }
232 $suggestion['children'][] = $child;
233 }
234 $suggestions[] = $suggestion;
235 }
236
237 // Difference from current to expected
238 $dropRename = $schemaMigrationService->getUpdateSuggestions($sqlStatements, true);
239
240 // Aggregate the per-connection statements into one flat array
241 $dropRename = array_merge_recursive(...array_values($dropRename));
242 if (!empty($dropRename['change_table'])) {
243 $suggestion = [
244 'key' => 'renameTableToUnused',
245 'label' => 'Remove tables (rename with prefix)',
246 'enabled' => false,
247 'children' => [],
248 ];
249 foreach ($dropRename['change_table'] as $hash => $statement) {
250 $child = [
251 'hash' => $hash,
252 'statement' => $statement,
253 ];
254 if (!empty($dropRename['tables_count'][$hash])) {
255 $child['rowCount'] = $dropRename['tables_count'][$hash];
256 }
257 $suggestion['children'][] = $child;
258 }
259 $suggestions[] = $suggestion;
260 }
261 if (!empty($dropRename['change'])) {
262 $suggestion = [
263 'key' => 'renameTableFieldToUnused',
264 'label' => 'Remove unused fields (rename with prefix)',
265 'enabled' => false,
266 'children' => [],
267 ];
268 foreach ($dropRename['change'] as $hash => $statement) {
269 $suggestion['children'][] = [
270 'hash' => $hash,
271 'statement' => $statement,
272 ];
273 }
274 $suggestions[] = $suggestion;
275 }
276 if (!empty($dropRename['drop'])) {
277 $suggestion = [
278 'key' => 'deleteField',
279 'label' => 'Drop fields (really!)',
280 'enabled' => false,
281 'children' => [],
282 ];
283 foreach ($dropRename['drop'] as $hash => $statement) {
284 $suggestion['children'][] = [
285 'hash' => $hash,
286 'statement' => $statement,
287 ];
288 }
289 $suggestions[] = $suggestion;
290 }
291 if (!empty($dropRename['drop_table'])) {
292 $suggestion = [
293 'key' => 'deleteTable',
294 'label' => 'Drop tables (really!)',
295 'enabled' => false,
296 'children' => [],
297 ];
298 foreach ($dropRename['drop_table'] as $hash => $statement) {
299 $child = [
300 'hash' => $hash,
301 'statement' => $statement,
302 ];
303 if (!empty($dropRename['tables_count'][$hash])) {
304 $child['rowCount'] = $dropRename['tables_count'][$hash];
305 }
306 $suggestion['children'][] = $child;
307 }
308 $suggestions[] = $suggestion;
309 }
310
311 $messageQueue->enqueue(new FlashMessage(
312 '',
313 'Analyzed current database'
314 ));
315 } catch (StatementException $e) {
316 $messageQueue->enqueue(new FlashMessage(
317 $e->getMessage(),
318 'Database analysis failed',
319 FlashMessage::ERROR
320 ));
321 }
322 return new JsonResponse([
323 'success' => true,
324 'status' => $messageQueue,
325 'suggestions' => $suggestions,
326 'html' => $view->render(),
327 ]);
328 }
329
330 /**
331 * Apply selected database changes
332 *
333 * @param ServerRequestInterface $request
334 * @return ResponseInterface
335 */
336 public function databaseAnalyzerExecuteAction(ServerRequestInterface $request): ResponseInterface
337 {
338 $this->loadExtLocalconfDatabaseAndExtTables();
339 $messageQueue = new FlashMessageQueue('install');
340 $selectedHashes = $request->getParsedBody()['install']['hashes'] ?? [];
341 if (empty($selectedHashes)) {
342 $messageQueue->enqueue(new FlashMessage(
343 '',
344 'No database changes selected',
345 FlashMessage::WARNING
346 ));
347 } else {
348 $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
349 $sqlStatements = $sqlReader->getCreateTableStatementArray($sqlReader->getTablesDefinitionString());
350 $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
351 $statementHashesToPerform = array_flip($selectedHashes);
352 $results = $schemaMigrationService->migrate($sqlStatements, $statementHashesToPerform);
353 // Create error flash messages if any
354 foreach ($results as $errorMessage) {
355 $messageQueue->enqueue(new FlashMessage(
356 'Error: ' . $errorMessage,
357 'Database update failed',
358 FlashMessage::ERROR
359 ));
360 }
361 $messageQueue->enqueue(new FlashMessage(
362 'Executed database updates',
363 'Executed database updates'
364 ));
365 }
366 return new JsonResponse([
367 'success' => true,
368 'status' => $messageQueue,
369 ]);
370 }
371
372 /**
373 * Clear table overview statistics action
374 *
375 * @param ServerRequestInterface $request
376 * @return ResponseInterface
377 */
378 public function clearTablesStatsAction(ServerRequestInterface $request): ResponseInterface
379 {
380 $view = $this->initializeStandaloneView($request, 'Maintenance/ClearTables.html');
381 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
382 $view->assignMultiple([
383 'clearTablesClearToken' => $formProtection->generateToken('installTool', 'clearTablesClear'),
384 ]);
385 return new JsonResponse([
386 'success' => true,
387 'stats' => (new ClearTableService())->getTableStatistics(),
388 'html' => $view->render(),
389 ]);
390 }
391
392 /**
393 * Truncate a specific table
394 *
395 * @param ServerRequestInterface $request
396 * @return ResponseInterface
397 * @throws \RuntimeException
398 */
399 public function clearTablesClearAction(ServerRequestInterface $request): ResponseInterface
400 {
401 $table = $request->getParsedBody()['install']['table'];
402 if (empty($table)) {
403 throw new \RuntimeException(
404 'No table name given',
405 1501944076
406 );
407 }
408 (new ClearTableService())->clearSelectedTable($table);
409 $messageQueue = (new FlashMessageQueue('install'))->enqueue(
410 new FlashMessage('Cleared table')
411 );
412 return new JsonResponse([
413 'success' => true,
414 'status' => $messageQueue
415 ]);
416 }
417 /**
418 * Create Admin Get Data action
419 *
420 * @param ServerRequestInterface $request
421 * @return ResponseInterface
422 */
423 public function createAdminGetDataAction(ServerRequestInterface $request): ResponseInterface
424 {
425 $view = $this->initializeStandaloneView($request, 'Maintenance/CreateAdmin.html');
426 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
427 $view->assignMultiple([
428 'createAdminToken' => $formProtection->generateToken('installTool', 'createAdmin'),
429 ]);
430 return new JsonResponse([
431 'success' => true,
432 'html' => $view->render(),
433 ]);
434 }
435
436 /**
437 * Create a backend administrator from given username and password
438 *
439 * @param ServerRequestInterface $request
440 * @return ResponseInterface
441 */
442 public function createAdminAction(ServerRequestInterface $request): ResponseInterface
443 {
444 $username = preg_replace('/\\s/i', '', $request->getParsedBody()['install']['userName']);
445 $password = $request->getParsedBody()['install']['userPassword'];
446 $passwordCheck = $request->getParsedBody()['install']['userPasswordCheck'];
447 $isSystemMaintainer = ((bool)$request->getParsedBody()['install']['userSystemMaintainer'] == '1') ? true : false;
448
449 $messages = new FlashMessageQueue('install');
450
451 if (strlen($username) < 1) {
452 $messages->enqueue(new FlashMessage(
453 'No valid username given.',
454 'Administrator user not created',
455 FlashMessage::ERROR
456 ));
457 } elseif ($password !== $passwordCheck) {
458 $messages->enqueue(new FlashMessage(
459 'Passwords do not match.',
460 'Administrator user not created',
461 FlashMessage::ERROR
462 ));
463 } elseif (strlen($password) < 8) {
464 $messages->enqueue(new FlashMessage(
465 'Password must be at least eight characters long.',
466 'Administrator user not created',
467 FlashMessage::ERROR
468 ));
469 } else {
470 $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
471 $userExists = $connectionPool->getConnectionForTable('be_users')
472 ->count(
473 'uid',
474 'be_users',
475 ['username' => $username]
476 );
477 if ($userExists) {
478 $messages->enqueue(new FlashMessage(
479 'A user with username "' . $username . '" exists already.',
480 'Administrator user not created',
481 FlashMessage::ERROR
482 ));
483 } else {
484 $hashInstance = GeneralUtility::makeInstance(PasswordHashFactory::class)->getDefaultHashInstance('BE');
485 $hashedPassword = $hashInstance->getHashedPassword($password);
486 $adminUserFields = [
487 'username' => $username,
488 'password' => $hashedPassword,
489 'admin' => 1,
490 'tstamp' => $GLOBALS['EXEC_TIME'],
491 'crdate' => $GLOBALS['EXEC_TIME']
492 ];
493 $connectionPool->getConnectionForTable('be_users')->insert('be_users', $adminUserFields);
494
495 if ($isSystemMaintainer) {
496
497 // Get the new admin user uid juste created
498 $newAdminUserUid = (int)$connectionPool->getConnectionForTable('be_users')->lastInsertId('be_users');
499
500 // Get the list of the existing systemMaintainer
501 $existingSystemMaintainersList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? [];
502
503 // Add the new admin user to the existing systemMaintainer list
504 $newSystemMaintainersList = $existingSystemMaintainersList;
505 $newSystemMaintainersList[] = $newAdminUserUid;
506
507 // Update the LocalConfiguration.php file with the new list
508 $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
509 $configurationManager->setLocalConfigurationValuesByPathValuePairs(
510 ['SYS/systemMaintainers' => $newSystemMaintainersList]
511 );
512 }
513
514 $messages->enqueue(new FlashMessage(
515 '',
516 'Administrator created with username "' . $username . '".'
517 ));
518 }
519 }
520 return new JsonResponse([
521 'success' => true,
522 'status' => $messages,
523 ]);
524 }
525
526 /**
527 * Entry action of language packs module gets
528 * * list of available languages with details like active or not and last update
529 * * list of loaded extensions
530 *
531 * @param ServerRequestInterface $request
532 * @return ResponseInterface
533 */
534 public function languagePacksGetDataAction(ServerRequestInterface $request): ResponseInterface
535 {
536 $view = $this->initializeStandaloneView($request, 'Maintenance/LanguagePacks.html');
537 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
538 $view->assignMultiple([
539 'languagePacksActivateLanguageToken' => $formProtection->generateToken('installTool', 'languagePacksActivateLanguage'),
540 'languagePacksDeactivateLanguageToken' => $formProtection->generateToken('installTool', 'languagePacksDeactivateLanguage'),
541 'languagePacksUpdatePackToken' => $formProtection->generateToken('installTool', 'languagePacksUpdatePack'),
542 'languagePacksUpdateIsoTimesToken' => $formProtection->generateToken('installTool', 'languagePacksUpdateIsoTimes'),
543 ]);
544 // This action needs TYPO3_CONF_VARS for full GeneralUtility::getUrl() config
545 $this->loadExtLocalconfDatabaseAndExtTables();
546 $languagePacksService = GeneralUtility::makeInstance(LanguagePackService::class);
547 $languagePacksService->updateMirrorBaseUrl();
548 $extensions = $languagePacksService->getExtensionLanguagePackDetails();
549 return new JsonResponse([
550 'success' => true,
551 'languages' => $languagePacksService->getLanguageDetails(),
552 'extensions' => $extensions,
553 'activeLanguages' => $languagePacksService->getActiveLanguages(),
554 'activeExtensions' => array_column($extensions, 'key'),
555 'html' => $view->render(),
556 ]);
557 }
558
559 /**
560 * Activate a language and any possible dependency it may have
561 *
562 * @param ServerRequestInterface $request
563 * @return ResponseInterface
564 */
565 public function languagePacksActivateLanguageAction(ServerRequestInterface $request): ResponseInterface
566 {
567 $messageQueue = new FlashMessageQueue('install');
568 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
569 $locales = GeneralUtility::makeInstance(Locales::class);
570 $availableLanguages = $languagePackService->getAvailableLanguages();
571 $activeLanguages = $languagePackService->getActiveLanguages();
572 $iso = $request->getParsedBody()['install']['iso'];
573 $activateArray = [];
574 foreach ($availableLanguages as $availableIso => $name) {
575 if ($availableIso === $iso && !in_array($availableIso, $activeLanguages, true)) {
576 $activateArray[] = $iso;
577 $dependencies = $locales->getLocaleDependencies($availableIso);
578 if (!empty($dependencies)) {
579 foreach ($dependencies as $dependency) {
580 if (!in_array($dependency, $activeLanguages, true)) {
581 $activateArray[] = $dependency;
582 }
583 }
584 }
585 }
586 }
587 if (!empty($activateArray)) {
588 $activeLanguages = array_merge($activeLanguages, $activateArray);
589 sort($activeLanguages);
590 $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
591 $configurationManager->setLocalConfigurationValueByPath(
592 'EXTCONF/lang',
593 ['availableLanguages' => $activeLanguages]
594 );
595 $activationArray = [];
596 foreach ($activateArray as $activateIso) {
597 $activationArray[] = $availableLanguages[$activateIso] . ' (' . $activateIso . ')';
598 }
599 $messageQueue->enqueue(
600 new FlashMessage(
601 'These languages have been activated: ' . implode(', ', $activationArray)
602 )
603 );
604 } else {
605 $messageQueue->enqueue(
606 new FlashMessage('Language with ISO code "' . $iso . '" not found or already active.', '', FlashMessage::ERROR)
607 );
608 }
609 return new JsonResponse([
610 'success' => true,
611 'status' => $messageQueue,
612 ]);
613 }
614
615 /**
616 * Deactivate a language if no other active language depends on it
617 *
618 * @param ServerRequestInterface $request
619 * @return ResponseInterface
620 * @throws \RuntimeException
621 */
622 public function languagePacksDeactivateLanguageAction(ServerRequestInterface $request): ResponseInterface
623 {
624 $messageQueue = new FlashMessageQueue('install');
625 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
626 $locales = GeneralUtility::makeInstance(Locales::class);
627 $availableLanguages = $languagePackService->getAvailableLanguages();
628 $activeLanguages = $languagePackService->getActiveLanguages();
629 $iso = $request->getParsedBody()['install']['iso'];
630 if (empty($iso)) {
631 throw new \RuntimeException('No iso code given', 1520109807);
632 }
633 $otherActiveLanguageDependencies = [];
634 foreach ($activeLanguages as $activeLanguage) {
635 if ($activeLanguage === $iso) {
636 continue;
637 }
638 $dependencies = $locales->getLocaleDependencies($activeLanguage);
639 if (in_array($iso, $dependencies, true)) {
640 $otherActiveLanguageDependencies[] = $activeLanguage;
641 }
642 }
643 if (!empty($otherActiveLanguageDependencies)) {
644 // Error: Must disable dependencies first
645 $dependentArray = [];
646 foreach ($otherActiveLanguageDependencies as $dependency) {
647 $dependentArray[] = $availableLanguages[$dependency] . ' (' . $dependency . ')';
648 }
649 $messageQueue->enqueue(
650 new FlashMessage(
651 'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" can not be deactivated. These'
652 . ' other languages depend on it and need to be deactivated before:'
653 . implode(', ', $dependentArray),
654 '',
655 FlashMessage::ERROR
656 )
657 );
658 } else {
659 if (in_array($iso, $activeLanguages, true)) {
660 // Deactivate this language
661 $newActiveLanguages = [];
662 foreach ($activeLanguages as $activeLanguage) {
663 if ($activeLanguage === $iso) {
664 continue;
665 }
666 $newActiveLanguages[] = $activeLanguage;
667 }
668 $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
669 $configurationManager->setLocalConfigurationValueByPath(
670 'EXTCONF/lang',
671 ['availableLanguages' => $newActiveLanguages]
672 );
673 $messageQueue->enqueue(
674 new FlashMessage(
675 'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" has been deactivated'
676 )
677 );
678 } else {
679 $messageQueue->enqueue(
680 new FlashMessage(
681 'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" has not been deactivated',
682 '',
683 FlashMessage::ERROR
684 )
685 );
686 }
687 }
688 return new JsonResponse([
689 'success' => true,
690 'status' => $messageQueue,
691 ]);
692 }
693
694 /**
695 * Update a pack of one extension and one language
696 *
697 * @param ServerRequestInterface $request
698 * @return ResponseInterface
699 * @throws \RuntimeException
700 */
701 public function languagePacksUpdatePackAction(ServerRequestInterface $request): ResponseInterface
702 {
703 $this->loadExtLocalconfDatabaseAndExtTables();
704 $iso = $request->getParsedBody()['install']['iso'];
705 $key = $request->getParsedBody()['install']['extension'];
706 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
707 return new JsonResponse([
708 'success' => true,
709 'packResult' => $languagePackService->languagePackDownload($key, $iso)
710 ]);
711 }
712
713 /**
714 * Set "last updated" time in registry for fully updated language packs.
715 *
716 * @param ServerRequestInterface $request
717 * @return ResponseInterface
718 */
719 public function languagePacksUpdateIsoTimesAction(ServerRequestInterface $request): ResponseInterface
720 {
721 $isos = $request->getParsedBody()['install']['isos'];
722 $languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
723 $languagePackService->setLastUpdatedIsoCode($isos);
724
725 // The cache manager is already instantiated in the install tool
726 // with some hacked settings to disable caching of extbase and fluid.
727 // We want a "fresh" object here to operate on a different cache setup.
728 // cacheManager implements SingletonInterface, so the only way to get a "fresh"
729 // instance is by circumventing makeInstance and/or the objectManager and
730 // using new directly!
731 $cacheManager = new \TYPO3\CMS\Core\Cache\CacheManager();
732 $cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
733 $cacheManager->getCache('l10n')->flush();
734
735 return new JsonResponse(['success' => true]);
736 }
737
738 /**
739 * Set 'uc' field of all backend users to empty string
740 *
741 * @return ResponseInterface
742 */
743 public function resetBackendUserUcAction(): ResponseInterface
744 {
745 GeneralUtility::makeInstance(ConnectionPool::class)
746 ->getQueryBuilderForTable('be_users')
747 ->update('be_users')
748 ->set('uc', '')
749 ->execute();
750 $messageQueue = new FlashMessageQueue('install');
751 $messageQueue->enqueue(new FlashMessage(
752 'All backend users preferences has been reseted',
753 'Reset all backend users preferences'
754 ));
755 return new JsonResponse([
756 'success' => true,
757 'status' => $messageQueue
758 ]);
759 }
760 }