[FEATURE] Doctrine: Implement SchemaMigrationService
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / Action / Step / DatabaseData.php
1 <?php
2 namespace TYPO3\CMS\Install\Controller\Action\Step;
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 Doctrine\DBAL\DBALException;
18 use TYPO3\CMS\Core\Configuration\ConfigurationManager;
19 use TYPO3\CMS\Core\Database\ConnectionPool;
20 use TYPO3\CMS\Core\Database\Schema\SchemaMigrator;
21 use TYPO3\CMS\Core\Database\Schema\SqlReader;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23 use TYPO3\CMS\Install\Status\ErrorStatus;
24
25 /**
26 * Populate base tables, insert admin user, set install tool password
27 */
28 class DatabaseData extends AbstractStepAction
29 {
30 /**
31 * Import tables and data, create admin user, create install tool password
32 *
33 * @return \TYPO3\CMS\Install\Status\StatusInterface[]
34 */
35 public function execute()
36 {
37 $result = [];
38
39 /** @var ConfigurationManager $configurationManager */
40 $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
41
42 $postValues = $this->postValues['values'];
43
44 $username = (string)$postValues['username'] !== '' ? $postValues['username'] : 'admin';
45
46 // Check password and return early if not good enough
47 $password = $postValues['password'];
48 if (strlen($password) < 8) {
49 $errorStatus = GeneralUtility::makeInstance(ErrorStatus::class);
50 $errorStatus->setTitle('Administrator password not secure enough!');
51 $errorStatus->setMessage(
52 'You are setting an important password here! It gives an attacker full control over your instance if cracked.' .
53 ' It should be strong (include lower and upper case characters, special characters and numbers) and must be at least eight characters long.'
54 );
55 $result[] = $errorStatus;
56 return $result;
57 }
58
59 // Set site name
60 if (!empty($postValues['sitename'])) {
61 $configurationManager->setLocalConfigurationValueByPath('SYS/sitename', $postValues['sitename']);
62 }
63
64 $result = $this->importDatabaseData();
65 if (!empty($result)) {
66 return $result;
67 }
68
69 // Insert admin user
70 $adminUserFields = [
71 'username' => $username,
72 'password' => $this->getHashedPassword($password),
73 'admin' => 1,
74 'tstamp' => $GLOBALS['EXEC_TIME'],
75 'crdate' => $GLOBALS['EXEC_TIME']
76 ];
77 $databaseConnection = GeneralUtility::makeInstance(ConnectionPool::class)
78 ->getConnectionForTable('be_users');
79 try {
80 $databaseConnection->insert('be_users', $adminUserFields);
81 } catch (DBALException $exception) {
82 $errorStatus = GeneralUtility::makeInstance(ErrorStatus::class);
83 $errorStatus->setTitle('Administrator account not created!');
84 $errorStatus->setMessage(
85 'The administrator account could not be created. The following error occurred:' . LF .
86 $exception->getPrevious()->getMessage()
87 );
88 $result[] = $errorStatus;
89 return $result;
90 }
91
92 // Set password as install tool password
93 $configurationManager->setLocalConfigurationValueByPath('BE/installToolPassword', $this->getHashedPassword($password));
94
95 // Mark the initial import as done
96 $this->markImportDatabaseDone();
97
98 return $result;
99 }
100
101 /**
102 * Step needs to be executed if there are no tables in database
103 *
104 * @return bool
105 */
106 public function needsExecution()
107 {
108 $existingTables = GeneralUtility::makeInstance(ConnectionPool::class)
109 ->getConnectionByName('Default')
110 ->getSchemaManager()
111 ->listTableNames();
112 if (empty($existingTables)) {
113 $result = true;
114 } else {
115 $result = !$this->isImportDatabaseDone();
116 }
117 return $result;
118 }
119
120 /**
121 * Executes the step
122 *
123 * @return string Rendered content
124 */
125 protected function executeAction()
126 {
127 $this->assignSteps();
128 return $this->view->render();
129 }
130
131 /**
132 * Create tables and import static rows
133 *
134 * @return \TYPO3\CMS\Install\Status\StatusInterface[]
135 * @throws \Doctrine\DBAL\DBALException
136 * @throws \Doctrine\DBAL\Schema\SchemaException
137 * @throws \InvalidArgumentException
138 * @throws \RuntimeException
139 * @throws \TYPO3\CMS\Core\Database\Schema\Exception\StatementException
140 * @throws \TYPO3\CMS\Core\Database\Schema\Exception\UnexpectedSignalReturnValueTypeException
141 * @throws \TYPO3\CMS\Extbase\SignalSlot\Exception\InvalidSlotReturnException
142 * @throws \TYPO3\CMS\Extbase\SignalSlot\Exception\InvalidSlotException
143 */
144 protected function importDatabaseData()
145 {
146 // Will load ext_localconf and ext_tables. This is pretty safe here since we are
147 // in first install (database empty), so it is very likely that no extension is loaded
148 // that could trigger a fatal at this point.
149 $this->loadExtLocalconfDatabaseAndExtTables();
150
151 $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
152 $sqlCode = $sqlReader->getTablesDefinitionString(true);
153
154 $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
155 $createTableStatements = $sqlReader->getCreateTableStatementArray($sqlCode);
156
157 $results = $schemaMigrationService->install($createTableStatements);
158
159 // Only keep statements with error messages
160 $results = array_filter($results);
161 if (count($results) === 0) {
162 $insertStatements = $sqlReader->getInsertStatementArray($sqlCode);
163 $results = $schemaMigrationService->importStaticData($insertStatements);
164 }
165
166 foreach ($results as $statement => &$message) {
167 if ($message === '') {
168 unset($results[$statement]);
169 continue;
170 }
171
172 $errorStatus = GeneralUtility::makeInstance(ErrorStatus::class);
173 $errorStatus->setTitle('Database query failed!');
174 $errorStatus->setMessage(
175 'Query:' . LF .
176 ' ' . $statement . LF .
177 'Error:' . LF .
178 ' ' . $message
179 );
180 $message = $errorStatus;
181 }
182
183 return array_values($results);
184 }
185
186 /**
187 * Persist the information that the initial import has been performed
188 */
189 protected function markImportDatabaseDone()
190 {
191 GeneralUtility::makeInstance(ConfigurationManager::class)
192 ->setLocalConfigurationValueByPath('SYS/isInitialDatabaseImportDone', true);
193 }
194
195 /**
196 * Checks if the initial import has been performed
197 *
198 * @return bool
199 */
200 protected function isImportDatabaseDone()
201 {
202 return GeneralUtility::makeInstance(ConfigurationManager::class)
203 ->getConfigurationValueByPath('SYS/isInitialDatabaseImportDone');
204 }
205 }