[TASK] Move configuration code from bootstrap to ext:install
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / StepController.php
1 <?php
2 namespace TYPO3\CMS\Install\Controller;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28
29 /**
30 * Install step controller, dispatcher class of step actions.
31 */
32 class StepController extends AbstractController {
33
34 /**
35 * @var array List of valid action names that need authentication. Order is important!
36 */
37 protected $authenticationActions = array(
38 'environmentAndFolders',
39 'databaseConnect',
40 'databaseSelect',
41 'databaseData',
42 'defaultConfiguration',
43 );
44
45 /**
46 * Index action acts a a dispatcher to different steps
47 *
48 * @throws Exception
49 * @return void
50 */
51 public function execute() {
52 $this->loadBaseExtensions();
53 $this->initializeObjectManager();
54
55 // Warning: Order of these methods is security relevant and interferes with different access
56 // conditions (new/existing installation). See the single method comments for details.
57 $this->outputInstallToolNotEnabledMessageIfNeeded();
58 $this->migrateLocalconfToLocalConfigurationIfNeeded();
59 $this->outputInstallToolPasswordNotSetMessageIfNeeded();
60 $this->executeOrOutputFirstInstallStepIfNeeded();
61 $this->executeSilentConfigurationUpgradesIfNeeded();
62 $this->initializeSession();
63 $this->checkSessionToken();
64 $this->checkSessionLifetime();
65 $this->loginIfRequested();
66 $this->outputLoginFormIfNotAuthorized();
67 $this->executeSpecificStep();
68 $this->outputSpecificStep();
69 $this->redirectToTool();
70 }
71
72 /**
73 * Execute a step action if requested. If executed, a redirect is done, so
74 * the next request will render step one again if needed or initiate a
75 * request to test the next step.
76 *
77 * @throws Exception
78 * @return void
79 */
80 protected function executeSpecificStep() {
81 $action = $this->getAction();
82 $postValues = $this->getPostValues();
83 if ($action && isset($postValues['set']) && $postValues['set'] === 'execute') {
84 $stepAction = $this->getActionInstance($action);
85 $stepAction->setAction($action);
86 $stepAction->setToken($this->generateTokenForAction($action));
87 $stepAction->setPostValues($this->getPostValues());
88 $messages = $stepAction->execute();
89 $this->addSessionMessages($messages);
90 $this->redirect();
91 }
92 }
93
94 /**
95 * Render a specific step. Fallback to first step if none is given.
96 * The according step is instantiated and 'needsExecution' is called. If
97 * it needs execution, the step will be rendered, otherwise a redirect
98 * to test the next step is initiated.
99 *
100 * @return void
101 */
102 protected function outputSpecificStep() {
103 $action = $this->getAction();
104 if ($action === '') {
105 // First step action
106 list($action) = $this->authenticationActions;
107 }
108 $stepAction = $this->getActionInstance($action);
109 $stepAction->setAction($action);
110 $stepAction->setController('step');
111 $stepAction->setToken($this->generateTokenForAction($action));
112 $stepAction->setPostValues($this->getPostValues());
113
114 try {
115 // needsExecution() may throw a RedirectException to communicate that it changed
116 // configuration parameters and need an application reload.
117 $needsExecution = $stepAction->needsExecution();
118 } catch (Exception\RedirectException $e) {
119 $this->redirect();
120 }
121
122 if ($needsExecution) {
123 $stepAction->setMessages($this->session->getMessagesAndFlush());
124 $this->output($stepAction->handle());
125 } else {
126 // Redirect to next step if there are any
127 $currentPosition = array_keys($this->authenticationActions, $action, TRUE);
128 $nextAction = array_slice($this->authenticationActions, $currentPosition[0] + 1, 1);
129 if (!empty($nextAction)) {
130 $this->redirect('', $nextAction[0]);
131 }
132 }
133 }
134
135 /**
136 * Instantiate a specific action class
137 *
138 * @param string $action Action to instantiate
139 * @throws Exception
140 * @return \TYPO3\CMS\Install\Controller\Action\Step\StepInterface
141 */
142 protected function getActionInstance($action) {
143 $this->validateAuthenticationAction($action);
144 $actionClass = ucfirst($action);
145 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $stepAction */
146 $stepAction = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\' . $actionClass);
147 if (!($stepAction instanceof Action\Step\StepInterface)) {
148 throw new Exception(
149 $action . ' does non implement StepInterface',
150 1371303903
151 );
152 }
153 return $stepAction;
154 }
155
156 /**
157 * If the last step was reached and none needs execution, a redirect
158 * to call the tool controller is initiated.
159 *
160 * @return void
161 */
162 protected function redirectToTool() {
163 $this->redirect('tool');
164 }
165
166 /**
167 * "Silent" upgrade very early in step installer, before rendering step 1:
168 * If typo3conf and typo3conf/localconf.php exist, but no typo3conf/LocalConfiguration,
169 * create LocalConfiguration.php / AdditionalConfiguration.php from localconf.php
170 * Might throw exception if typo3conf directory is not writable.
171 *
172 * @return void
173 */
174 protected function migrateLocalconfToLocalConfigurationIfNeeded() {
175 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
176 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
177
178 $localConfigurationFileLocation = $configurationManager->getLocalConfigurationFileLocation();
179 $localConfigurationFileExists = is_file($localConfigurationFileLocation);
180 $localConfFileLocation = PATH_typo3conf . 'localconf.php';
181 $localConfFileExists = is_file($localConfFileLocation);
182
183 if (is_dir(PATH_typo3conf) && $localConfFileExists && !$localConfigurationFileExists) {
184 $localConfContent = file($localConfFileLocation);
185
186 // Line array for the three categories: localConfiguration, db settings, additionalConfiguration
187 $typo3ConfigurationVariables = array();
188 $typo3DatabaseVariables = array();
189 $additionalConfiguration = array();
190 foreach ($localConfContent as $line) {
191 $line = trim($line);
192 $matches = array();
193 // Convert extList to array
194 if (
195 preg_match('/^\\$TYPO3_CONF_VARS\\[\'EXT\'\\]\\[\'extList\'\\] *={1} *\'(.+)\';{1}/', $line, $matches) === 1
196 || preg_match('/^\\$GLOBALS\\[\'TYPO3_CONF_VARS\'\\]\\[\'EXT\'\\]\\[\'extList\'\\] *={1} *\'(.+)\';{1}/', $line, $matches) === 1
197 ) {
198 $extListAsArray = GeneralUtility::trimExplode(',', $matches[1], TRUE);
199 $typo3ConfigurationVariables[] = '$TYPO3_CONF_VARS[\'EXT\'][\'extListArray\'] = ' . var_export($extListAsArray, TRUE) . ';';
200 } elseif (
201 preg_match('/^\\$TYPO3_CONF_VARS.+;{1}/', $line, $matches) === 1
202 ) {
203 $typo3ConfigurationVariables[] = $matches[0];
204 } elseif (
205 preg_match('/^\\$GLOBALS\\[\'TYPO3_CONF_VARS\'\\].+;{1}/', $line, $matches) === 1
206 ) {
207 $lineWithoutGlobals = str_replace('$GLOBALS[\'TYPO3_CONF_VARS\']', '$TYPO3_CONF_VARS', $matches[0]);
208 $typo3ConfigurationVariables[] = $lineWithoutGlobals;
209 } elseif (
210 preg_match('/^\\$typo_db.+;{1}/', $line, $matches) === 1
211 ) {
212 eval($matches[0]);
213 if (isset($typo_db_host)) {
214 $typo3DatabaseVariables['host'] = $typo_db_host;
215 } elseif (isset($typo_db)) {
216 $typo3DatabaseVariables['database'] = $typo_db;
217 } elseif (isset($typo_db_username)) {
218 $typo3DatabaseVariables['username'] = $typo_db_username;
219 } elseif (isset($typo_db_password)) {
220 $typo3DatabaseVariables['password'] = $typo_db_password;
221 } elseif (isset($typo_db_extTableDef_script)) {
222 $typo3DatabaseVariables['extTablesDefinitionScript'] = $typo_db_extTableDef_script;
223 }
224 unset($typo_db_host, $typo_db, $typo_db_username, $typo_db_password, $typo_db_extTableDef_script);
225 } elseif (
226 strlen($line) > 0 && preg_match('/^\\/\\/.+|^#.+|^<\\?php$|^<\\?$|^\\?>$/', $line, $matches) === 0
227 ) {
228 $additionalConfiguration[] = $line;
229 }
230 }
231
232 // Build new TYPO3_CONF_VARS array
233 $TYPO3_CONF_VARS = NULL;
234 // Issue #39434: Combining next two lines into one triggers a weird issue in some PHP versions
235 $evalData = implode(LF, $typo3ConfigurationVariables);
236 eval($evalData);
237
238 // Add db settings to array
239 $TYPO3_CONF_VARS['DB'] = $typo3DatabaseVariables;
240 $TYPO3_CONF_VARS = \TYPO3\CMS\Core\Utility\ArrayUtility::sortByKeyRecursive($TYPO3_CONF_VARS);
241
242 // Write out new LocalConfiguration file
243 $configurationManager->writeLocalConfiguration($TYPO3_CONF_VARS);
244
245 // Write out new AdditionalConfiguration file
246 if (sizeof($additionalConfiguration) > 0) {
247 $configurationManager->writeAdditionalConfiguration($additionalConfiguration);
248 } else {
249 @unlink($configurationManager->getAdditionalConfigurationFileLocation());
250 }
251
252 // Move localconf.php to localconf.obsolete.php
253 rename($localConfFileLocation, PATH_site . 'typo3conf/localconf.obsolete.php');
254
255 // Perform a reload to self, so bootstrap now uses new LocalConfiguration.php
256 $this->redirect();
257 }
258 }
259
260 /**
261 * The first install step has a special standing and needs separate handling:
262 * At this point no directory exists (no typo3conf, no typo3temp), so we can
263 * not start the session handling (that stores the install tool session within typo3temp).
264 * This also means, we can not start the token handling for CSRF protection. This
265 * is no real problem, since no local configuration or other security relevant
266 * information was created yet.
267 *
268 * So, if no typo3conf directory exists yet, the first step is just rendered, or
269 * executed if called so. After that, a redirect is initiated to proceed with
270 * other tasks.
271 *
272 * @return void
273 */
274 protected function executeOrOutputFirstInstallStepIfNeeded() {
275 $postValues = $this->getPostValues();
276
277 $wasExecuted= FALSE;
278 $errorMessagesFromExecute = array();
279 if (isset($postValues['action'])
280 && $postValues['action'] === 'environmentAndFolders'
281 ) {
282 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
283 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
284 $errorMessagesFromExecute = $action->execute();
285 $wasExecuted = TRUE;
286 }
287
288 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
289 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
290
291 try {
292 // needsExecution() may throw a RedirectException to communicate that it changed
293 // configuration parameters and need an application reload.
294 $needsExecution = $action->needsExecution();
295 } catch (Exception\RedirectException $e) {
296 $this->redirect();
297 }
298
299 if (!is_dir(PATH_typo3conf)
300 || count($errorMessagesFromExecute) > 0
301 || $needsExecution
302 ) {
303 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
304 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
305 $action->setController('step');
306 $action->setAction('environmentAndFolders');
307 if (count($errorMessagesFromExecute) > 0) {
308 $action->setMessages($errorMessagesFromExecute);
309 }
310 $this->output($action->handle());
311 }
312
313 if ($wasExecuted) {
314 $this->redirect();
315 }
316 }
317
318 /**
319 * Call silent upgrade class, redirect to self if configuration was changed.
320 *
321 * @return void
322 */
323 protected function executeSilentConfigurationUpgradesIfNeeded() {
324 /** @var \TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService $upgradeService */
325 $upgradeService = $this->objectManager->get(
326 'TYPO3\\CMS\\Install\\Service\\SilentConfigurationUpgradeService'
327 );
328 try {
329 $upgradeService->execute();
330 } catch (Exception\RedirectException $e) {
331 $this->redirect();
332 }
333 }
334 }
335 ?>