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