TYPO3  7.6
UpgradeWizard.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Install\Controller\Action\Tool;
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 
18 use TYPO3\CMS\Install\Controller\Action;
21 
26 {
33 
39  protected function executeAction()
40  {
41  // ext_localconf, db and ext_tables must be loaded for the updates
43 
44  // To make sure initialUpdateDatabaseSchema is first wizard, it is added here instead of ext_localconf.php
45  $initialUpdateDatabaseSchemaUpdateObject = $this->getUpdateObjectInstance(\TYPO3\CMS\Install\Updates\InitialDatabaseSchemaUpdate::class, 'initialUpdateDatabaseSchema');
46  if ($initialUpdateDatabaseSchemaUpdateObject->shouldRenderWizard()) {
47  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'] = array_merge(
48  array('initialUpdateDatabaseSchema' => \TYPO3\CMS\Install\Updates\InitialDatabaseSchemaUpdate::class),
49  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']
50  );
51  $this->needsInitialUpdateDatabaseSchema = true;
52  }
53 
54  // To make sure finalUpdateDatabaseSchema is last wizard, it is added here instead of ext_localconf.php
55  $finalUpdateDatabaseSchemaUpdateObject = $this->getUpdateObjectInstance(\TYPO3\CMS\Install\Updates\FinalDatabaseSchemaUpdate::class, 'finalUpdateDatabaseSchema');
56  if ($finalUpdateDatabaseSchemaUpdateObject->shouldRenderWizard()) {
57  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['finalUpdateDatabaseSchema'] = \TYPO3\CMS\Install\Updates\FinalDatabaseSchemaUpdate::class;
58  }
59 
60  // Perform silent cache framework table upgrade
61  $this->silentCacheFrameworkTableSchemaMigration();
62 
63  $actionMessages = array();
64 
65  if (isset($this->postValues['set']['getUserInput'])) {
66  $actionMessages[] = $this->getUserInputForUpdate();
67  $this->view->assign('updateAction', 'getUserInput');
68  } elseif (isset($this->postValues['set']['performUpdate'])) {
69  $actionMessages[] = $this->performUpdate();
70  $this->view->assign('updateAction', 'performUpdate');
71  } else {
72  $actionMessages[] = $this->listUpdates();
73  $this->view->assign('updateAction', 'listUpdates');
74  }
75 
76  $this->view->assign('actionMessages', $actionMessages);
77 
78  return $this->view->render();
79  }
80 
86  protected function listUpdates()
87  {
88  if (empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'])) {
90  $message = $this->objectManager->get(\TYPO3\CMS\Install\Status\WarningStatus::class);
91  $message->setTitle('No update wizards registered');
92  return $message;
93  }
94 
95  $availableUpdates = array();
96  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'] as $identifier => $className) {
97  $updateObject = $this->getUpdateObjectInstance($className, $identifier);
98  if ($updateObject->shouldRenderWizard()) {
99  // $explanation is changed by reference in Update objects!
100  $explanation = '';
101  $updateObject->checkForUpdate($explanation);
102  $availableUpdates[$identifier] = array(
103  'identifier' => $identifier,
104  'title' => $updateObject->getTitle(),
105  'explanation' => $explanation,
106  'renderNext' => false,
107  );
108  if ($identifier === 'initialUpdateDatabaseSchema') {
109  $availableUpdates['initialUpdateDatabaseSchema']['renderNext'] = $this->needsInitialUpdateDatabaseSchema;
110  // initialUpdateDatabaseSchema is always the first update
111  // we stop immediately here as the remaining updates may
112  // require the new fields to be present in order to avoid SQL errors
113  break;
114  } elseif ($identifier === 'finalUpdateDatabaseSchema') {
115  // Okay to check here because finalUpdateDatabaseSchema is last element in array
116  $availableUpdates['finalUpdateDatabaseSchema']['renderNext'] = count($availableUpdates) === 1;
117  } elseif (!$this->needsInitialUpdateDatabaseSchema && $updateObject->shouldRenderNextButton()) {
118  // There are Updates that only show text and don't want to be executed
119  $availableUpdates[$identifier]['renderNext'] = true;
120  }
121  }
122  }
123 
124  $this->view->assign('availableUpdates', $availableUpdates);
125 
127  $message = $this->objectManager->get(\TYPO3\CMS\Install\Status\OkStatus::class);
128  $message->setTitle('Show available update wizards');
129  return $message;
130  }
131 
137  protected function getUserInputForUpdate()
138  {
139  $wizardIdentifier = $this->postValues['values']['identifier'];
140 
141  $className = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][$wizardIdentifier];
142  $updateObject = $this->getUpdateObjectInstance($className, $wizardIdentifier);
143  $wizardHtml = '';
144  if (method_exists($updateObject, 'getUserInput')) {
145  $wizardHtml = $updateObject->getUserInput('install[values][' . $wizardIdentifier . ']');
146  }
147 
148  $updateData = array(
149  'identifier' => $wizardIdentifier,
150  'title' => $updateObject->getTitle(),
151  'wizardHtml' => $wizardHtml,
152  );
153 
154  $this->view->assign('updateData', $updateData);
155 
157  $message = $this->objectManager->get(\TYPO3\CMS\Install\Status\OkStatus::class);
158  $message->setTitle('Show wizard options');
159  return $message;
160  }
161 
168  protected function performUpdate()
169  {
170  $this->getDatabaseConnection()->store_lastBuiltQuery = true;
171 
172  $wizardIdentifier = $this->postValues['values']['identifier'];
173  $className = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][$wizardIdentifier];
174  $updateObject = $this->getUpdateObjectInstance($className, $wizardIdentifier);
175 
176  $wizardData = array(
177  'identifier' => $wizardIdentifier,
178  'title' => $updateObject->getTitle(),
179  );
180 
181  // $wizardInputErrorMessage is given as reference to wizard object!
182  $wizardInputErrorMessage = '';
183  if (method_exists($updateObject, 'checkUserInput') && !$updateObject->checkUserInput($wizardInputErrorMessage)) {
185  $message = $this->objectManager->get(\TYPO3\CMS\Install\Status\ErrorStatus::class);
186  $message->setTitle('Input parameter broken');
187  $message->setMessage($wizardInputErrorMessage ?: 'Something went wrong!');
188  $wizardData['wizardInputBroken'] = true;
189  } else {
190  if (!method_exists($updateObject, 'performUpdate')) {
191  throw new \TYPO3\CMS\Install\Exception(
192  'No performUpdate method in update wizard with identifier ' . $wizardIdentifier,
193  1371035200
194  );
195  }
196 
197  // Both variables are used by reference in performUpdate()
198  $customOutput = '';
199  $databaseQueries = array();
200  $performResult = $updateObject->performUpdate($databaseQueries, $customOutput);
201 
202  if ($performResult) {
204  $message = $this->objectManager->get(\TYPO3\CMS\Install\Status\OkStatus::class);
205  $message->setTitle('Update successful');
206  } else {
208  $message = $this->objectManager->get(\TYPO3\CMS\Install\Status\ErrorStatus::class);
209  $message->setTitle('Update failed!');
210  if ($customOutput) {
211  $message->setMessage($customOutput);
212  }
213  }
214 
215  if ($this->postValues['values']['showDatabaseQueries'] == 1) {
216  $wizardData['queries'] = $databaseQueries;
217  }
218  }
219 
220  $this->view->assign('wizardData', $wizardData);
221 
222  $this->getDatabaseConnection()->store_lastBuiltQuery = false;
223 
224  // Next update wizard, if available
225  $nextUpdate = $this->getNextUpdateInstance($updateObject);
226  $nextUpdateIdentifier = '';
227  if ($nextUpdate) {
228  $nextUpdateIdentifier = $nextUpdate->getIdentifier();
229  }
230  $this->view->assign('nextUpdateIdentifier', $nextUpdateIdentifier);
231 
232  return $message;
233  }
234 
242  protected function getUpdateObjectInstance($className, $identifier)
243  {
244  $userInput = $this->postValues['values'][$identifier];
245  $versionAsInt = VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version);
246  return GeneralUtility::makeInstance($className, $identifier, $versionAsInt, $userInput, $this);
247  }
248 
256  protected function getNextUpdateInstance(AbstractUpdate $currentUpdate)
257  {
258  $isPreviousRecord = true;
259  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'] as $identifier => $className) {
260  // Find the current update wizard, and then start validating the next ones
261  if ($currentUpdate->getIdentifier() === $identifier) {
262  $isPreviousRecord = false;
263  // For the updateDatabaseSchema-wizards verify they do not have to be executed again
264  if ($identifier !== 'initialUpdateDatabaseSchema' && $identifier !== 'finalUpdateDatabaseSchema') {
265  continue;
266  }
267  }
268  if (!$isPreviousRecord) {
269  $nextUpdate = $this->getUpdateObjectInstance($className, $identifier);
270  if ($nextUpdate->shouldRenderWizard()) {
271  return $nextUpdate;
272  }
273  }
274  }
275  return null;
276  }
277 
284  protected function silentCacheFrameworkTableSchemaMigration()
285  {
287  $sqlHandler = $this->objectManager->get(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService::class);
288 
290  $cachingFrameworkDatabaseSchemaService = $this->objectManager->get(\TYPO3\CMS\Core\Cache\DatabaseSchemaService::class);
291  $expectedSchemaString = $cachingFrameworkDatabaseSchemaService->getCachingFrameworkRequiredDatabaseSchema();
292  $cleanedExpectedSchemaString = implode(LF, $sqlHandler->getStatementArray($expectedSchemaString, true, '^CREATE TABLE '));
293  $neededTableDefinition = $sqlHandler->getFieldDefinitions_fileContent($cleanedExpectedSchemaString);
294  $currentTableDefinition = $sqlHandler->getFieldDefinitions_database();
295  $updateTableDefinition = $sqlHandler->getDatabaseExtra($neededTableDefinition, $currentTableDefinition);
296  $updateStatements = $sqlHandler->getUpdateSuggestions($updateTableDefinition);
297  if (isset($updateStatements['create_table']) && !empty($updateStatements['create_table'])) {
298  $sqlHandler->performUpdateQueries($updateStatements['create_table'], $updateStatements['create_table']);
299  }
300  if (isset($updateStatements['add']) && !empty($updateStatements['add'])) {
301  $sqlHandler->performUpdateQueries($updateStatements['add'], $updateStatements['add']);
302  }
303  if (isset($updateStatements['change']) && !empty($updateStatements['change'])) {
304  $sqlHandler->performUpdateQueries($updateStatements['change'], $updateStatements['change']);
305  }
306  }
307 
315  protected function getDatabaseConnection()
316  {
317  return $GLOBALS['TYPO3_DB'];
318  }
319 }