5690e3094dd50d388555dcf20e677cda81ccb239
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Command / UpgradeWizardCommand.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Install\Command;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 use Symfony\Component\Console\Command\Command;
20 use Symfony\Component\Console\Input\InputArgument;
21 use Symfony\Component\Console\Input\InputInterface;
22 use Symfony\Component\Console\Output\OutputInterface;
23 use Symfony\Component\Console\Question\ConfirmationQuestion;
24 use TYPO3\CMS\Core\Registry;
25 use TYPO3\CMS\Core\Utility\GeneralUtility;
26 use TYPO3\CMS\Install\Service\UpgradeWizardsService;
27 use TYPO3\CMS\Install\Updates\ChattyInterface;
28 use TYPO3\CMS\Install\Updates\ConfirmableInterface;
29 use TYPO3\CMS\Install\Updates\PrerequisiteCollection;
30 use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
31
32 /**
33 * Core function for updating language packs
34 */
35 class UpgradeWizardCommand extends Command
36 {
37 /**
38 * @var UpgradeWizardsService
39 */
40 private $upgradeWizardsService;
41
42 protected function bootstrap(): void
43 {
44 \TYPO3\CMS\Core\Core\Bootstrap::loadTypo3LoadedExtAndExtLocalconf(false);
45 \TYPO3\CMS\Core\Core\Bootstrap::unsetReservedGlobalVariables();
46 \TYPO3\CMS\Core\Core\Bootstrap::loadBaseTca(false);
47 \TYPO3\CMS\Core\Core\Bootstrap::loadExtTables(false);
48 }
49
50 /**
51 * Configure the command by defining the name, options and arguments
52 */
53 protected function configure()
54 {
55 $this->setAliases(['install:wizards']);
56 $this->setDescription('Update the language files of all activated extensions')
57 ->addArgument(
58 'wizardName',
59 InputArgument::OPTIONAL
60 )->addOption('dry-run', 'd');
61 }
62
63 /**
64 * Update language packs of all active languages for all active extensions
65 *
66 * @param InputInterface $input
67 * @param OutputInterface $output
68 * @throws \InvalidArgumentException
69 * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
70 * @return int
71 */
72 protected function execute(InputInterface $input, OutputInterface $output)
73 {
74 $this->bootstrap();
75 $this->upgradeWizardsService = new UpgradeWizardsService();
76
77 if ($input->getArgument('wizardName')) {
78 $wizardToExecute = $input->getArgument('wizardName');
79 $this->runSingleWizard($input, $output, $wizardToExecute);
80 } else {
81 $this->runAllWizards($input, $output);
82 }
83 }
84
85 /**
86 * Handles prerequisites of update wizards, allows a more flexible definition and declaration of dependencies
87 * Currently implemented prerequisites include "database needs to be up-to-date" and "referenceIndex needs to be up-
88 * to-date"
89 * At the moment the install tool automatically displays the database updates when necessary but can't do more
90 * prerequisites
91 *
92 * @todo handle in install tool
93 * @param \Symfony\Component\Console\Output\OutputInterface $output
94 * @param UpgradeWizardInterface[]
95 */
96 protected function handlePrerequisites(OutputInterface $output, array $instances): void
97 {
98 $collection = GeneralUtility::makeInstance(PrerequisiteCollection::class);
99 foreach ($instances as $instance) {
100 foreach ($instance->getPrerequisites() as $prerequisite) {
101 $collection->addPrerequisite($prerequisite);
102 }
103 }
104 foreach ($collection->getPrerequisites() as $prerequisite) {
105 if (!$prerequisite->met()) {
106 $output->writeln('Prerequisite "' . $prerequisite->getName() . '" not met, will ensure.');
107 $prerequisite->ensure();
108 } else {
109 $output->writeln('Prerequisite "' . $prerequisite->getName() . '" met.');
110 }
111 }
112 }
113
114 /**
115 * @param \Symfony\Component\Console\Input\InputInterface $input
116 * @param \Symfony\Component\Console\Output\OutputInterface $output
117 * @param $wizardToExecute
118 * @return int
119 */
120 protected function runSingleWizard(
121 InputInterface $input,
122 OutputInterface $output,
123 UpgradeWizardInterface $instance
124 ): int {
125 $output->writeln('Running Wizard ' . $instance->getTitle());
126 if ($instance instanceof ConfirmableInterface) {
127 $defaultString = $instance->getConfirmationDefault() ? '(Y/n)' : '(y/N)';
128 $question = new ConfirmationQuestion(
129 '<info>' .
130 $instance->getConfirmationTitle() .
131 '</info>' .
132 "\r\n" .
133 $instance->getConfirmationMessage() .
134 ' ' .
135 $defaultString,
136 $instance->getConfirmationDefault()
137 );
138 $helper = $this->getHelper('question');
139 if (!$helper->ask($input, $output, $question)) {
140 $this->upgradeWizardsService->markWizardAsDone($instance->getIdentifier());
141 return 0;
142 }
143 }
144 if ($instance->executeUpdate()) {
145 $output->writeln('<info>Successfully ran wizard ' . $instance->getTitle() . '</info>');
146 $this->upgradeWizardsService->markWizardAsDone($instance->getIdentifier());
147 return 0;
148 }
149 $output->writeln('<error>Something went wrong while running ' . $instance->getTitle() . '</error>');
150 return 1;
151 }
152
153 /**
154 * Get list of registered upgrade wizards.
155 *
156 * @return array List of upgrade wizards in correct order with detail information
157 */
158 public function runAllWizards(InputInterface $input, OutputInterface $output): array
159 {
160 $wizards = [];
161 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'] as $identifier => $class) {
162 $wizardInstance = GeneralUtility::makeInstance($class);
163 $registry = GeneralUtility::makeInstance(Registry::class);
164 $markedDoneInRegistry = $registry->get(
165 'installUpdate',
166 $identifier,
167 false
168 );
169 // already done
170 if ($markedDoneInRegistry) {
171 continue;
172 }
173 // not cli runnable
174 if (!($wizardInstance instanceof UpgradeWizardInterface)) {
175 continue;
176 }
177
178 if ($wizardInstance instanceof ChattyInterface) {
179 $wizardInstance->setOutput($output);
180 }
181
182 if ($wizardInstance->updateNecessary()) {
183 $wizardInstances = [];
184 if (!($wizardInstance instanceof UpgradeWizardInterface)) {
185 $output->writeln(
186 'Wizard ' .
187 $class .
188 ' needs to be manually run from the install tool, as it does not implement ' .
189 UpgradeWizardInterface::class
190 );
191 } else {
192 $wizardInstances[] = $wizardInstance;
193 }
194 if (count($wizardInstances) > 0) {
195 $this->handlePrerequisites($output, $wizardInstances);
196 foreach ($wizardInstances as $wizardInstance) {
197 $this->runSingleWizard($input, $output, $wizardInstance);
198 }
199 }
200 }
201 }
202 return $wizards;
203 }
204 }