982aed646b32c69cc0d6515a871fa82c280ef6c8
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / StepController.php
1 <?php
2 namespace TYPO3\CMS\Install\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18 use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService;
19
20 /**
21 * Install step controller, dispatcher class of step actions.
22 */
23 class StepController extends AbstractController {
24
25 /**
26 * @var array List of valid action names that need authentication. Order is important!
27 */
28 protected $authenticationActions = array(
29 'environmentAndFolders',
30 'databaseConnect',
31 'databaseSelect',
32 'databaseData',
33 'defaultConfiguration',
34 );
35
36 /**
37 * Index action acts a a dispatcher to different steps
38 *
39 * Warning: Order of these methods is security relevant and interferes with different access
40 * conditions (new/existing installation). See the single method comments for details.
41 *
42 * @throws Exception
43 * @return void
44 */
45 public function execute() {
46 $this->loadBaseExtensions();
47 $this->initializeObjectManager();
48
49 $this->outputInstallToolNotEnabledMessageIfNeeded();
50 $this->outputInstallToolPasswordNotSetMessageIfNeeded();
51 $this->recreatePackageStatesFileIfNotExisting();
52 $this->executeOrOutputFirstInstallStepIfNeeded();
53 $this->executeSilentConfigurationUpgradesIfNeeded();
54 $this->initializeSession();
55 $this->checkSessionToken();
56 $this->checkSessionLifetime();
57 $this->loginIfRequested();
58 $this->outputLoginFormIfNotAuthorized();
59 $this->executeSpecificStep();
60 $this->outputSpecificStep();
61 $this->redirectToTool();
62 }
63
64 /**
65 * Execute a step action if requested. If executed, a redirect is done, so
66 * the next request will render step one again if needed or initiate a
67 * request to test the next step.
68 *
69 * @throws Exception
70 * @return void
71 */
72 protected function executeSpecificStep() {
73 $action = $this->getAction();
74 $postValues = $this->getPostValues();
75 if ($action && isset($postValues['set']) && $postValues['set'] === 'execute') {
76 $stepAction = $this->getActionInstance($action);
77 $stepAction->setAction($action);
78 $stepAction->setToken($this->generateTokenForAction($action));
79 $stepAction->setPostValues($this->getPostValues());
80 $messages = $stepAction->execute();
81 $this->addSessionMessages($messages);
82 $this->redirect();
83 }
84 }
85
86 /**
87 * Render a specific step. Fallback to first step if none is given.
88 * The according step is instantiated and 'needsExecution' is called. If
89 * it needs execution, the step will be rendered, otherwise a redirect
90 * to test the next step is initiated.
91 *
92 * @return void
93 */
94 protected function outputSpecificStep() {
95 $action = $this->getAction();
96 if ($action === '') {
97 // First step action
98 list($action) = $this->authenticationActions;
99 }
100 $stepAction = $this->getActionInstance($action);
101 $stepAction->setAction($action);
102 $stepAction->setController('step');
103 $stepAction->setToken($this->generateTokenForAction($action));
104 $stepAction->setPostValues($this->getPostValues());
105
106 $needsExecution = TRUE;
107 try {
108 // needsExecution() may throw a RedirectException to communicate that it changed
109 // configuration parameters and need an application reload.
110 $needsExecution = $stepAction->needsExecution();
111 } catch (Exception\RedirectException $e) {
112 $this->redirect();
113 }
114
115 if ($needsExecution) {
116 if ($this->isInitialInstallationInProgress()) {
117 $currentStep = (array_search($action, $this->authenticationActions) + 1);
118 $totalSteps = count($this->authenticationActions);
119 $stepAction->setStepsCounter($currentStep, $totalSteps);
120 }
121 $stepAction->setMessages($this->session->getMessagesAndFlush());
122 $this->output($stepAction->handle());
123 } else {
124 // Redirect to next step if there are any
125 $currentPosition = array_keys($this->authenticationActions, $action, TRUE);
126 $nextAction = array_slice($this->authenticationActions, $currentPosition[0] + 1, 1);
127 if (!empty($nextAction)) {
128 $this->redirect('', $nextAction[0]);
129 }
130 }
131 }
132
133 /**
134 * Instantiate a specific action class
135 *
136 * @param string $action Action to instantiate
137 * @throws Exception
138 * @return \TYPO3\CMS\Install\Controller\Action\Step\StepInterface
139 */
140 protected function getActionInstance($action) {
141 $this->validateAuthenticationAction($action);
142 $actionClass = ucfirst($action);
143 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $stepAction */
144 $stepAction = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\' . $actionClass);
145 if (!($stepAction instanceof Action\Step\StepInterface)) {
146 throw new Exception(
147 $action . ' does non implement StepInterface',
148 1371303903
149 );
150 }
151 return $stepAction;
152 }
153
154 /**
155 * If the last step was reached and none needs execution, a redirect
156 * to call the tool controller is initiated.
157 *
158 * @return void
159 */
160 protected function redirectToTool() {
161 $this->redirect('tool');
162 }
163
164 /**
165 * Create PackageStates.php if missing and LocalConfiguration exists.
166 *
167 * It is fired if PackageStates.php is deleted on a running instance,
168 * all packages marked as "part of minimal system" are activated in this case.
169 *
170 * The step installer creates typo3conf/, LocalConfiguration and PackageStates in
171 * one call, so an "installation in progress" does not trigger creation of
172 * PackageStates here.
173 *
174 * @throws \Exception
175 * @return void
176 */
177 protected function recreatePackageStatesFileIfNotExisting() {
178 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
179 $configurationManager = $this->objectManager->get(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
180 $localConfigurationFileLocation = $configurationManager->getLocalConfigurationFileLocation();
181 $localConfigurationFileExists = is_file($localConfigurationFileLocation);
182 $packageStatesFilePath = PATH_typo3conf . 'PackageStates.php';
183 $localConfigurationBackupFilePath = preg_replace(
184 '/\\.php$/',
185 '.beforePackageStatesMigration.php',
186 $configurationManager->getLocalConfigurationFileLocation()
187 );
188
189 if (file_exists($packageStatesFilePath)
190 || (is_dir(PATH_typo3conf) && !$localConfigurationFileExists)
191 || !is_dir(PATH_typo3conf)
192 ) {
193 return;
194 }
195
196 try {
197 /** @var \TYPO3\CMS\Core\Package\FailsafePackageManager $packageManager */
198 $packageManager = \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->getEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class);
199
200 // Activate all packages required for a minimal usable system
201 $packages = $packageManager->getAvailablePackages();
202 foreach ($packages as $package) {
203 /** @var $package \TYPO3\CMS\Core\Package\PackageInterface */
204 if ($package instanceof \TYPO3\CMS\Core\Package\PackageInterface
205 && $package->isPartOfMinimalUsableSystem()
206 ) {
207 $packageManager->activatePackage($package->getPackageKey());
208 }
209 }
210
211 // Backup LocalConfiguration.php
212 copy(
213 $configurationManager->getLocalConfigurationFileLocation(),
214 $localConfigurationBackupFilePath
215 );
216
217 $packageManager->forceSortAndSavePackageStates();
218
219 // Perform a reload to self, so bootstrap now uses new PackageStates.php
220 $this->redirect();
221 } catch (\Exception $exception) {
222 if (file_exists($packageStatesFilePath)) {
223 unlink($packageStatesFilePath);
224 }
225 if (file_exists($localConfigurationBackupFilePath)) {
226 unlink($localConfigurationBackupFilePath);
227 }
228 throw $exception;
229 }
230 }
231
232 /**
233 * The first install step has a special standing and needs separate handling:
234 * At this point no directory exists (no typo3conf, no typo3temp), so we can
235 * not start the session handling (that stores the install tool session within typo3temp).
236 * This also means, we can not start the token handling for CSRF protection. This
237 * is no real problem, since no local configuration or other security relevant
238 * information was created yet.
239 *
240 * So, if no typo3conf directory exists yet, the first step is just rendered, or
241 * executed if called so. After that, a redirect is initiated to proceed with
242 * other tasks.
243 *
244 * @return void
245 */
246 protected function executeOrOutputFirstInstallStepIfNeeded() {
247 $postValues = $this->getPostValues();
248
249 $wasExecuted = FALSE;
250 $errorMessagesFromExecute = array();
251 if (isset($postValues['action'])
252 && $postValues['action'] === 'environmentAndFolders'
253 ) {
254 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
255 $action = $this->objectManager->get(\TYPO3\CMS\Install\Controller\Action\Step\EnvironmentAndFolders::class);
256 $errorMessagesFromExecute = $action->execute();
257 $wasExecuted = TRUE;
258 }
259
260 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
261 $action = $this->objectManager->get(\TYPO3\CMS\Install\Controller\Action\Step\EnvironmentAndFolders::class);
262
263 $needsExecution = TRUE;
264 try {
265 // needsExecution() may throw a RedirectException to communicate that it changed
266 // configuration parameters and need an application reload.
267 $needsExecution = $action->needsExecution();
268 } catch (Exception\RedirectException $e) {
269 $this->redirect();
270 }
271
272 $testReflection = new \ReflectionMethod(get_class($this), __FUNCTION__);
273 if (!@is_dir(PATH_typo3conf)
274 || $needsExecution
275 || $testReflection->getDocComment() === FALSE
276 ) {
277 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
278 $action = $this->objectManager->get(\TYPO3\CMS\Install\Controller\Action\Step\EnvironmentAndFolders::class);
279 if ($this->isInitialInstallationInProgress()) {
280 $currentStep = (array_search('environmentAndFolders', $this->authenticationActions) + 1);
281 $totalSteps = count($this->authenticationActions);
282 $action->setStepsCounter($currentStep, $totalSteps);
283 }
284 $action->setController('step');
285 $action->setAction('environmentAndFolders');
286 if (!empty($errorMessagesFromExecute)) {
287 $action->setMessages($errorMessagesFromExecute);
288 }
289 $this->output($action->handle());
290 }
291
292 if ($wasExecuted) {
293 $this->redirect();
294 }
295 }
296
297 /**
298 * Call silent upgrade class, redirect to self if configuration was changed.
299 *
300 * @return void
301 */
302 protected function executeSilentConfigurationUpgradesIfNeeded() {
303 /** @var SilentConfigurationUpgradeService $upgradeService */
304 $upgradeService = $this->objectManager->get(SilentConfigurationUpgradeService::class);
305 try {
306 $upgradeService->execute();
307 } catch (Exception\RedirectException $e) {
308 $this->redirect();
309 }
310 }
311
312 }