* Installer: Improved the installer module.
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / modules / installer / class.tx_install_module_installer.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2007 Thomas Hempel (thomas@work.de)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 class tx_install_module_installer extends tx_install_module_base {
29 private $moduleKey = 'installer';
30
31 /**
32 * Defines the steps for the installation.
33 *
34 * Each step consists of a set of methods, checks and options in various states of processing.
35 * Each step can have the following states
36 *
37 * - pre: Only checks are allowed here! This checks are executed each time a step is called. If any of
38 * the checks return false, the step can't be be processed.
39 *
40 * - main: The main method that is executed in this step. In most cases this will be a formconfig for
41 * the render engine.
42 *
43 * - post: Here are all things located that has to be executed before a step can be finished. This is
44 * the right place for processing and checking the input data from the main method.
45 * If any of this checks returns false the main state is called again.
46 *
47 * @var array
48 */
49 private $stepConfig = array (
50 // these steps basically reimplements the old 1-2-3 installer with some small improvements
51
52 // inital checks / no further processing
53 0 => array (
54 'pre' => array (
55 0 => 'php:checkVersion',
56 1 => 'directories:checkDirs'
57 ),
58 'main' => array (
59 0 => array (
60 'type' => 'form',
61 'module' => 'installer',
62 'method' => 'selectLanguage'
63 )
64 )
65 ),
66
67 // database connection
68 1 => array (
69 'main' => array (
70 0 => array (
71 'type' => 'form',
72 'module' => 'database',
73 'method' => 'databaseConnectionData'
74 )
75 ),
76 'post' => array (
77 'database:connectDatabaseProcess'
78 )
79 ),
80
81 // select / create database
82 2 => array (
83 'main' => array (
84 array (
85 'type' => 'form',
86 'module' => 'database',
87 'method' => 'selectDatabaseForm'
88 )
89 ),
90 'post' => array (
91 'database:createDatabase'
92 )
93 ),
94
95 // create / import tables from file
96 3 => array (
97 'preMode' => 'skipMain',
98 'pre' => array (
99 'database:checkForStaticFiles'
100 ),
101 'main' => array (
102 array (
103 'type' => 'form',
104 'module' => 'database',
105 'method' => 'selectStaticFileForm'
106 )
107 ),
108 'post' => array (
109 'database:importTables'
110 )
111 ),
112
113 // create an admin user
114 4 => array (
115 'main' => array (
116 array (
117 'type' => 'form',
118 'module' => 'database',
119 'method' => 'createAdminForm'
120 )
121 ),
122 'post' => array (
123 'database:createAdmin'
124 )
125 ),
126
127 5 => array (
128 'main' => array (
129 array (
130 'type' => 'label',
131 'index' => 'msg_step5_done'
132 )
133 )
134 )
135
136 );
137
138 /**
139 * Defines which is the last mandatory step
140 *
141 * @var integer
142 */
143 private $lastMandatoryStep = 5;
144
145 /**
146 * Holds the current step
147 *
148 * @var integer
149 */
150 private $step = null;
151
152 /**
153 * This is the main method
154 */
155 public function main() {
156 // get the current step
157 $this->step = (empty($this->env['step'])) ? 0 : intval($this->env['step']);
158
159 // get the state
160 $stepResult = $this->executeStep($this->step);
161
162 // render back button if step is bigger than 1
163 if ($this->step > 0) {
164 $formConfig = array (
165 'type' => 'form',
166 'value' => array(
167 'options' => array(
168 'name' => 'form_stepData',
169 'submit' => $this->get_LL('label_prev_step'),
170 ),
171 'hidden' => array (
172 'step' => $this->step-1,
173 'mode' => '123',
174 'state' => 'pre'
175 ),
176 )
177 );
178
179 $btnBack = $this->pObj->getViewObject()->render($formConfig);
180 } else {
181 $btnBack = '';
182 }
183
184 // build-up the output
185 $marker = array (
186 '###STEP###' => sprintf($this->get_LL('label_step'), $this->step),
187 '###PROGRESS###' => $this->getProgress(),
188 '###MSG_TITLE###' => $this->get_LL('msg_step'.$this->step.'_title'),
189 '###MSG_DESCRIPTION###' => $this->get_LL('msg_step'.$this->step.'_description'),
190 '###BTN_BACK###' => $btnBack,
191 '###CONTENT###' => $stepResult,
192 '###PREV_RESULT###' => $this->pObj->getViewObject()->getLastMessage(true)
193 );
194
195 if (count($this->pObj->getViewObject()->getErrors('general')) > 0) {
196 $marker['###ERRORS_GENERAL###'] = $this->pObj->getViewObject()->renderErrors(true);
197 } else {
198 $marker['###ERRORS_GENERAL###'] = '';
199 }
200
201 $result = array(
202 'title' => '',
203 'content' => $this->pObj->getViewObject()->render(array (
204 'type' => 'html',
205 'value' => array(
206 'template' => implode('', file($this->pObj->getBasicsObject()->getModulePath($this->moduleKey).'res/tpl_step.html')),
207 'marker' => $marker
208 )
209 ))
210 );
211
212 return $result;
213 }
214
215 /**
216 * Executes all states of a certain step.
217 */
218 private function executeStep() {
219 // let's check if the step is defined, die if not
220 if (!isset($this->stepConfig[$this->step])) {
221 $this->addError(sprintf($this->get_LL('msg_error_stepnotdefined'), $this->step), FATAL);
222 return false;
223 }
224
225 // process pre state - result is true if this state is not configured
226 if (isset($this->stepConfig[$this->step]['pre'])) {
227 $preResult = $this->executeStepState('pre');
228 } else {
229 $preResult = true;
230 }
231
232 // exit if pre checks are not sucessfull
233 if ($preResult === false) {
234 // depending on preMode we only skip state main or stop here immediately
235 if (!isset($this->stepConfig[$this->step]['preMode']) &&Ê($this->stepConfig[$this->step]['preMode'] != 'skipMain')) {
236 return false;
237 } else {
238 $this->env['state'] = 'post';
239 }
240 }
241
242 // if state is post, execute that state
243 $processResult = true;
244 if ($this->env['state'] == 'post') {
245 // execute process methods (has to be configured)
246 $processResult = $this->executeStepState('post');
247 $this->env['state'] = 'pre';
248
249 // go to next step if process was ok
250 if ($processResult === true) {
251 $this->step++;
252 return $this->executeStep();
253 }
254 }
255
256 // execute main state (has to be configured!)
257 $mainResult = $this->executeStepState('main');
258
259 if ($mainResult === false) {
260 return false;
261 }
262
263 return $mainResult;
264 }
265
266
267 /**
268 * Executes a certain state in a step. It dispatches all the config to the particular handlers.
269 * For example rendering forms, or calling the methods in that specific module.
270 *
271 * @param integer $state: The state that has to be executed
272 * @return The collected results of all given methods (In state pre and post it returns false if any of the methods returns false!)
273 */
274 private function executeStepState($state) {
275 $stateConfig = $this->stepConfig[$this->step][$state];
276
277 // check if there is any config for this state in this particular step
278 if (!is_array($stateConfig)) {
279 if ($state == 'pre' || $state == 'post') {
280 // state pre and post don't have to be set, if they are not configured we estimate that everything is ok and return true
281 return true;
282 } else {
283 $this->addError(sprintf($this->get_LL('msg_error_stepstatenotdefined'), $state, $this->step), FATAL);
284 return false;
285 }
286 }
287
288 // prepare result streams
289 $formElements = array('normal' => NULL, 'advanced' => NULL);
290 $stateResult = '';
291
292 // cycle through the various methods defined for this state
293 foreach ($stateConfig as $index => $stateMethod) {
294 $stateMethodResult = $this->dispatchStateMethod($stateMethod, $formElements);
295
296 if ($state == 'main') {
297 if ($stateMethodResult == false) {
298 return false;
299 }
300 if (is_string($stateMethodResult)) {
301 $stateResult .= $stateMethodResult;
302 }
303 } else {
304 $stateResult = $stateMethodResult;
305
306 // exit cycle if in pre or post state any state method returned false
307 if ($stateResult === false) {
308 break;
309 }
310 }
311 }
312
313 // render form if any fields are set
314 if ($state == 'main' && (is_array($formElements['normal']) || is_array($formElements['advanced']))) {
315 $elements = array(array(
316 'type' => 'formelement',
317 'value' => array (
318 'elementType' => 'fieldset',
319 'label' => $this->get_LL('label_normalFields')
320 )
321 ));
322
323 $elements = array_merge($elements, $formElements['normal']);
324
325 if (is_array($formElements['advanced'])) {
326 $elements[] = array (
327 'type' => 'formelement',
328 'value' => array (
329 'elementType' => 'fieldset',
330 'label' => $this->get_LL('label_advancedFields'),
331 'class' => 'fieldset-advanced'
332 )
333 );
334 $elements = array_merge($elements, $formElements['advanced']);
335 }
336
337 $formConfig = array (
338 'type' => 'form',
339 'value' => array(
340 'options' => array(
341 'name' => 'form_stepData',
342 'submit' => $this->get_LL('label_next_step'),
343 ),
344 'hidden' => array (
345 'step' => $this->step,
346 'mode' => '123',
347 'state' => 'post'
348 ),
349 'elements' => $elements
350 )
351 );
352
353 $stateResult = $this->pObj->getViewObject()->render($formConfig).$stateResult;
354 }
355
356 return $stateResult;
357 }
358
359 /**
360 * Receives a state config and executs the respective method and returns the result
361 *
362 * @param mixed $stateMethod: The method to call and the optional the type.
363 * @param array $formElements: An array that collects the form elements for the step (PASS BY REFERENCE)
364 * @return False if somethign went wrong, otherwise the result of the called method
365 */
366 private function dispatchStateMethod($stateMethod, &$formElements) {
367 $result = true;
368 if ($stateMethod['type'] == 'label') {
369 $methodResult = nl2br($this->get_LL($stateMethod['index']));
370 } else {
371 if (is_string($stateMethod)) {
372 $methodResult = $this->pObj->getBasicsObject()->executeMethod($stateMethod);
373 } else {
374 $methodResult = $this->pObj->getBasicsObject()->executeMethod(array($stateMethod['module'], $stateMethod['method']));
375 }
376 }
377
378 if ($methodResult === false) {
379 return false;
380 }
381
382 switch ($stateMethod['type']) {
383 case 'form':
384 $formElements = array_merge($formElements, $methodResult);
385 break;
386 default:
387 $result = $methodResult;
388 break;
389 }
390
391 return $result;
392 }
393
394
395 /**
396 * Returns an XHTML fragment with a progressbar, that shows the progress in the installation process.
397 *
398 * @return string
399 */
400 private function getProgress() {
401 $progressBar = '<div id="progressbar"><div class="mandatory" ';
402 $progressBar .= 'style="width:'.(100 /count($this->stepConfig) *$this->step).'%"></div></div>';
403 return $progressBar;
404 }
405
406 /*
407 * STEP METHODS
408 */
409
410 /**
411 * Shows the welcome screen.
412 *
413 */
414 public function welcomeScreen() {
415 return 'Hi';
416 }
417
418 /**
419 * provides a selectbox with all available languages
420 */
421 public function selectLanguage() {
422 $languageData = $this->pObj->getLanguageObject()->includeLLFile('EXT:setup/mod/locallang.xml', false);
423 $this->pObj->setLocalLang(t3lib_div::array_merge_recursive_overrule($this->pObj->getLocalLang(), $languageData));
424
425 $theLanguages = t3lib_div::trimExplode('|',TYPO3_languages);
426 foreach ($theLanguages as $val) {
427 $opt[$val] = $this->pObj->getBasicsObject()->getLabel('lang_'.$val);
428 }
429
430 $elements['normal'] = array (
431 array (
432 'type' => 'formelement',
433 'value' => array (
434 'label' => 'label_available_languages',
435 'elementType' => 'selectbox',
436 'options' => array (
437 'name' => 'L',
438 'elements' => $opt
439 )
440 )
441 )
442 );
443
444 return $elements;
445 }
446 }
447
448 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/install/modules/installer/class.tx_install_module_installer.php']) {
449 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/install/modules/installer/class.tx_install_module_installer.php']);
450 }
451 ?>