[!!!][TASK] Rewrite install tool
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / Action / Tool / UpdateWizard.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 update wizards
32 */
33 class UpdateWizard 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 // ext_localconf, db and ext_tables must be loaded for the upgrade wizards
44 $this->loadExtLocalconfDatabaseAndExtTables();
45
46 // Perform silent cache framework table upgrades
47 $this->silentCacheFrameworkTableSchemaMigration();
48
49 $actionMessages = array();
50
51 if (isset($this->postValues['set']['getUserInput'])) {
52 $actionMessages[] = $this->getUserInputForUpdateWizard();
53 $this->view->assign('updateAction', 'getUserInput');
54 } elseif (isset($this->postValues['set']['performUpdate'])) {
55 $actionMessages[] = $this->performUpdate();
56 $this->view->assign('updateAction', 'performUpdate');
57 } else {
58 $actionMessages[] = $this->listUpdates();
59 $this->view->assign('updateAction', 'listUpdates');
60 }
61
62 $this->view->assign('actionMessages', $actionMessages);
63
64 return $this->view->render();
65 }
66
67 /**
68 * List of available updates
69 *
70 * @return \TYPO3\CMS\Install\Status\StatusInterface
71 */
72 protected function listUpdates() {
73 if (empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'])) {
74 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
75 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\WarningStatus');
76 $message->setTitle('No update wizards registered');
77 return $message;
78 }
79
80 $availableUpdates = array();
81 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'] as $identifier => $className) {
82 $updateObject = $this->getUpgradeObjectInstance($className, $identifier);
83 if ($updateObject->shouldRenderWizard()) {
84 // $explanation is changed by reference in upgrade objects!
85 $explanation = '';
86 $updateObject->checkForUpdate($explanation);
87 $availableUpdates[$identifier] = array(
88 'identifier' => $identifier,
89 'title' => $updateObject->getTitle(),
90 'explanation' => $explanation,
91 'renderNext' => FALSE,
92 );
93 // There are upgrade wizards that only show text and don't want to be executed
94 if ($updateObject->shouldRenderNextButton()) {
95 $availableUpdates[$identifier]['renderNext'] = TRUE;
96 }
97 }
98 }
99
100 $this->view->assign('availableUpdates', $availableUpdates);
101
102 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
103 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\OkStatus');
104 $message->setTitle('Show available update wizards');
105 return $message;
106 }
107
108 /**
109 * Get user input of update wizard
110 *
111 * @return \TYPO3\CMS\Install\Status\StatusInterface
112 */
113 protected function getUserInputForUpdateWizard() {
114 $wizardIdentifier = $this->postValues['values']['identifier'];
115
116 $className = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][$wizardIdentifier];
117 $updateObject = $this->getUpgradeObjectInstance($className, $wizardIdentifier);
118 $wizardHtml = '';
119 if (method_exists($updateObject, 'getUserInput')) {
120 $wizardHtml = $updateObject->getUserInput('install[values][wizardData]');
121 }
122
123 $updateWizardData = array(
124 'identifier' => $wizardIdentifier,
125 'title' => $updateObject->getTitle(),
126 'wizardHtml' => $wizardHtml,
127 );
128
129 $this->view->assign('updateWizardData', $updateWizardData);
130
131 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
132 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\OkStatus');
133 $message->setTitle('Show wizard options');
134 return $message;
135 }
136
137 /**
138 * Perform update of a specific wizard
139 *
140 * @throws \TYPO3\CMS\Install\Exception
141 * @return \TYPO3\CMS\Install\Status\StatusInterface
142 */
143 protected function performUpdate() {
144 $this->getDatabase()->store_lastBuiltQuery = TRUE;
145
146 $wizardIdentifier = $this->postValues['values']['identifier'];
147 $className = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][$wizardIdentifier];
148 $updateObject = $this->getUpgradeObjectInstance($className, $wizardIdentifier);
149
150 $wizardData = array(
151 'identifier' => $wizardIdentifier,
152 'title' => $updateObject->getTitle(),
153 );
154
155 // $wizardInputErrorMessage is given as reference to wizard object!
156 $wizardInputErrorMessage = '';
157 if (method_exists($updateObject, 'checkUserInput') && !$updateObject->checkUserInput($wizardInputErrorMessage)) {
158 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
159 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
160 $message->setTitle('Input parameter broken');
161 $message->setMessage($wizardInputErrorMessage ?: 'Something went wrong!');
162 $wizardData['wizardInputBroken'] = TRUE;
163 } else {
164 if (!method_exists($updateObject, 'performUpdate')) {
165 throw new \TYPO3\CMS\Install\Exception(
166 'No performUpdate method in update wizard with identifier ' . $wizardIdentifier,
167 1371035200
168 );
169 } else {
170 // Both variables are used by reference in performUpdate()
171 $customOutput = '';
172 $databaseQueries = array();
173 $performResult = $updateObject->performUpdate($databaseQueries, $customOutput);
174
175 if ($performResult) {
176 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
177 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\OkStatus');
178 $message->setTitle('Update successful');
179 } else {
180 /** @var $message \TYPO3\CMS\Install\Status\StatusInterface */
181 $message = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\ErrorStatus');
182 $message->setTitle('Update failed!');
183 if ($customOutput) {
184 $message->setMessage($customOutput);
185 }
186 }
187
188 if ($this->postValues['values']['showDatabaseQueries'] == 1) {
189 $wizardData['queries'] = $databaseQueries;
190 }
191 }
192 }
193
194 $this->view->assign('wizardData', $wizardData);
195
196 $this->getDatabase()->store_lastBuiltQuery = FALSE;
197
198 // Next update wizard, if available
199 $nextUpdateWizard = $this->getNextUpdateWizardInstance($updateObject);
200 $nextUpdateWizardIdentifier = '';
201 if ($nextUpdateWizard) {
202 $nextUpdateWizardIdentifier = $nextUpdateWizard->getIdentifier();
203 }
204 $this->view->assign('nextUpdateWizardIdentifier', $nextUpdateWizardIdentifier);
205
206 return $message;
207 }
208
209 /**
210 * Creates instance of an upgrade object, setting the pObj, versionNumber and pObj
211 *
212 * @param string $className The class name
213 * @param string $identifier The identifier of upgrade object - needed to fetch user input
214 * @return object Newly instantiated upgrade object
215 */
216 protected function getUpgradeObjectInstance($className, $identifier) {
217 $formValues = $this->postValues;
218 $updateObject = GeneralUtility::getUserObj($className);
219 $updateObject->setIdentifier($identifier);
220 $updateObject->versionNumber = \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version);
221 $updateObject->pObj = $this;
222 $updateObject->userInput = $formValues['update'][$identifier];
223 return $updateObject;
224 }
225
226 /**
227 * Returns the next upgrade wizard object
228 * Used to show the link/button to the next upgrade wizard
229 *
230 * @param object $currentObj Current update wizard object
231 * @return mixed Upgrade wizard instance or FALSE
232 */
233 protected function getNextUpdateWizardInstance($currentObj) {
234 $isPreviousRecord = TRUE;
235 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'] as $identifier => $className) {
236 // Find the current update wizard, and then start validating the next ones
237 if ($currentObj->getIdentifier() == $identifier) {
238 $isPreviousRecord = FALSE;
239 continue;
240 }
241 if (!$isPreviousRecord) {
242 $nextUpdateWizard = $this->getUpgradeObjectInstance($className, $identifier);
243 if ($nextUpdateWizard->shouldRenderWizard()) {
244 return $nextUpdateWizard;
245 }
246 }
247 }
248 return FALSE;
249 }
250
251 /**
252 * Force creation / update of caching framework tables that are needed by some update wizards
253 *
254 * @TODO: See also the other remarks on this topic in the abstract class, this whole area needs improvements
255 * @return void
256 */
257 protected function silentCacheFrameworkTableSchemaMigration() {
258 /** @var $sqlHandler \TYPO3\CMS\Install\Service\SqlSchemaMigrationService */
259 $sqlHandler = $this->objectManager->get('TYPO3\\CMS\\Install\\Service\\SqlSchemaMigrationService');
260
261 /** @var \TYPO3\CMS\Install\Service\SqlExpectedSchemaService $expectedSchemaService */
262 $expectedSchemaService = $this->objectManager->get('TYPO3\\CMS\\Install\\Service\\SqlExpectedSchemaService');
263 $expectedCachingFrameworkSchema = $expectedSchemaService->getExpectedDatabaseSchema();
264
265 // Forces creation / update of caching framework tables that are needed by some update wizards
266 $cacheTablesConfiguration = implode(
267 LF,
268 $sqlHandler->getStatementArray($expectedCachingFrameworkSchema, 1, '^CREATE TABLE ')
269 );
270 $neededTableDefinition = $sqlHandler->getFieldDefinitions_fileContent($cacheTablesConfiguration);
271 $currentTableDefinition = $sqlHandler->getFieldDefinitions_database();
272 $updateTableDefinition = $sqlHandler->getDatabaseExtra($neededTableDefinition, $currentTableDefinition);
273 $updateStatements = $sqlHandler->getUpdateSuggestions($updateTableDefinition);
274 if (isset($updateStatements['create_table']) && count($updateStatements['create_table']) > 0) {
275 $sqlHandler->performUpdateQueries($updateStatements['create_table'], $updateStatements['create_table']);
276 }
277 if (isset($updateStatements['add']) && count($updateStatements['add']) > 0) {
278 $sqlHandler->performUpdateQueries($updateStatements['add'], $updateStatements['add']);
279 }
280 if (isset($updateStatements['change']) && count($updateStatements['change']) > 0) {
281 $sqlHandler->performUpdateQueries($updateStatements['change'], $updateStatements['change']);
282 }
283 }
284
285 /**
286 * Overwrite getDatabase method of abstract!
287 *
288 * Returns $GLOBALS['TYPO3_DB'] directly, since this global is instantiated properly in update wizards
289 *
290 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
291 */
292 protected function getDatabase() {
293 return $GLOBALS['TYPO3_DB'];
294 }
295 }
296
297 ?>