[!!!][TASK] Rewrite install tool
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / Action / Tool / ImportantActions.php
1 <?php
2 namespace TYPO3\CMS\Install\Controller\Action\Tool;
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\Install\Controller\Action;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29
30 /**
31 * Handle important actions
32 */
33 class ImportantActions extends Action\AbstractAction implements Action\ActionInterface {
34
35 /**
36 * Handle this action
37 *
38 * @return string content
39 */
40 public function handle() {
41 $this->initialize();
42
43 if (isset($this->postValues['set']['changeEncryptionKey'])) {
44 $this->setNewEncryptionKeyAndLogOut();
45 }
46
47 $actionMessages = array();
48 if (isset($this->postValues['set']['changeInstallToolPassword'])) {
49 $actionMessages[] = $this->changeInstallToolPassword();
50 }
51 if (isset($this->postValues['set']['changeSiteName'])) {
52 $actionMessages[] = $this->changeSiteName();
53 }
54 if (isset($this->postValues['set']['createAdministrator'])) {
55 $actionMessages[] = $this->createAdministrator();
56 }
57
58 // Database analyzer handling
59 if (isset($this->postValues['set']['databaseAnalyzerExecute'])
60 || isset($this->postValues['set']['databaseAnalyzerAnalyze'])
61 ) {
62 $this->loadExtLocalconfDatabaseAndExtTables();
63 }
64 if (isset($this->postValues['set']['databaseAnalyzerExecute'])) {
65 $actionMessages = array_merge($actionMessages, $this->databaseAnalyzerExecute());
66 }
67 if (isset($this->postValues['set']['databaseAnalyzerAnalyze'])) {
68 $actionMessages[] = $this->databaseAnalyzerAnalyze();
69 }
70
71 $this->view->assign('actionMessages', $actionMessages);
72
73 $operatingSystem = TYPO3_OS === 'WIN' ? 'Windows' : 'Unix';
74 $cgiDetected = (PHP_SAPI == 'fpm-fcgi' || PHP_SAPI == 'cgi' || PHP_SAPI == 'isapi' || PHP_SAPI == 'cgi-fcgi')
75 ? TRUE
76 : FALSE;
77
78 $this->view
79 ->assign('operatingSystem', $operatingSystem)
80 ->assign('cgiDetected', $cgiDetected)
81 ->assign('databaseName', $GLOBALS['TYPO3_CONF_VARS']['DB']['database'])
82 ->assign('databaseUsername', $GLOBALS['TYPO3_CONF_VARS']['DB']['username'])
83 ->assign('databaseHost', $GLOBALS['TYPO3_CONF_VARS']['DB']['host'])
84 ->assign('databasePort', $GLOBALS['TYPO3_CONF_VARS']['DB']['port'])
85 ->assign('databaseNumberOfTables', count($this->getDatabase()->admin_get_tables()));
86
87 return $this->view->render();
88 }
89
90 /**
91 * Set new password if requested
92 *
93 * @return \TYPO3\CMS\Install\Status\StatusInterface
94 */
95 protected function changeInstallToolPassword() {
96 $values = $this->postValues['values'];
97 if ($values['newInstallToolPassword'] !== $values['newInstallToolPasswordCheck']) {
98 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
99 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
100 $message->setTitle('Install tool password not changed');
101 $message->setMessage('Given passwords do not match.');
102 } elseif (strlen($values['newInstallToolPassword']) < 8) {
103 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
104 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
105 $message->setTitle('Install tool password not changed');
106 $message->setMessage('Given passwords must be a least eight characters long.');
107 } else {
108 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
109 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
110 $configurationManager->setLocalConfigurationValueByPath('BE/installToolPassword', md5($values['newInstallToolPassword']));
111 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
112 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\OkStatus');
113 $message->setTitle('Install tool password changed');
114 }
115 return $message;
116 }
117
118 /**
119 * Set new site name
120 *
121 * @return \TYPO3\CMS\Install\Status\StatusInterface
122 */
123 protected function changeSiteName() {
124 $values = $this->postValues['values'];
125 if (isset($values['newSiteName']) && strlen($values['newSiteName']) > 0) {
126 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
127 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
128 $configurationManager->setLocalConfigurationValueByPath('SYS/sitename', $values['newSiteName']);
129 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
130 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\OkStatus');
131 $message->setTitle('Site name changed');
132 $this->view->assign('siteName', $values['newSiteName']);
133 } else {
134 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
135 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
136 $message->setTitle('Site name not changed');
137 $message->setMessage('Site name must be at least one character long.');
138 }
139 return $message;
140 }
141
142 /**
143 * Set new encryption key
144 *
145 * @return void
146 */
147 protected function setNewEncryptionKeyAndLogOut() {
148 $newKey = \TYPO3\CMS\Core\Utility\GeneralUtility::getRandomHexString(96);
149 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
150 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
151 $configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $newKey);
152 /** @var $formProtection \TYPO3\CMS\Core\FormProtection\InstallToolFormProtection */
153 $formProtection = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get(
154 'TYPO3\\CMS\\Core\\FormProtection\\InstallToolFormProtection'
155 );
156 $formProtection->clean();
157 /** @var \TYPO3\CMS\Install\Service\SessionService $session */
158 $session = $this->objectManager->get('TYPO3\\CMS\\Install\\Service\\SessionService');
159 $session->destroySession();
160 \TYPO3\CMS\Core\Utility\HttpUtility::redirect('Install.php?install[context]=' . $this->getContext());
161 }
162
163 /**
164 * Create administrator user
165 *
166 * @return \TYPO3\CMS\Install\Status\StatusInterface
167 */
168 protected function createAdministrator() {
169 $values = $this->postValues['values'];
170 $username = preg_replace('/[^\\da-z._]/i', '', trim($values['newUserUsername']));
171 $password = $values['newUserPassword'];
172 $passwordCheck = $values['newUserPasswordCheck'];
173
174 if (strlen($username) < 1) {
175 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
176 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
177 $message->setTitle('Administrator user not created');
178 $message->setMessage('No valid username given.');
179 } elseif ($password !== $passwordCheck) {
180 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
181 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
182 $message->setTitle('Administrator user not created');
183 $message->setMessage('Passwords do not match.');
184 } elseif (strlen($password) < 8) {
185 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
186 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
187 $message->setTitle('Administrator user not created');
188 $message->setMessage('Password must be at least eight characters long.');
189 } else {
190 $database = $this->getDatabase();
191 $userExists = $database->exec_SELECTcountRows(
192 'uid',
193 'be_users',
194 'username=' . $database->fullQuoteStr($username, 'be_users')
195 );
196 if ($userExists) {
197 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
198 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
199 $message->setTitle('Administrator user not created');
200 $message->setMessage('A user with username ' . $username . ' exists already.');
201 } else {
202 // @TODO: Handle saltedpasswords in installer and store password salted in the first place
203 $adminUserFields = array(
204 'username' => $username,
205 'password' => md5($password),
206 'admin' => 1,
207 'tstamp' => $GLOBALS['EXEC_TIME'],
208 'crdate' => $GLOBALS['EXEC_TIME']
209 );
210 $database->exec_INSERTquery('be_users', $adminUserFields);
211 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
212 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\OkStatus');
213 $message->setTitle('Administrator created');
214 }
215 }
216
217 return $message;
218 }
219
220 /**
221 * Execute database migration
222 *
223 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
224 */
225 protected function databaseAnalyzerExecute() {
226 $messages = array();
227
228 // Early return in case no updade was selected
229 if (empty($this->postValues['values'])) {
230 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
231 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\WarningStatus');
232 $message->setTitle('No database changes selected');
233 $messages[] = $message;
234 return $message;
235 }
236
237 /** @var \TYPO3\CMS\Install\Service\SqlSchemaMigrationService $schemaMigrationService */
238 $schemaMigrationService = $this->objectManager->get('TYPO3\\CMS\\Install\\Service\\SqlSchemaMigrationService');
239 /** @var \TYPO3\CMS\Install\Service\SqlExpectedSchemaService $expectedSchemaService */
240 $expectedSchemaService = $this->objectManager->get('TYPO3\\CMS\\Install\\Service\\SqlExpectedSchemaService');
241 $expectedSchema = $expectedSchemaService->getExpectedDatabaseSchema();
242 $currentSchema = $schemaMigrationService->getFieldDefinitions_database();
243
244 $statementHashesToPerform = $this->postValues['values'];
245
246 $results = array();
247
248 // Difference from expected to current
249 $addCreateChange = $schemaMigrationService->getDatabaseExtra($expectedSchema, $currentSchema);
250 $addCreateChange = $schemaMigrationService->getUpdateSuggestions($addCreateChange);
251 $results[] = $schemaMigrationService->performUpdateQueries($addCreateChange['add'], $statementHashesToPerform);
252 $results[] = $schemaMigrationService->performUpdateQueries($addCreateChange['change'], $statementHashesToPerform);
253 $results[] = $schemaMigrationService->performUpdateQueries($addCreateChange['create_table'], $statementHashesToPerform);
254
255 // Difference from current to expected
256 $dropRename = $schemaMigrationService->getDatabaseExtra($currentSchema, $expectedSchema);
257 $dropRename = $schemaMigrationService->getUpdateSuggestions($dropRename, 'remove');
258 $results[] = $schemaMigrationService->performUpdateQueries($dropRename['change'], $statementHashesToPerform);
259 $results[] = $schemaMigrationService->performUpdateQueries($dropRename['drop'], $statementHashesToPerform);
260 $results[] = $schemaMigrationService->performUpdateQueries($dropRename['change_table'], $statementHashesToPerform);
261 $results[] = $schemaMigrationService->performUpdateQueries($dropRename['drop_table'], $statementHashesToPerform);
262
263 // Create error flash messages if any
264 foreach ($results as $resultSet) {
265 if (is_array($resultSet)) {
266 foreach ($resultSet as $errorMessage) {
267 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
268 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
269 $message->setTitle('Database update failed');
270 $message->setMessage('Error: ' . $errorMessage);
271 $messages[] = $message;
272 }
273 }
274 }
275
276 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
277 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\OkStatus');
278 $message->setTitle('Executed database updates');
279 $messages[] = $message;
280
281 return $messages;
282 }
283
284 /**
285 * "Compare" action of analyzer
286 *
287 * @TODO: The SchemaMigration API is a mess and should be refactored
288 * @TODO: Refactoring this should aim to make EM and dbal independent from ext:install by moving SchemaMigration to ext:core
289 * @return \TYPO3\CMS\Install\Status\StatusInterface
290 */
291 protected function databaseAnalyzerAnalyze() {
292 /** @var \TYPO3\CMS\Install\Service\SqlSchemaMigrationService $schemaMigrationService */
293 $schemaMigrationService = $this->objectManager->get('TYPO3\\CMS\\Install\\Service\\SqlSchemaMigrationService');
294 /** @var \TYPO3\CMS\Install\Service\SqlExpectedSchemaService $expectedSchemaService */
295 $expectedSchemaService = $this->objectManager->get('TYPO3\\CMS\\Install\\Service\\SqlExpectedSchemaService');
296 $expectedSchema = $expectedSchemaService->getExpectedDatabaseSchema();
297
298 $currentSchema = $schemaMigrationService->getFieldDefinitions_database();
299
300 $databaseAnalyzerSuggestion = array();
301
302 // Difference from expected to current
303 $addCreateChange = $schemaMigrationService->getDatabaseExtra($expectedSchema, $currentSchema);
304 $addCreateChange = $schemaMigrationService->getUpdateSuggestions($addCreateChange);
305 if (isset($addCreateChange['create_table'])) {
306 $databaseAnalyzerSuggestion['addTable'] = array();
307 foreach ($addCreateChange['create_table'] as $hash => $statement) {
308 $databaseAnalyzerSuggestion['addTable'][$hash] = array(
309 'hash' => $hash,
310 'statement' => $statement,
311 );
312 }
313 }
314 if (isset($addCreateChange['add'])) {
315 $databaseAnalyzerSuggestion['addField'] = array();
316 foreach ($addCreateChange['add'] as $hash => $statement) {
317 $databaseAnalyzerSuggestion['addField'][$hash] = array(
318 'hash' => $hash,
319 'statement' => $statement,
320 );
321 }
322 }
323 if (isset($addCreateChange['change'])) {
324 $databaseAnalyzerSuggestion['change'] = array();
325 foreach ($addCreateChange['change'] as $hash => $statement) {
326 $databaseAnalyzerSuggestion['change'][$hash] = array(
327 'hash' => $hash,
328 'statement' => $statement,
329 );
330 if (isset($addCreateChange['change_currentValue'][$hash])) {
331 $databaseAnalyzerSuggestion['change'][$hash]['current'] = $addCreateChange['change_currentValue'][$hash];
332 }
333 }
334 }
335
336 // Difference from current to expected
337 $dropRename = $schemaMigrationService->getDatabaseExtra($currentSchema, $expectedSchema);
338 $dropRename = $schemaMigrationService->getUpdateSuggestions($dropRename, 'remove');
339 if (isset($dropRename['change_table'])) {
340 $databaseAnalyzerSuggestion['renameTableToUnused'] = array();
341 foreach ($dropRename['change_table'] as $hash => $statement) {
342 $databaseAnalyzerSuggestion['renameTableToUnused'][$hash] = array(
343 'hash' => $hash,
344 'statement' => $statement,
345 );
346 if (!empty($dropRename['tables_count'][$hash])) {
347 $databaseAnalyzerSuggestion['renameTableToUnused'][$hash]['count'] = $dropRename['tables_count'][$hash];
348 }
349 }
350 }
351 if (isset($dropRename['change'])) {
352 $databaseAnalyzerSuggestion['renameTableFieldToUnused'] = array();
353 foreach ($dropRename['change'] as $hash => $statement) {
354 $databaseAnalyzerSuggestion['renameTableFieldToUnused'][$hash] = array(
355 'hash' => $hash,
356 'statement' => $statement,
357 );
358 }
359 }
360 if (isset($dropRename['drop'])) {
361 $databaseAnalyzerSuggestion['deleteField'] = array();
362 foreach ($dropRename['drop'] as $hash => $statement) {
363 $databaseAnalyzerSuggestion['deleteField'][$hash] = array(
364 'hash' => $hash,
365 'statement' => $statement,
366 );
367 }
368 }
369 if (isset($dropRename['drop_table'])) {
370 $databaseAnalyzerSuggestion['deleteTable'] = array();
371 foreach ($dropRename['drop_table'] as $hash => $statement) {
372 $databaseAnalyzerSuggestion['deleteTable'][$hash] = array(
373 'hash' => $hash,
374 'statement' => $statement,
375 );
376 if (!empty($dropRename['tables_count'][$hash])) {
377 $databaseAnalyzerSuggestion['deleteTable'][$hash]['count'] = $dropRename['tables_count'][$hash];
378 }
379 }
380 }
381
382 $this->view->assign('databaseAnalyzerSuggestion', $databaseAnalyzerSuggestion);
383
384 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
385 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\OkStatus');
386 $message->setTitle('Analyzed current database');
387 return $message;
388 }
389 }
390 ?>