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