From f07b60c5048d8cc15643539b1ee5273ddb811de6 Mon Sep 17 00:00:00 2001 From: Jan Helke <typo3@helke.de> Date: Sun, 10 Apr 2016 21:06:09 +0200 Subject: [PATCH] [TASK] Reintegrate TYPO3 7 LTS upgrade wizards As decided during the last RL Product Team meeting in Dusseldorf, the upgrade wizards from the install tool should stay for two generations of LTS versions. This reverts commit 021237d53a6cd74e384f3a9696cd9662c068d8f5. Releases: master Resolves: #75485 Related: #72367 Change-Id: I2f9d52a5bf0ffeca6843780354e349a5151ddb07 Reviewed-on: https://review.typo3.org/47556 Reviewed-by: Susanne Moog <typo3@susannemoog.de> Tested-by: Susanne Moog <typo3@susannemoog.de> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> --- .../SilentConfigurationUpgradeService.php | 18 -- .../Updates/AccessRightParametersUpdate.php | 91 ++++++ .../Updates/BackendUserStartModuleUpdate.php | 78 +++++ .../Compatibility6ExtractionUpdate.php | 122 ++++++++ .../Updates/ContentTypesToTextMediaUpdate.php | 159 +++++++++++ .../Updates/FileListIsStartModuleUpdate.php | 81 ++++++ .../Updates/FilesReplacePermissionUpdate.php | 122 ++++++++ .../Classes/Updates/LanguageIsoCodeUpdate.php | 85 ++++++ .../Updates/MediaceExtractionUpdate.php | 90 ++++++ .../MigrateMediaToAssetsForTextMediaCe.php | 99 +++++++ .../MigrateShortcutUrlsAgainUpdate.php | 91 ++++++ .../Updates/OpenidExtractionUpdate.php | 91 ++++++ .../Updates/PageShortcutParentUpdate.php | 86 ++++++ .../Updates/ProcessedFileChecksumUpdate.php | 143 ++++++++++ .../TableFlexFormToTtContentFieldsUpdate.php | 195 +++++++++++++ .../WorkspacesNotificationSettingsUpdate.php | 181 ++++++++++++ .../ContentTypesToTextMediaUpdateTest.php | 73 +++++ typo3/sysext/install/ext_localconf.php | 17 ++ .../Hook/Install/DeprecatedRteProperties.php | 270 ++++++++++++++++++ .../RteAcronymButtonRenamedToAbbreviation.php | 170 +++++++++++ typo3/sysext/rtehtmlarea/ext_localconf.php | 5 + 21 files changed, 2249 insertions(+), 18 deletions(-) create mode 100644 typo3/sysext/install/Classes/Updates/AccessRightParametersUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/BackendUserStartModuleUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/Compatibility6ExtractionUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/ContentTypesToTextMediaUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/FileListIsStartModuleUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/FilesReplacePermissionUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/LanguageIsoCodeUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/MediaceExtractionUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/MigrateMediaToAssetsForTextMediaCe.php create mode 100644 typo3/sysext/install/Classes/Updates/MigrateShortcutUrlsAgainUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/OpenidExtractionUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/PageShortcutParentUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/ProcessedFileChecksumUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/TableFlexFormToTtContentFieldsUpdate.php create mode 100644 typo3/sysext/install/Classes/Updates/WorkspacesNotificationSettingsUpdate.php create mode 100644 typo3/sysext/install/Tests/Unit/Updates/ContentTypesToTextMediaUpdateTest.php create mode 100644 typo3/sysext/rtehtmlarea/Classes/Hook/Install/DeprecatedRteProperties.php create mode 100644 typo3/sysext/rtehtmlarea/Classes/Hook/Install/RteAcronymButtonRenamedToAbbreviation.php diff --git a/typo3/sysext/install/Classes/Service/SilentConfigurationUpgradeService.php b/typo3/sysext/install/Classes/Service/SilentConfigurationUpgradeService.php index daf9bfdbc094..f80473990ecf 100755 --- a/typo3/sysext/install/Classes/Service/SilentConfigurationUpgradeService.php +++ b/typo3/sysext/install/Classes/Service/SilentConfigurationUpgradeService.php @@ -47,24 +47,6 @@ class SilentConfigurationUpgradeService * @var array */ protected $obsoleteLocalConfigurationSettings = array( - // #72367 - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\AccessRightParametersUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\BackendUserStartModuleUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\Compatibility6ExtractionUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\ContentTypesToTextMediaUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FileListIsStartModuleUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FilesReplacePermissionUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\LanguageIsoCodeUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\MediaceExtractionUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\MigrateMediaToAssetsForTextMediaCe', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\MigrateShortcutUrlsAgainUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\OpenidExtractionUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\PageShortcutParentUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\ProcessedFileChecksumUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\TableFlexFormToTtContentFieldsUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\WorkspacesNotificationSettingsUpdate', - 'INSTALL/wizardDone/TYPO3\\CMS\\Rtehtmlarea\\Hook\\Install\\DeprecatedRteProperties', - 'INSTALL/wizardDone/TYPO3\\CMS\\Rtehtmlarea\\Hook\\Install\\RteAcronymButtonRenamedToAbbreviation', // #72400 'BE/spriteIconGenerator_handler', // #72417 diff --git a/typo3/sysext/install/Classes/Updates/AccessRightParametersUpdate.php b/typo3/sysext/install/Classes/Updates/AccessRightParametersUpdate.php new file mode 100644 index 000000000000..3f7d9ba1f6d2 --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/AccessRightParametersUpdate.php @@ -0,0 +1,91 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Core\Configuration\ConfigurationManager; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +/** + * Move access right parameters from "BE" to "SYS" configuration section + */ +class AccessRightParametersUpdate extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Move access right parameters configuration to "SYS" section'; + + /** + * @var array + */ + protected $movedAccessRightConfigurationSettings = [ + 'BE/fileCreateMask' => 'SYS/fileCreateMask', + 'BE/folderCreateMask' => 'SYS/folderCreateMask', + 'BE/createGroup' => 'SYS/createGroup', + ]; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + $updateNeeded = false; + + /** @var ConfigurationManager $configurationManager */ + $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class); + + // If the local configuration path can be accessed, the path is valid and the update wizard has to be executed + foreach ($this->movedAccessRightConfigurationSettings as $oldPath => $newPath) { + try { + $configurationManager->getLocalConfigurationValueByPath($oldPath); + $updateNeeded = true; + break; + } catch (\RuntimeException $e) { + } + } + + $description = 'Some access right parameters were moved from the "BE" to the "SYS" configuration section. ' . + 'The update wizards moves the settings to the new configuration destination.'; + + return $updateNeeded; + } + + /** + * Performs the configuration update + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + /** @var ConfigurationManager $configurationManager */ + $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class); + foreach ($this->movedAccessRightConfigurationSettings as $oldPath => $newPath) { + try { + $value = $configurationManager->getLocalConfigurationValueByPath($oldPath); + $configurationManager->setLocalConfigurationValueByPath($newPath, $value); + } catch (\RuntimeException $e) { + } + } + $configurationManager->removeLocalConfigurationKeysByPath(array_keys($this->movedAccessRightConfigurationSettings)); + + $this->markWizardAsDone(); + return true; + } +} diff --git a/typo3/sysext/install/Classes/Updates/BackendUserStartModuleUpdate.php b/typo3/sysext/install/Classes/Updates/BackendUserStartModuleUpdate.php new file mode 100644 index 000000000000..4cda88c71e99 --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/BackendUserStartModuleUpdate.php @@ -0,0 +1,78 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * Update backend user setting startModule if set to "help_aboutmodules" + */ +class BackendUserStartModuleUpdate extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Update backend user setting "startModule"'; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + $backendUsersCount = $this->getDatabaseConnection()->exec_SELECTcountRows('uid', 'be_users'); + if ($this->isWizardDone() || $backendUsersCount === 0) { + return false; + } + + $description = 'The backend user setting startModule is changed for the extension aboutmodules. Update all backend users that use ext:aboutmodules as startModule.'; + + return true; + } + + /** + * Performs the database update if backend user's startmodule is help_aboutmodules + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $db = $this->getDatabaseConnection(); + $backendUsers = $db->exec_SELECTgetRows('uid,uc', 'be_users', '1=1'); + if (!empty($backendUsers)) { + foreach ($backendUsers as $backendUser) { + if ($backendUser['uc'] !== null) { + $userConfig = unserialize($backendUser['uc']); + if ($userConfig['startModule'] === 'help_aboutmodules') { + $userConfig['startModule'] = 'help_AboutmodulesAboutmodules'; + $db->exec_UPDATEquery( + 'be_users', + 'uid=' . (int)$backendUser['uid'], + array( + 'uc' => serialize($userConfig), + ) + ); + $databaseQueries[] = $db->debug_lastBuiltQuery; + } + } + } + } + + $this->markWizardAsDone(); + return true; + } +} diff --git a/typo3/sysext/install/Classes/Updates/Compatibility6ExtractionUpdate.php b/typo3/sysext/install/Classes/Updates/Compatibility6ExtractionUpdate.php new file mode 100644 index 000000000000..b18f1004d24e --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/Compatibility6ExtractionUpdate.php @@ -0,0 +1,122 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Core\Utility\GeneralUtility; + +/** + * Installs and downloads EXT:compatibility6 if needed + */ +class Compatibility6ExtractionUpdate extends AbstractDownloadExtensionUpdate +{ + /** + * @var string + */ + protected $title = 'Installs extension "compatibility6" from TER'; + + /** + * @var string + */ + protected $extensionKey = 'compatibility6'; + + /** + * @var array + */ + protected $extensionDetails = [ + 'compatibility6' => [ + 'title' => 'Compatibility Mode for TYPO3 CMS 6.x', + 'description' => 'Provides an additional backwards-compatibility layer with legacy functionality for sites that haven\'t fully migrated to TYPO3 CMS 7 yet.', + 'versionString' => '7.6.0', + ] + ]; + + /** + * Checks if an update is needed + * + * @param string $description The description for the update + * @return bool Whether an update is needed (true) or not (false) + */ + public function checkForUpdate(&$description) + { + $description = 'The extension "compatibility6" (Compatibility Mode for TYPO3 CMS 6.x) was extracted into ' + . 'the TYPO3 Extension Repository. This update downloads the TYPO3 Extension from the TER.'; + + $updateNeeded = false; + + if (!$this->isWizardDone()) { + $updateNeeded = true; + } + + return $updateNeeded; + } + + /** + * Second step: Ask user to install the extension + * + * @param string $inputPrefix input prefix, all names of form fields have to start with this. Append custom name in [ ... ] + * @return string HTML output + */ + public function getUserInput($inputPrefix) + { + return ' + <div class="panel panel-danger"> + <div class="panel-heading">Are you really sure?</div> + <div class="panel-body"> + <p>You should install EXT:compatibility6 only if you really need it.</p> + <p>This update wizard cannot check if the extension was installed before the update.</p> + <p>Are you really sure, you want to install EXT:compatibility6?</p> + <div class="btn-group clearfix" data-toggle="buttons"> + <label class="btn btn-default active"> + <input type="radio" name="' . $inputPrefix . '[install]" value="0" checked="checked" /> no, don\'t install + </label> + <label class="btn btn-default"> + <input type="radio" name="' . $inputPrefix . '[install]" value="1" /> yes, please install + </label> + </div> + </div> + </div> + '; + } + + /** + * Performs the update if EXT:compatibility6 should be installed. + * + * @param array $databaseQueries Queries done in this update + * @param mixed $customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $requestParams = GeneralUtility::_GP('install'); + if (!isset($requestParams['values']['compatibility6Extension']['install'])) { + return false; + } + $install = (int)$requestParams['values']['compatibility6Extension']['install']; + + if ($install === 1) { + // user decided to install extension, install and mark wizard as done + $updateSuccessful = $this->installExtension($this->extensionKey, $customMessages); + if ($updateSuccessful) { + $this->markWizardAsDone(); + return true; + } + } else { + // user decided to not install extension, mark wizard as done + $this->markWizardAsDone(); + return true; + } + return $updateSuccessful; + } +} diff --git a/typo3/sysext/install/Classes/Updates/ContentTypesToTextMediaUpdate.php b/typo3/sysext/install/Classes/Updates/ContentTypesToTextMediaUpdate.php new file mode 100644 index 000000000000..d0cfb6a3a8e9 --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/ContentTypesToTextMediaUpdate.php @@ -0,0 +1,159 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; + +/** + * Migrate CTypes 'text', 'image' and 'textpic' to 'textmedia' for extension 'frontend' + */ +class ContentTypesToTextMediaUpdate extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Migrate CTypes text, image and textpic to textmedia and move file relations from "image" to "asset_references"'; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + $updateNeeded = true; + + if ( + !ExtensionManagementUtility::isLoaded('fluid_styled_content') + || ExtensionManagementUtility::isLoaded('css_styled_content') + || $this->isWizardDone() + ) { + $updateNeeded = false; + } else { + $nonTextmediaCount = $this->getDatabaseConnection()->exec_SELECTcountRows( + 'uid', + 'tt_content', + 'CType IN (\'text\', \'image\', \'textpic\')' + ); + + if ($nonTextmediaCount === 0) { + $updateNeeded = false; + } + } + + $description = 'The extension "fluid_styled_content" is using a new CType, textmedia, ' . + 'which replaces the CTypes text, image and textpic. ' . + 'This update wizard migrates these old CTypes to the new one in the database. ' . + 'If backend groups have the explicit deny/allow flag set for any of the old CTypes, ' . + 'the according flag for the CType textmedia is set as well.'; + + return $updateNeeded; + } + + /** + * Performs the database update if old CTypes are available + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $databaseConnection = $this->getDatabaseConnection(); + $databaseConnection->store_lastBuiltQuery = true; + + // Update 'text' records + $databaseConnection->exec_UPDATEquery( + 'tt_content', + 'tt_content.CType=' . $databaseConnection->fullQuoteStr('text', 'tt_content'), + [ + 'CType' => 'textmedia', + ] + ); + + // Store last executed query + $databaseQueries[] = str_replace(chr(10), ' ', $databaseConnection->debug_lastBuiltQuery); + // Check for errors + if ($databaseConnection->sql_error()) { + $customMessages = 'SQL-ERROR: ' . htmlspecialchars($databaseConnection->sql_error()); + return false; + } + + // Update 'textpic' and 'image' records + $query = ' + UPDATE tt_content + LEFT JOIN sys_file_reference + ON sys_file_reference.uid_foreign=tt_content.uid + AND sys_file_reference.tablenames=' . $databaseConnection->fullQuoteStr('tt_content', 'sys_file_reference') + . ' AND sys_file_reference.fieldname=' . $databaseConnection->fullQuoteStr('image', 'sys_file_reference') + . ' SET tt_content.CType=' . $databaseConnection->fullQuoteStr('textmedia', 'tt_content') + . ', tt_content.assets=tt_content.image, + tt_content.image=0, + sys_file_reference.fieldname=' . $databaseConnection->fullQuoteStr('assets', 'tt_content') + . ' WHERE + tt_content.CType=' . $databaseConnection->fullQuoteStr('textpic', 'tt_content') + . ' OR tt_content.CType=' . $databaseConnection->fullQuoteStr('image', 'tt_content'); + $databaseConnection->sql_query($query); + + // Store last executed query + $databaseQueries[] = str_replace(chr(10), ' ', $query); + // Check for errors + if ($databaseConnection->sql_error()) { + $customMessages = 'SQL-ERROR: ' . htmlspecialchars($databaseConnection->sql_error()); + return false; + } + + // Update explicitDeny - ALLOW + $databaseConnection->exec_UPDATEquery( + 'be_groups', + '(explicit_allowdeny LIKE ' . $databaseConnection->fullQuoteStr('%' . $databaseConnection->escapeStrForLike('tt_content:CType:textpic:ALLOW', 'tt_content') . '%', 'tt_content') + . ' OR explicit_allowdeny LIKE ' . $databaseConnection->fullQuoteStr('%' . $databaseConnection->escapeStrForLike('tt_content:CType:image:ALLOW', 'tt_content') . '%', 'tt_content') + . ' OR explicit_allowdeny LIKE ' . $databaseConnection->fullQuoteStr('%' . $databaseConnection->escapeStrForLike('tt_content:CType:text:ALLOW', 'tt_content') . '%', 'tt_content') + . ') AND explicit_allowdeny NOT LIKE ' . $databaseConnection->fullQuoteStr('%' . $databaseConnection->escapeStrForLike('tt_content:CType:textmedia:ALLOW', 'tt_content') . '%', 'tt_content'), + [ + 'explicit_allowdeny' => 'CONCAT(explicit_allowdeny,' . $databaseConnection->fullQuoteStr(',tt_content:CType:textmedia:ALLOW', 'tt_content') . ')', + ], + [ + 'explicit_allowdeny', + ] + ); + + // Store last executed query + $databaseQueries[] = str_replace(chr(10), ' ', $databaseConnection->debug_lastBuiltQuery); + + // Update explicitDeny - DENY + $databaseConnection->exec_UPDATEquery( + 'be_groups', + '(explicit_allowdeny LIKE ' . $databaseConnection->fullQuoteStr('%' . $databaseConnection->escapeStrForLike('tt_content:CType:textpic:DENY', 'tt_content') . '%', 'tt_content') + . ' OR explicit_allowdeny LIKE ' . $databaseConnection->fullQuoteStr('%' . $databaseConnection->escapeStrForLike('tt_content:CType:image:DENY', 'tt_content') . '%', 'tt_content') + . ' OR explicit_allowdeny LIKE ' . $databaseConnection->fullQuoteStr('%' . $databaseConnection->escapeStrForLike('tt_content:CType:text:DENY', 'tt_content') . '%', 'tt_content') + . ') AND explicit_allowdeny NOT LIKE ' . $databaseConnection->fullQuoteStr('%' . $databaseConnection->escapeStrForLike('tt_content:CType:textmedia:DENY', 'tt_content') . '%', 'tt_content'), + [ + 'explicit_allowdeny' => 'CONCAT(explicit_allowdeny,' . $databaseConnection->fullQuoteStr(',tt_content:CType:textmedia:DENY', 'tt_content') . ')', + ], + [ + 'explicit_allowdeny', + ] + ); + + // Store last executed query + $databaseQueries[] = str_replace(chr(10), ' ', $databaseConnection->debug_lastBuiltQuery); + + $this->markWizardAsDone(); + + return true; + } +} diff --git a/typo3/sysext/install/Classes/Updates/FileListIsStartModuleUpdate.php b/typo3/sysext/install/Classes/Updates/FileListIsStartModuleUpdate.php new file mode 100644 index 000000000000..3480cae9cf5b --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/FileListIsStartModuleUpdate.php @@ -0,0 +1,81 @@ +<?php +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Install\Updates; + +/** + * Update backend user setting startModule if set to "file_list" + */ +class FileListIsStartModuleUpdate extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Update filelist user setting "startModule"'; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + if ($this->isWizardDone()) { + return false; + } + + if ($this->getDatabaseConnection()->exec_SELECTcountRows('uid', 'be_users') === 0) { + return false; + } + + $description = 'The backend user setting startModule is changed for the extension filelist. Update all backend users that use ext:filelist as startModule.'; + + return true; + } + + /** + * Performs the database update if backend user's startmodule is file_list + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $db = $this->getDatabaseConnection(); + $backendUsers = $db->exec_SELECTgetRows('uid,uc', 'be_users', '1=1'); + if (!empty($backendUsers)) { + foreach ($backendUsers as $backendUser) { + if ($backendUser['uc'] !== null) { + $userConfig = unserialize($backendUser['uc']); + if ($userConfig['startModule'] === 'file_list') { + $userConfig['startModule'] = 'file_FilelistList'; + $db->exec_UPDATEquery( + 'be_users', + 'uid=' . (int)$backendUser['uid'], + array( + 'uc' => serialize($userConfig), + ) + ); + $databaseQueries[] = $db->debug_lastBuiltQuery; + } + } + } + } + + $this->markWizardAsDone(); + return true; + } +} diff --git a/typo3/sysext/install/Classes/Updates/FilesReplacePermissionUpdate.php b/typo3/sysext/install/Classes/Updates/FilesReplacePermissionUpdate.php new file mode 100644 index 000000000000..4da08fe92898 --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/FilesReplacePermissionUpdate.php @@ -0,0 +1,122 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * Upgrade wizard which goes through all users and groups and set the "replaceFile" permission if "writeFile" is set + */ +class FilesReplacePermissionUpdate extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Set the "Files:replace" permission for all BE user/groups with "Files:write" set'; + + /** + * Checks whether updates are required. + * + * @param string &$description The description for the update + * @return bool Whether an update is required (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + if ($this->isWizardDone()) { + return false; + } + $description = 'A new file permission was introduced regarding replacing files.' . + ' This update sets "Files:replace" for all BE users/groups with the permission "Files:write".'; + $updateNeeded = false; + $db = $this->getDatabaseConnection(); + + // Fetch user records where the writeFile is set and replaceFile is not + $notMigratedRowsCount = $db->exec_SELECTcountRows( + 'uid', + 'be_users', + $this->getWhereClause() + ); + if ($notMigratedRowsCount > 0) { + $updateNeeded = true; + } + + if (!$updateNeeded) { + // Fetch group records where the writeFile is set and replaceFile is not + $notMigratedRowsCount = $db->exec_SELECTcountRows( + 'uid', + 'be_groups', + $this->getWhereClause() + ); + if ($notMigratedRowsCount > 0) { + $updateNeeded = true; + } + } + return $updateNeeded; + } + + /** + * Performs the accordant updates. + * + * @param array &$dbQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool Whether everything went smoothly or not + */ + public function performUpdate(array &$dbQueries, &$customMessages) + { + $db = $this->getDatabaseConnection(); + + // Iterate over users and groups table to perform permission updates + $tablesToProcess = ['be_groups', 'be_users']; + foreach ($tablesToProcess as $table) { + $records = $this->getRecordsFromTable($table); + foreach ($records as $singleRecord) { + $updateArray = [ + 'file_permissions' => $singleRecord['file_permissions'] . ',replaceFile' + ]; + $db->exec_UPDATEquery($table, 'uid=' . (int)$singleRecord['uid'], $updateArray); + // Get last executed query + $dbQueries[] = str_replace(chr(10), ' ', $db->debug_lastBuiltQuery); + // Check for errors + if ($db->sql_error()) { + $customMessages = 'SQL-ERROR: ' . htmlspecialchars($db->sql_error()); + return false; + } + } + } + $this->markWizardAsDone(); + return true; + } + + /** + * Retrieve every record which needs to be processed + * + * @param string $table + * @return array + */ + protected function getRecordsFromTable($table) + { + $fields = implode(',', array('uid', 'file_permissions')); + $records = $this->getDatabaseConnection()->exec_SELECTgetRows($fields, $table, $this->getWhereClause()); + return $records; + } + + /** + * Returns the where clause for database requests + * + * @return string + */ + protected function getWhereClause() + { + return 'file_permissions LIKE \'%writeFile%\' AND file_permissions LIKE \'%replaceFile%\''; + } +} diff --git a/typo3/sysext/install/Classes/Updates/LanguageIsoCodeUpdate.php b/typo3/sysext/install/Classes/Updates/LanguageIsoCodeUpdate.php new file mode 100644 index 000000000000..72910bcb6a3f --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/LanguageIsoCodeUpdate.php @@ -0,0 +1,85 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; + +/** + * Update sys_language records to use the newly created + * field language_isocode, if they have used the now deprecated + * static_lang_isocode + */ +class LanguageIsoCodeUpdate extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Update sys_language records to use new ISO 639-1 letter-code field'; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + if ($this->isWizardDone() || !ExtensionManagementUtility::isLoaded('static_info_tables')) { + return false; + } + + $emptyValue = $this->getDatabaseConnection()->fullQuoteStr('', 'sys_language'); + $migratableLanguageRecords = $this->getDatabaseConnection()->exec_SELECTcountRows('uid', 'sys_language', 'language_isocode=' . $emptyValue . ' AND CAST(static_lang_isocode AS CHAR) != ' . $emptyValue); + if ($migratableLanguageRecords === 0) { + return false; + } + + $description = 'The sys_language records have a new iso code field which removes the dependency of the TYPO3 CMS Core to the extension "static_info_tables". This upgrade wizard migrates the data of the existing "static_lang_isocode" field to the new DB field.'; + + return true; + } + + /** + * Performs the database update if the old field "static_lang_isocode" + * is in use and populates the new field "language_isocode" with the + * data of the old relation. + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $emptyValue = $this->getDatabaseConnection()->fullQuoteStr('', 'sys_language'); + $migrateableLanguageRecords = $this->getDatabaseConnection()->exec_SELECTgetRows('uid,static_lang_isocode', 'sys_language', 'language_isocode=' . $emptyValue . ' AND CAST(static_lang_isocode AS CHAR) != ' . $emptyValue); + if (!empty($migrateableLanguageRecords)) { + foreach ($migrateableLanguageRecords as $languageRecord) { + $staticLanguageRecord = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'static_languages', 'uid=' . (int)$languageRecord['static_lang_isocode']); + if (!empty($staticLanguageRecord['lg_iso_2'])) { + $this->getDatabaseConnection()->exec_UPDATEquery( + 'sys_language', + 'uid=' . (int)$languageRecord['uid'], + array( + 'language_isocode' => strtolower($staticLanguageRecord['lg_iso_2']) + ) + ); + $databaseQueries[] = $this->getDatabaseConnection()->debug_lastBuiltQuery; + } + } + } + + $this->markWizardAsDone(); + return true; + } +} diff --git a/typo3/sysext/install/Classes/Updates/MediaceExtractionUpdate.php b/typo3/sysext/install/Classes/Updates/MediaceExtractionUpdate.php new file mode 100644 index 000000000000..3f2bf36e2b38 --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/MediaceExtractionUpdate.php @@ -0,0 +1,90 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; + +/** + * Installs and downloads EXT:mediace if needed + */ +class MediaceExtractionUpdate extends AbstractDownloadExtensionUpdate +{ + /** + * @var string + */ + protected $title = 'Installs extension "mediace" from TER if media elements are used.'; + + /** + * @var string + */ + protected $extensionKey = 'mediace'; + + /** + * @var array + */ + protected $extensionDetails = [ + 'mediace' => [ + 'title' => 'Media Content Element', + 'description' => 'The media functionality from TYPO3 6.2 and earlier can be found here. This extension provides ContentObjects and Content Elements.', + 'versionString' => '7.6.0', + ] + ]; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + $updateNeeded = true; + + if ($this->isWizardDone() || ExtensionManagementUtility::isLoaded('mediace')) { + $updateNeeded = false; + } else { + $amountOfMediaElements = $this->getDatabaseConnection()->exec_SELECTcountRows( + 'uid', + 'tt_content', + 'CType IN (\'media\', \'multimedia\') AND deleted=0' + ); + + if ($amountOfMediaElements === 0) { + $updateNeeded = false; + } + } + + $description = 'The extension "mediace" (Media Content Element) was extracted into the TYPO3 Extension Repository. ' . + 'This update checks if media content elements are used and downloads the TYPO3 Extension from the TER.'; + + return $updateNeeded; + } + + /** + * Performs the database update if media CTypes are available. + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $updateSuccessful = $this->installExtension($this->extensionKey, $customMessages); + if ($updateSuccessful) { + $this->markWizardAsDone(); + } + return true; + } +} diff --git a/typo3/sysext/install/Classes/Updates/MigrateMediaToAssetsForTextMediaCe.php b/typo3/sysext/install/Classes/Updates/MigrateMediaToAssetsForTextMediaCe.php new file mode 100644 index 000000000000..d38927943249 --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/MigrateMediaToAssetsForTextMediaCe.php @@ -0,0 +1,99 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * Migrate CTypes 'textmedia' to use 'assets' field instead of 'media' + */ +class MigrateMediaToAssetsForTextMediaCe extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Migrate CTypes textmedia database field "media" to "assets"'; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + $updateNeeded = true; + + if ($this->isWizardDone()) { + $updateNeeded = false; + } else { + // No need to join the sys_file_references table here as we can rely on the reference + // counter to check if the wizards has any textmedia content elements to upgrade. + $textmediaCount = $this->getDatabaseConnection()->exec_SELECTcountRows( + 'uid', + 'tt_content', + 'CType = \'textmedia\' AND media > 0' + ); + + if ($textmediaCount === 0) { + $updateNeeded = false; + $this->markWizardAsDone(); + } + } + + $description = 'The extension "fluid_styled_content" is using a new database field for mediafile references. ' . + 'This update wizard migrates these old references to use the new database field.'; + + return $updateNeeded; + } + + /** + * Performs the database update if old mediafile references are available + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $databaseConnection = $this->getDatabaseConnection(); + + // Update 'textmedia' + $query = ' + UPDATE sys_file_reference + LEFT JOIN tt_content + ON sys_file_reference.uid_foreign = tt_content.uid + AND sys_file_reference.tablenames =\'tt_content\' + AND sys_file_reference.fieldname = \'media\' + SET tt_content.assets = tt_content.media, + tt_content.media = 0, + sys_file_reference.fieldname = \'assets\' + WHERE + tt_content.CType = \'textmedia\' + AND tt_content.media > 0 + '; + $databaseConnection->sql_query($query); + + // Store last executed query + $databaseQueries[] = str_replace(chr(10), ' ', $query); + // Check for errors + if ($databaseConnection->sql_error()) { + $customMessages = 'SQL-ERROR: ' . htmlspecialchars($databaseConnection->sql_error()); + return false; + } + + $this->markWizardAsDone(); + + return true; + } +} diff --git a/typo3/sysext/install/Classes/Updates/MigrateShortcutUrlsAgainUpdate.php b/typo3/sysext/install/Classes/Updates/MigrateShortcutUrlsAgainUpdate.php new file mode 100644 index 000000000000..f4f1c501a3bf --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/MigrateShortcutUrlsAgainUpdate.php @@ -0,0 +1,91 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * Migrate backend shortcut urls + */ +class MigrateShortcutUrlsAgainUpdate extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Migrate backend shortcut urls'; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + $shortcutsCount = $this->getDatabaseConnection()->exec_SELECTcountRows('uid', 'sys_be_shortcuts'); + if ($this->isWizardDone() || $shortcutsCount === 0) { + return false; + } + + $description = 'Migrate old shortcut urls to the new module urls.'; + + return true; + } + + /** + * Performs the database update if shortcuts are available + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $db = $this->getDatabaseConnection(); + $shortcuts = $db->exec_SELECTgetRows('uid,url', 'sys_be_shortcuts', '1=1'); + if (!empty($shortcuts)) { + foreach ($shortcuts as $shortcut) { + $decodedUrl = urldecode($shortcut['url']); + $encodedUrl = str_replace( + array( + '/typo3/sysext/cms/layout/db_layout.php?&', + '/typo3/sysext/cms/layout/db_layout.php?', + '/typo3/file_edit.php?&', + // From 7.2 to 7.4 + 'mod.php', + ), + array( + '/typo3/index.php?&M=web_layout&', + urlencode('/typo3/index.php?&M=web_layout&'), + '/typo3/index.php?&M=file_edit&', + // From 7.2 to 7.4 + 'index.php', + ), + $decodedUrl + ); + + $db->exec_UPDATEquery( + 'sys_be_shortcuts', + 'uid=' . (int)$shortcut['uid'], + array( + 'url' => $encodedUrl, + ) + ); + $databaseQueries[] = $db->debug_lastBuiltQuery; + } + } + + $this->markWizardAsDone(); + return true; + } +} diff --git a/typo3/sysext/install/Classes/Updates/OpenidExtractionUpdate.php b/typo3/sysext/install/Classes/Updates/OpenidExtractionUpdate.php new file mode 100644 index 000000000000..be4fe27aecdd --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/OpenidExtractionUpdate.php @@ -0,0 +1,91 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * Installs and downloads EXT:openid if needed + */ +class OpenidExtractionUpdate extends AbstractDownloadExtensionUpdate +{ + /** + * @var string + */ + protected $title = 'Installs extension "openid" from TER if openid is used.'; + + /** + * @var string + */ + protected $extensionKey = 'openid'; + + /** + * @var array + */ + protected $extensionDetails = [ + 'openid' => [ + 'title' => 'OpenID authentication', + 'description' => 'Adds OpenID authentication to TYPO3', + 'versionString' => '7.6.0', + ] + ]; + + /** + * Checks if an update is needed + * + * @param string $description The description for the update + * @return bool Whether an update is needed (true) or not (false) + */ + public function checkForUpdate(&$description) + { + $updateNeeded = false; + + if (!$this->isWizardDone()) { + $columnsExists = false; + + $columns = $this->getDatabaseConnection()->admin_get_fields('fe_users'); + if (isset($columns['tx_openid_openid'])) { + $columnsExists = true; + } + $columns = $this->getDatabaseConnection()->admin_get_fields('be_users'); + if (isset($columns['tx_openid_openid'])) { + $columnsExists = true; + } + if ($columnsExists) { + $updateNeeded = true; + } + } + + $description = 'The extension "openid" (OpenID authentication) was extracted into ' + . 'the TYPO3 Extension Repository. This update checks if openid id used and ' + . 'downloads the TYPO3 Extension from the TER.'; + + return $updateNeeded; + } + + /** + * Performs the update if EXT:openid is used. + * + * @param array $databaseQueries Queries done in this update + * @param mixed $customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $updateSuccessful = $this->installExtension($this->extensionKey, $customMessages); + if ($updateSuccessful) { + $this->markWizardAsDone(); + } + return $updateSuccessful; + } +} diff --git a/typo3/sysext/install/Classes/Updates/PageShortcutParentUpdate.php b/typo3/sysext/install/Classes/Updates/PageShortcutParentUpdate.php new file mode 100644 index 000000000000..650b6ccc1a5d --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/PageShortcutParentUpdate.php @@ -0,0 +1,86 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Frontend\Page\PageRepository; + +/** + * Update all pages which have set the shortcut mode "Parent of selected or current page" (PageRepository::SHORTCUT_MODE_PARENT_PAGE) + * to remove a possibly selected page as this would cause a different behaviour of the shortcut now + * since the selected page is now respected in this shortcut mode. + */ +class PageShortcutParentUpdate extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Update page shortcuts with shortcut type "Parent of selected or current page"'; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + if ($this->isWizardDone()) { + return false; + } + + $pagesNeedingUpdate = $this->getUpdatablePages(); + if (empty($pagesNeedingUpdate)) { + return false; + } + + $description = 'There are some shortcut pages that need to updated in order to preserve their current behaviour.'; + + return true; + } + + /** + * Get pages which need to be updated + * + * @return array|NULL + */ + protected function getUpdatablePages() + { + return $this->getDatabaseConnection()->exec_SELECTgetRows('uid', 'pages', 'shortcut <> 0 AND shortcut_mode = ' . PageRepository::SHORTCUT_MODE_PARENT_PAGE); + } + + /** + * Performs the database update + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $pagesNeedingUpdate = $this->getUpdatablePages(); + if (!empty($pagesNeedingUpdate)) { + $uids = array_column($pagesNeedingUpdate, 'uid'); + $this->getDatabaseConnection()->exec_UPDATEquery( + 'pages', + 'uid IN (' . implode(',', $uids) . ')', + [ 'shortcut' => 0 ] + ); + $databaseQueries[] = $this->getDatabaseConnection()->debug_lastBuiltQuery; + } + + $this->markWizardAsDone(); + return true; + } +} diff --git a/typo3/sysext/install/Classes/Updates/ProcessedFileChecksumUpdate.php b/typo3/sysext/install/Classes/Updates/ProcessedFileChecksumUpdate.php new file mode 100644 index 000000000000..214ea9f07934 --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/ProcessedFileChecksumUpdate.php @@ -0,0 +1,143 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Core\Resource\ProcessedFile; +use TYPO3\CMS\Core\Resource\ResourceFactory; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +/** + * Updates the checksum of sys_file_processedfile records to avoid regeneration of the thumbnails + */ +class ProcessedFileChecksumUpdate extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = '[Optional] Update sys_file_processedfile records to match new checksum calculation.'; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + if ($this->isWizardDone()) { + return false; + } + + $join = 'sys_file_processedfile LEFT JOIN sys_registry ON entry_key = CAST(sys_file_processedfile.uid AS CHAR) AND entry_namespace = \'ProcessedFileChecksumUpdate\''; + $count = $this->getDatabaseConnection()->exec_SELECTcountRows('*', $join, '(entry_key IS NULL AND sys_file_processedfile.identifier <> \'\') OR sys_file_processedfile.width IS NULL'); + if (!$count) { + return false; + } + + $description = 'The checksum calculation for processed files (image thumbnails) has been changed with TYPO3 CMS 7.3 and 6.2.13. +This means that your processed files need to be updated, if you update from versions <strong>below TYPO3 CMS 7.3 or 6.2.13</strong>.<br /> +This can either happen on demand, when the processed file is first needed, or by executing this wizard, which updates all processed images at once.<br /> +<strong>Important:</strong> If you have lots of processed files, you should prefer using this wizard, otherwise this might cause a lot of work for your server.'; + + return true; + } + + /** + * Performs the update + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $db = $this->getDatabaseConnection(); + + // remove all invalid records which hold NULL values + $db->exec_DELETEquery('sys_file_processedfile', 'width IS NULL or height IS NULL'); + + $factory = GeneralUtility::makeInstance(ResourceFactory::class); + + $join = 'sys_file_processedfile LEFT JOIN sys_registry ON entry_key = CAST(sys_file_processedfile.uid AS CHAR) AND entry_namespace = \'ProcessedFileChecksumUpdate\''; + $res = $db->exec_SELECTquery('sys_file_processedfile.*', $join, 'entry_key IS NULL AND sys_file_processedfile.identifier <> \'\''); + while ($processedFileRow = $db->sql_fetch_assoc($res)) { + try { + $storage = $factory->getStorageObject($processedFileRow['storage']); + } catch (\InvalidArgumentException $e) { + $storage = null; + } + if (!$storage) { + // invalid storage, delete record, we can't take care of the associated file + $db->exec_DELETEquery('sys_file_processedfile', 'uid=' . $processedFileRow['uid']); + continue; + } + + if ($storage->getDriverType() !== 'Local') { + // non-local storage, we can't treat this, skip the record and mark it done + $db->exec_INSERTquery('sys_registry', array('entry_namespace' => 'ProcessedFileChecksumUpdate', 'entry_key' => $processedFileRow['uid'])); + continue; + } + + $configuration = $storage->getConfiguration(); + if ($configuration['pathType'] === 'relative') { + $absoluteBasePath = PATH_site . $configuration['basePath']; + } else { + $absoluteBasePath = $configuration['basePath']; + } + $filePath = rtrim($absoluteBasePath, '/') . '/' . ltrim($processedFileRow['identifier'], '/'); + + try { + $originalFile = $factory->getFileObject($processedFileRow['original']); + } catch (\Exception $e) { + // no original file there anymore, delete local file + @unlink($filePath); + $db->exec_DELETEquery('sys_file_processedfile', 'uid=' . $processedFileRow['uid']); + continue; + } + + $processedFileObject = new ProcessedFile($originalFile, '', array(), $processedFileRow); + + // calculate new checksum and name + $newChecksum = $processedFileObject->calculateChecksum(); + + // if the checksum already matches, there is nothing to do + if ($newChecksum !== $processedFileRow['checksum']) { + $newName = str_replace($processedFileRow['checksum'], $newChecksum, $processedFileRow['name']); + $newIdentifier = str_replace($processedFileRow['checksum'], $newChecksum, $processedFileRow['identifier']); + $newFilePath = str_replace($processedFileRow['checksum'], $newChecksum, $filePath); + + // rename file + if (@rename($filePath, $newFilePath)) { + // save result back into database + $fields = array( + 'tstamp' => time(), + 'identifier' => $newIdentifier, + 'name' => $newName, + 'checksum' => $newChecksum + ); + $db->exec_UPDATEquery('sys_file_processedfile', 'uid=' . $processedFileRow['uid'], $fields); + } + // if the rename of the file failed, keep the record, but do not bother with it again + } + + // remember we finished this record + $db->exec_INSERTquery('sys_registry', array('entry_namespace' => 'ProcessedFileChecksumUpdate', 'entry_key' => $processedFileRow['uid'])); + } + + $db->exec_DELETEquery('sys_registry', 'entry_namespace = \'ProcessedFileChecksumUpdate\''); + $this->markWizardAsDone(); + return true; + } +} diff --git a/typo3/sysext/install/Classes/Updates/TableFlexFormToTtContentFieldsUpdate.php b/typo3/sysext/install/Classes/Updates/TableFlexFormToTtContentFieldsUpdate.php new file mode 100644 index 000000000000..4cf904ee5478 --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/TableFlexFormToTtContentFieldsUpdate.php @@ -0,0 +1,195 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +/** + * Migrate the Flexform for CType 'table' to regular fields in tt_content + */ +class TableFlexFormToTtContentFieldsUpdate extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Migrate the Flexform for CType "table" to regular fields in tt_content'; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + $flexFormCount = $this->getDatabaseConnection()->exec_SELECTcountRows( + 'uid', + 'tt_content', + 'CType=\'table\' AND pi_flexform IS NOT NULL AND deleted = 0' + ); + + if ( + $this->isWizardDone() || $flexFormCount === 0 + || ExtensionManagementUtility::isLoaded('css_styled_content') + ) { + return false; + } + + $description = 'The extension "frontend" uses regular database fields in the tt_content table ' . + 'for the CType "table". Before this was a FlexForm.<br /><br />' . + 'This update wizard migrates these FlexForms to regular database fields.'; + + return true; + } + + /** + * Performs the database update if CType 'table' still has content in pi_flexform + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $databaseConnection = $this->getDatabaseConnection(); + + $databaseResult = $databaseConnection->exec_SELECTquery( + 'uid, pi_flexform', + 'tt_content', + 'CType=\'table\' AND pi_flexform IS NOT NULL AND deleted = 0' + ); + + while ($tableRecord = $databaseConnection->sql_fetch_assoc($databaseResult)) { + $flexForm = $this->initializeFlexForm($tableRecord['pi_flexform']); + + if (is_array($flexForm)) { + $fields = $this->mapFieldsFromFlexForm($flexForm); + + // Set pi_flexform to NULL + $fields['pi_flexform'] = null; + + $databaseConnection->exec_UPDATEquery( + 'tt_content', + 'uid=' . (int)$tableRecord['uid'], + $fields + ); + + $databaseQueries[] = $databaseConnection->debug_lastBuiltQuery; + } + } + + $databaseConnection->sql_free_result($databaseResult); + + $this->markWizardAsDone(); + + return true; + } + + /** + * Map the old FlexForm values to the new database fields + * and fill them with the proper data + * + * @param array $flexForm The content of the FlexForm + * @return array The fields which need to be updated in the tt_content table + */ + protected function mapFieldsFromFlexForm($flexForm) + { + $fields = array(); + + $mapping = array( + 'table_caption' => array( + 'sheet' => 'sDEF', + 'fieldName' => 'acctables_caption', + 'default' => '', + 'values' => 'passthrough' + ), + 'table_delimiter' => array( + 'sheet' => 's_parsing', + 'fieldName' => 'tableparsing_delimiter', + 'default' => 124, + 'values' => 'passthrough' + ), + 'table_enclosure' => array( + 'sheet' => 's_parsing', + 'fieldName' => 'tableparsing_quote', + 'default' => 0, + 'values' => 'passthrough' + ), + 'table_header_position' => array( + 'sheet' => 'sDEF', + 'fieldName' => 'acctables_headerpos', + 'default' => 0, + 'values' => array( + 'top' => 1, + 'left' => 2 + ) + ), + 'table_tfoot' => array( + 'sheet' => 'sDEF', + 'fieldName' => 'acctables_tfoot', + 'default' => 0, + 'values' => 'passthrough' + ) + ); + + foreach ($mapping as $fieldName => $configuration) { + $flexFormValue = $this->getFlexFormValue($flexForm, $configuration['fieldName'], $configuration['sheet']); + + if ($flexFormValue !== '') { + if ($configuration['values'] === 'passthrough') { + $fields[$fieldName] = $flexFormValue; + } elseif (is_array($configuration['values'])) { + $fields[$fieldName] = $configuration['values'][$flexFormValue]; + } + } else { + $fields[$fieldName] = $configuration['default']; + } + } + + return $fields; + } + + /** + * Convert the XML of the FlexForm to an array + * + * @param string|NULL $flexFormXml The XML of the FlexForm + * @return array|NULL Converted XML to array + */ + protected function initializeFlexForm($flexFormXml) + { + $flexForm = null; + + if ($flexFormXml) { + $flexForm = GeneralUtility::xml2array($flexFormXml); + if (!is_array($flexForm)) { + $flexForm = null; + } + } + + return $flexForm; + } + + /** + * @param array $flexForm The content of the FlexForm + * @param string $fieldName The field name to get the value for + * @param string $sheet The sheet on which this value is located + * @return string The value + */ + protected function getFlexFormValue(array $flexForm, $fieldName, $sheet = 'sDEF') + { + return $flexForm['data'][$sheet]['lDEF'][$fieldName]['vDEF']; + } +} diff --git a/typo3/sysext/install/Classes/Updates/WorkspacesNotificationSettingsUpdate.php b/typo3/sysext/install/Classes/Updates/WorkspacesNotificationSettingsUpdate.php new file mode 100644 index 000000000000..cc764005e340 --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/WorkspacesNotificationSettingsUpdate.php @@ -0,0 +1,181 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; + +/** + * Migrate the workspaces notification settings to the enhanced schema. + */ +class WorkspacesNotificationSettingsUpdate extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Migrate the workspaces notification settings to the enhanced schema'; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) + { + if (!ExtensionManagementUtility::isLoaded('workspaces')) { + return false; + } + + if ($this->isWizardDone()) { + return false; + } + + $workspacesCount = $this->getDatabaseConnection()->exec_SELECTcountRows( + 'uid', + 'sys_workspace', + 'deleted=0' + ); + + $stagesCount = $this->getDatabaseConnection()->exec_SELECTcountRows( + 'uid', + 'sys_workspace_stage', + 'deleted=0' + ); + + if ($workspacesCount + $stagesCount > 0) { + $description = 'The workspaces notification settings have been extended' + . ' and need to be migrated to the new definitions. This update wizard' + . ' upgrades the accordant settings in the availble workspaces and stages.'; + return true; + } else { + $this->markWizardAsDone(); + } + + return false; + } + + /** + * Perform the database updates for workspace records + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) + { + $databaseConnection = $this->getDatabaseConnection(); + + $workspaceRecords = $databaseConnection->exec_SELECTgetRows('*', 'sys_workspace', 'deleted=0'); + foreach ($workspaceRecords as $workspaceRecord) { + $update = $this->prepareWorkspaceUpdate($workspaceRecord); + if ($update !== null) { + $databaseConnection->exec_UPDATEquery('sys_workspace', 'uid=' . (int)$workspaceRecord['uid'], $update); + $databaseQueries[] = $databaseConnection->debug_lastBuiltQuery; + } + } + + $stageRecords = $databaseConnection->exec_SELECTgetRows('*', 'sys_workspace_stage', 'deleted=0'); + foreach ($stageRecords as $stageRecord) { + $update = $this->prepareStageUpdate($stageRecord); + if ($update !== null) { + $databaseConnection->exec_UPDATEquery('sys_workspace_stage', 'uid=' . (int)$stageRecord['uid'], $update); + $databaseQueries[] = $databaseConnection->debug_lastBuiltQuery; + } + } + + $this->markWizardAsDone(); + return true; + } + + /** + * Prepares SQL updates for workspace records. + * + * @param array $workspaceRecord + * @return array|NULL + */ + protected function prepareWorkspaceUpdate(array $workspaceRecord) + { + if (empty($workspaceRecord['uid'])) { + return null; + } + + $update = array(); + $update = $this->mapSettings($workspaceRecord, $update, 'edit', 'edit'); + $update = $this->mapSettings($workspaceRecord, $update, 'publish', 'publish'); + $update = $this->mapSettings($workspaceRecord, $update, 'publish', 'execute'); + return $update; + } + + /** + * Prepares SQL update for stage records. + * + * @param array $stageRecord + * @return array|null + */ + protected function prepareStageUpdate(array $stageRecord) + { + if (empty($stageRecord['uid'])) { + return null; + } + + $update = array(); + $update = $this->mapSettings($stageRecord, $update); + return $update; + } + + /** + * Maps settings to new meaning. + * + * @param array $record + * @param array $update + * @param string $from + * @param string $to + * @return array + */ + protected function mapSettings(array $record, array $update, $from = '', $to = '') + { + $fromPrefix = ($from ? $from . '_' : ''); + $toPrefix = ($to ? $to . '_' : ''); + + $settings = 0; + // Previous setting: "Allow notification settings during stage change" + if ($record[$fromPrefix . 'allow_notificaton_settings']) { + $settings += 1; + } + // Previous setting: "All are selected per default (can be changed)" + if ((int)$record[$fromPrefix . 'notification_mode'] === 0) { + $settings += 2; + } + + // Custom stages: preselect responsible persons (8) + if (isset($record['responsible_persons'])) { + $preselection = 8; + // Workspace "edit" stage: preselect members (2) + } elseif ($to === 'edit') { + $preselection = 2; + // Workspace "publish" stage: preselect owners (1) + } elseif ($to === 'publish') { + $preselection = 1; + // Workspace "execute" stage: preselect owners (1) and members (2) as default + } else { + $preselection = 1 + 2; + } + + $update[$toPrefix . 'allow_notificaton_settings'] = $settings; + $update[$toPrefix . 'notification_preselection'] = $preselection; + + return $update; + } +} diff --git a/typo3/sysext/install/Tests/Unit/Updates/ContentTypesToTextMediaUpdateTest.php b/typo3/sysext/install/Tests/Unit/Updates/ContentTypesToTextMediaUpdateTest.php new file mode 100644 index 000000000000..a9ae7b15dfab --- /dev/null +++ b/typo3/sysext/install/Tests/Unit/Updates/ContentTypesToTextMediaUpdateTest.php @@ -0,0 +1,73 @@ +<?php +namespace TYPO3\CMS\Install\Tests\Unit\Updates; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use Prophecy\Argument; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophet; +use TYPO3\CMS\Core\Package\PackageManager; +use TYPO3\CMS\Core\Tests\Unit\Resource\BaseTestCase; +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; +use TYPO3\CMS\Install\Updates\ContentTypesToTextMediaUpdate as UpdateWizard; + +/** + * Test Class for ContentTypesToTextMediaUpdate + */ +class ContentTypesToTextMediaUpdateTest extends BaseTestCase +{ + /** + * @var PackageManager|ObjectProphecy + */ + protected $packageManagerProphecy; + + /** + * @var ObjectProphecy + */ + protected $dbProphecy; + + /** + * @var ObjectProphecy + */ + protected $updateWizard; + + public function setUp() + { + unset($GLOBALS['TYPO3_CONF_VARS']['INSTALL']['wizardDone']); + $prophet = new Prophet(); + $this->packageManagerProphecy = $prophet->prophesize(PackageManager::class); + $this->dbProphecy = $prophet->prophesize(\TYPO3\CMS\Core\Database\DatabaseConnection::class); + $GLOBALS['TYPO3_DB'] = $this->dbProphecy->reveal(); + $this->updateWizard = new UpdateWizard(); + ExtensionManagementUtility::setPackageManager($this->packageManagerProphecy->reveal()); + } + + public function tearDown() + { + ExtensionManagementUtility::setPackageManager(new PackageManager()); + } + + /** + * @test + * @return void + */ + public function updateWizardDoesNotRunIfCssStyledContentIsInstalled() + { + $this->packageManagerProphecy->isPackageActive('fluid_styled_content')->willReturn(true); + $this->packageManagerProphecy->isPackageActive('css_styled_content')->willReturn(true); + + $description = ''; + $this->assertFalse($this->updateWizard->checkForUpdate($description)); + } +} diff --git a/typo3/sysext/install/ext_localconf.php b/typo3/sysext/install/ext_localconf.php index abe1dcc800b8..fb6c325c7fc3 100644 --- a/typo3/sysext/install/ext_localconf.php +++ b/typo3/sysext/install/ext_localconf.php @@ -1,6 +1,23 @@ <?php defined('TYPO3_MODE') or die(); +// TYPO3 CMS 7 +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['accessRightParameters'] = \TYPO3\CMS\Install\Updates\AccessRightParametersUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['backendUserStartModule'] = \TYPO3\CMS\Install\Updates\BackendUserStartModuleUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['languageIsoCode'] = \TYPO3\CMS\Install\Updates\LanguageIsoCodeUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['PageShortcutParent'] = \TYPO3\CMS\Install\Updates\PageShortcutParentUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['backendShortcuts'] = \TYPO3\CMS\Install\Updates\MigrateShortcutUrlsAgainUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['processedFilesChecksum'] = \TYPO3\CMS\Install\Updates\ProcessedFileChecksumUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['filesReplacePermission'] = \TYPO3\CMS\Install\Updates\FilesReplacePermissionUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['tableCType'] = \TYPO3\CMS\Install\Updates\TableFlexFormToTtContentFieldsUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][\TYPO3\CMS\Install\Updates\FileListIsStartModuleUpdate::class] = \TYPO3\CMS\Install\Updates\FileListIsStartModuleUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['textmediaCType'] = \TYPO3\CMS\Install\Updates\ContentTypesToTextMediaUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][\TYPO3\CMS\Install\Updates\WorkspacesNotificationSettingsUpdate::class] = \TYPO3\CMS\Install\Updates\WorkspacesNotificationSettingsUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['textmediaAssets'] = \TYPO3\CMS\Install\Updates\MigrateMediaToAssetsForTextMediaCe::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['compatibility6Extension'] = \TYPO3\CMS\Install\Updates\Compatibility6ExtractionUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['mediaceExtension'] = \TYPO3\CMS\Install\Updates\MediaceExtractionUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['openidExtension'] = \TYPO3\CMS\Install\Updates\OpenidExtractionUpdate::class; + $signalSlotDispatcher = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class); $signalSlotDispatcher->connect( \TYPO3\CMS\Install\Service\SqlExpectedSchemaService::class, diff --git a/typo3/sysext/rtehtmlarea/Classes/Hook/Install/DeprecatedRteProperties.php b/typo3/sysext/rtehtmlarea/Classes/Hook/Install/DeprecatedRteProperties.php new file mode 100644 index 000000000000..a9442e1e6311 --- /dev/null +++ b/typo3/sysext/rtehtmlarea/Classes/Hook/Install/DeprecatedRteProperties.php @@ -0,0 +1,270 @@ +<?php +namespace TYPO3\CMS\Rtehtmlarea\Hook\Install; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Install\Updates\AbstractUpdate; + +/** + * Contains the update class for the replacement of deprecated RTE properties in Page TSconfig. + * Used by the upgrade wizard in the install tool. + */ +class DeprecatedRteProperties extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Deprecated RTE properties in Page TSconfig'; + + /** + * Properties that may be replaced automatically in Page TSconfig (except inludes from external files) + * Syntax: 'oldProperty' => 'newProperty' + * + * @var array + */ + protected $replacementRteProperties = array( + 'disableRightClick' => 'contextMenu.disable', + 'disableContextMenu' => 'contextMenu.disable', + 'hidePStyleItems' => 'buttons.formatblock.removeItems', + 'hideFontFaces' => 'buttons.fontstyle.removeItems', + 'fontFace' => 'buttons.fontstyle.addItems', + 'hideFontSizes' => 'buttons.fontsize.removeItems', + 'classesCharacter' => 'buttons.textstyle.tags.span.allowedClasses', + 'classesParagraph' => 'buttons.blockstyle.tags.div.allowedClasses', + 'classesTable' => 'buttons.blockstyle.tags.table.allowedClasses', + 'classesTD' => 'buttons.blockstyle.tags.td.allowedClasses', + 'classesImage' => 'buttons.image.properties.class.allowedClasses', + 'classesLinks' => 'buttons.link.properties.class.allowedClasses', + 'blindImageOptions' => 'buttons.image.options.removeItems', + 'blindLinkOptions' => 'buttons.link.options.removeItems', + 'defaultLinkTarget' => 'buttons.link.properties.target.default' + ); + + /** + * Properties that may be replaced automatically in Page TSconfig (except inludes from external files) + * Syntax: 'oldProperty' => [ 'newProperty', 'newProperty' ] + * + * @var array + */ + protected $doubleReplacementRteProperties = array( + 'disableTYPO3Browsers' => array( + 'buttons.image.TYPO3Browser.disabled', + 'buttons.link.TYPO3Browser.disabled' + ), + 'showTagFreeClasses' => array( + 'buttons.blockstyle.showTagFreeClasses', + 'buttons.textstyle.showTagFreeClasses' + ), + 'disablePCexamples' => array( + 'buttons.blockstyle.disableStyleOnOptionLabel', + 'buttons.textstyle.disableStyleOnOptionLabel' + ) + ); + + /** + * Properties that may not be replaced automatically in Page TSconfig + * Syntax: 'oldProperty' => 'newProperty' + * + * @var array + */ + protected $useInsteadRteProperties = array( + 'fontSize' => 'buttons.fontsize.addItems', + 'RTE.default.classesAnchor' => 'RTE.default.buttons.link.properties.class.allowedClasses', + 'RTE.default.classesAnchor.default.[link-type]' => 'RTE.default.buttons.link.[link-type].properties.class.default', + 'mainStyleOverride' => 'contentCSS', + 'mainStyleOverride_add.[key]' => 'contentCSS', + 'mainStyle_font' => 'contentCSS', + 'mainStyle_size' => 'contentCSS', + 'mainStyle_color' => 'contentCSS', + 'mainStyle_bgcolor' => 'contentCSS', + 'inlineStyle.[any-keystring]' => 'contentCSS', + 'ignoreMainStyleOverride' => 'n.a.' + ); + + /** + * Function which checks if update is needed. Called in the beginning of an update process. + * + * @param string $description Pointer to description for the update + * @return bool TRUE if update is needs to be performed, FALSE otherwise. + */ + public function checkForUpdate(&$description) + { + $result = false; + + $pages = $this->getPagesWithDeprecatedRteProperties($dbQueries, $customMessages); + $pagesCount = count($pages); + $deprecatedProperties = ''; + $deprecatedRteProperties = array_merge($this->replacementRteProperties, $this->useInsteadRteProperties); + foreach ($deprecatedRteProperties as $deprecatedProperty => $replacementProperty) { + $deprecatedProperties .= '<tr><td>' . $deprecatedProperty . '</td><td>' . $replacementProperty . '</td></tr>' . LF; + } + foreach ($this->doubleReplacementRteProperties as $deprecatedProperty => $replacementProperties) { + $deprecatedProperties .= '<tr><td>' . $deprecatedProperty . '</td><td>' . implode(' and ', $replacementProperties) . '</td></tr>' . LF; + } + $description = '<p>The following Page TSconfig RTE properties are deprecated since TYPO3 4.6 and have been removed in TYPO3 6.0.</p>' . LF . '<table><thead><tr><th>Deprecated property</th><th>Use instead</th></tr></thead>' . LF . '<tbody>' . $deprecatedProperties . '</tboby></table>' . LF . '<p>You are currently using some of these properties on <strong>' . strval($pagesCount) . ' pages</strong> (including deleted and hidden pages).</p>' . LF; + if ($pagesCount) { + $pagesUids = array(); + foreach ($pages as $page) { + $pagesUids[] = $page['uid']; + } + $description .= '<p>Pages id\'s: ' . implode(', ', $pagesUids) . '</p>'; + } + $replacementProperties = ''; + foreach ($this->useInsteadRteProperties as $deprecatedProperty => $replacementProperty) { + $replacementProperties .= '<tr><td>' . $deprecatedProperty . '</td><td>' . $replacementProperty . '</td></tr>' . LF; + } + if ($pagesCount) { + $updateablePages = $this->findUpdateablePagesWithDeprecatedRteProperties($pages); + if (!empty($updateablePages)) { + $replacementProperties = ''; + foreach ($this->replacementRteProperties as $deprecatedProperty => $replacementProperty) { + $replacementProperties .= '<tr><td>' . $deprecatedProperty . '</td><td>' . $replacementProperty . '</td></tr>' . LF; + } + $description .= '<p>This wizard will perform automatic replacement of the following properties on <strong>' . strval(count($updateablePages)) . ' pages</strong> (including deleted and hidden):</p>' . LF . '<table><thead><tr><th>Deprecated property</th><th>Will be replaced by</th></tr></thead><tbody>' . $replacementProperties . '</tboby></table>' . LF . '<p>The Page TSconfig column of the remaining pages will need to be updated manually.</p>' . LF; + } else { + $replacementProperties = ''; + foreach ($this->useInsteadRteProperties as $deprecatedProperty => $_) { + $replacementProperties .= '<tr><td>' . $deprecatedProperty . '</td></tr>' . LF; + } + foreach ($this->doubleReplacementRteProperties as $deprecatedProperty => $_) { + $replacementProperties .= '<tr><td>' . $deprecatedProperty . '</td></tr>' . LF; + } + $description .= '<p>This wizard cannot update the following properties, some of which are present on those pages:</p>' . LF . '<table><thead><tr><th>Deprecated property</th></tr></thead><tbody>' . $replacementProperties . '</tboby></table>' . LF . '<p>Therefore, the Page TSconfig column of those pages will need to be updated manually.</p>' . LF; + } + $result = true; + } else { + // if we found no occurrence of deprecated settings and wizard was already executed, then + // we do not show up anymore + if ($this->isWizardDone()) { + $result = false; + } + } + $description .= '<p>Only page records are searched for deprecated properties. However, such properties can also be used in BE group and BE user records (prepended with page.). These are not searched nor updated by this wizard.</p>' . LF . '<p>Page TSconfig may also be included from external files. These are not updated by this wizard. If required, the update will need to be done manually.</p>' . LF . '<p>Note also that deprecated properties have been replaced in default configurations provided by htmlArea RTE'; + + return $result; + } + + /** + * Performs the update itself + * + * @param array $dbQueries Pointer where to insert all DB queries made, so they can be shown to the user if wanted + * @param string $customMessages Pointer to output custom messages + * @return bool TRUE if update succeeded, FALSE otherwise + */ + public function performUpdate(array &$dbQueries, &$customMessages) + { + $customMessages = ''; + $pages = $this->getPagesWithDeprecatedRteProperties($dbQueries, $customMessages); + if (empty($customMessages)) { + $pagesCount = count($pages); + if ($pagesCount) { + $updateablePages = $this->findUpdateablePagesWithDeprecatedRteProperties($pages); + if (!empty($updateablePages)) { + $this->updatePages($updateablePages, $dbQueries, $customMessages); + // If the update was successful + if (empty($customMessages)) { + // If all pages were updated, we query again to check if any deprecated properties are still present. + if (count($updateablePages) === $pagesCount) { + $pagesAfter = $this->getPagesWithDeprecatedRteProperties($dbQueries, $customMessages); + if (empty($customMessages)) { + if (!empty($pagesAfter)) { + $customMessages = 'Some deprecated Page TSconfig properties were found. However, the wizard was unable to automatically replace all the deprecated properties found. Some properties will have to be replaced manually.'; + } + } + } else { + $customMessages = 'Some deprecated Page TSconfig properties were found. However, the wizard was unable to automatically replace all the deprecated properties found. Some properties will have to be replaced manually.'; + } + } + } else { + $customMessages = 'Some deprecated Page TSconfig properties were found. However, the wizard was unable to automatically replace any of the deprecated properties found. These properties will have to be replaced manually.'; + } + } + } + $this->markWizardAsDone(); + return empty($customMessages); + } + + /** + * Gets the pages with deprecated RTE properties in TSconfig column + * + * @param array $dbQueries Pointer where to insert all DB queries made, so they can be shown to the user if wanted + * @param string $customMessages Pointer to output custom messages + * @return array uid and inclusion string for the pages with deprecated RTE properties in TSconfig column + */ + protected function getPagesWithDeprecatedRteProperties(&$dbQueries, &$customMessages) + { + $fields = 'uid, TSconfig'; + $table = 'pages'; + $where = ''; + $db = $this->getDatabaseConnection(); + foreach (array_merge($this->replacementRteProperties, $this->useInsteadRteProperties, $this->doubleReplacementRteProperties) as $deprecatedRteProperty => $_) { + $where .= ($where ? ' OR ' : '') . '(TSconfig LIKE BINARY ' . $db->fullQuoteStr('%RTE.%' . $deprecatedRteProperty . '%', 'pages') . ' AND TSconfig NOT LIKE BINARY ' . $db->fullQuoteStr('%RTE.%' . $deprecatedRteProperty . 's%', 'pages') . ')' . LF; + } + $res = $db->exec_SELECTquery($fields, $table, $where); + $dbQueries[] = str_replace(LF, ' ', $db->debug_lastBuiltQuery); + if ($db->sql_error()) { + $customMessages = 'SQL-ERROR: ' . htmlspecialchars($db->sql_error()); + } + $pages = array(); + while ($row = $db->sql_fetch_assoc($res)) { + $pages[] = $row; + } + return $pages; + } + + /** + * Gets the pages with updateable deprecated RTE properties in TSconfig column + * + * @param array $pages reference to pages with deprecated property + * @return array uid and inclusion string for the pages with deprecated RTE properties in TSconfig column + */ + protected function findUpdateablePagesWithDeprecatedRteProperties(&$pages) + { + foreach ($pages as $index => $page) { + $deprecatedProperties = explode(',', '/' . implode('/,/((RTE\\.(default\\.|config\\.[a-zA-Z0-9_\\-]*\\.[a-zA-Z0-9_\\-]*\\.))|\\s)', array_keys($this->replacementRteProperties)) . '/'); + $replacementProperties = explode(',', '$1' . implode(',$1', array_values($this->replacementRteProperties))); + $updatedPageTSConfig = preg_replace($deprecatedProperties, $replacementProperties, $page['TSconfig']); + if ($updatedPageTSConfig == $page['TSconfig']) { + unset($pages[$index]); + } else { + $pages[$index]['TSconfig'] = $updatedPageTSConfig; + } + } + return $pages; + } + + /** + * updates the pages records with updateable Page TSconfig properties + * + * @param array $pages Page records to update, fetched by getTemplates() and filtered by + * @param array $dbQueries Pointer where to insert all DB queries made, so they can be shown to the user if wanted + * @param string $customMessages Pointer to output custom messages + */ + protected function updatePages($pages, &$dbQueries, &$customMessages) + { + $db = $this->getDatabaseConnection(); + foreach ($pages as $page) { + $table = 'pages'; + $where = 'uid =' . $page['uid']; + $field_values = array( + 'TSconfig' => $page['TSconfig'] + ); + $db->exec_UPDATEquery($table, $where, $field_values); + $dbQueries[] = str_replace(LF, ' ', $db->debug_lastBuiltQuery); + if ($db->sql_error()) { + $customMessages .= 'SQL-ERROR: ' . htmlspecialchars($db->sql_error()) . LF . LF; + } + } + } +} diff --git a/typo3/sysext/rtehtmlarea/Classes/Hook/Install/RteAcronymButtonRenamedToAbbreviation.php b/typo3/sysext/rtehtmlarea/Classes/Hook/Install/RteAcronymButtonRenamedToAbbreviation.php new file mode 100644 index 000000000000..cbe2a94e357b --- /dev/null +++ b/typo3/sysext/rtehtmlarea/Classes/Hook/Install/RteAcronymButtonRenamedToAbbreviation.php @@ -0,0 +1,170 @@ +<?php +namespace TYPO3\CMS\Rtehtmlarea\Hook\Install; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Install\Updates\AbstractUpdate; + +/** + * Contains the update class for the replacement of deprecated acronym button by abbreviation button in Page TSconfig. + * Used by the upgrade wizard in the install tool. + */ +class RteAcronymButtonRenamedToAbbreviation extends AbstractUpdate +{ + /** + * @var string + */ + protected $title = 'Rte "acronym" button renamed to "abbreviation"'; + + /** + * Function which checks if update is needed. Called in the beginning of an update process. + * + * @param string $description Pointer to description for the update + * @return bool TRUE if update is needs to be performed, FALSE otherwise. + */ + public function checkForUpdate(&$description) + { + $result = false; + + $pages = $this->getPagesWithDeprecatedRteProperties($dbQueries, $customMessages); + $pagesCount = count($pages); + $description = '<p>The RTE "acronym" button is deprecated and replaced by the "abbreviation" button since TYPO3 CMS 7.0.</p>' . LF . '<p>Page TSconfig currently includes the string "acronym" on <strong>' . strval($pagesCount) . ' pages</strong> (including deleted and hidden pages).</p>' . LF; + if ($pagesCount) { + $pagesUids = array(); + foreach ($pages as $page) { + $pagesUids[] = $page['uid']; + } + $description .= '<p>Pages id\'s: ' . implode(', ', $pagesUids) . '</p>'; + } + if ($pagesCount) { + $updateablePages = $this->findUpdateablePagesWithDeprecatedRteProperties($pages); + if (!empty($updateablePages)) { + $description .= '<p>This wizard will perform automatic replacement of the string "acronym" by the string "abbreviation" on the Page TSconfig of <strong>' . strval(count($updateablePages)) . ' pages</strong> (including deleted and hidden):</p>' . LF; + } + $result = true; + } else { + // if we found no occurrence of deprecated settings and wizard was already executed, then + // we do not show up anymore + if ($this->isWizardDone()) { + $result = false; + } + } + $description .= '<p>Only page records are searched for the string "acronym". However, such string may also be used in BE group and BE user records. These are not searched nor updated by this wizard.</p>' + . LF . '<p>Page TSconfig may also be included from external files. These are not updated by this wizard. If required, the update will need to be done manually.</p>' + . LF . '<p>Note that this string replacement will apply to all contents of PageTSconfig.</p>' + . LF . '<p>Note that the configuration of RTE processing options (RTE.default.proc) may also include the string "acronym".</p>'; + + return $result; + } + + /** + * Performs the update itself + * + * @param array $dbQueries Pointer where to insert all DB queries made, so they can be shown to the user if wanted + * @param string $customMessages Pointer to output custom messages + * @return bool TRUE if update succeeded, FALSE otherwise + */ + public function performUpdate(array &$dbQueries, &$customMessages) + { + $customMessages = ''; + $pages = $this->getPagesWithDeprecatedRteProperties($dbQueries, $customMessages); + if (empty($customMessages)) { + $pagesCount = count($pages); + if ($pagesCount) { + $updateablePages = $this->findUpdateablePagesWithDeprecatedRteProperties($pages); + if (!empty($updateablePages)) { + $this->updatePages($updateablePages, $dbQueries, $customMessages); + // If the update was successful + if (empty($customMessages)) { + if (count($updateablePages) !== $pagesCount) { + $customMessages = 'Some deprecated Page TSconfig properties were found. However, the wizard was unable to automatically replace all the deprecated properties found. Some properties will have to be replaced manually.'; + } + } + } else { + $customMessages = 'Some deprecated Page TSconfig properties were found. However, the wizard was unable to automatically replace any of the deprecated properties found. These properties will have to be replaced manually.'; + } + } + } + $this->markWizardAsDone(); + return empty($customMessages); + } + + /** + * Gets the pages with deprecated RTE properties in TSconfig column + * + * @param array $dbQueries Pointer where to insert all DB queries made, so they can be shown to the user if wanted + * @param string $customMessages Pointer to output custom messages + * @return array uid and inclusion string for the pages with deprecated RTE properties in TSconfig column + */ + protected function getPagesWithDeprecatedRteProperties(&$dbQueries, &$customMessages) + { + $db = $this->getDatabaseConnection(); + $fields = 'uid, TSconfig'; + $table = 'pages'; + $where = 'TSconfig LIKE BINARY ' . $db->fullQuoteStr('%acronym%', 'pages'); + $res = $db->exec_SELECTquery($fields, $table, $where); + $dbQueries[] = str_replace(LF, ' ', $db->debug_lastBuiltQuery); + if ($db->sql_error()) { + $customMessages = 'SQL-ERROR: ' . htmlspecialchars($db->sql_error()); + } + $pages = array(); + while ($row = $db->sql_fetch_assoc($res)) { + $pages[] = $row; + } + return $pages; + } + + /** + * Gets the pages with updateable deprecated RTE properties in TSconfig column + * + * @param array $pages reference to pages with deprecated property + * @return array uid and inclusion string for the pages with deprecated RTE properties in TSconfig column + */ + protected function findUpdateablePagesWithDeprecatedRteProperties(&$pages) + { + foreach ($pages as $index => $page) { + $updatedPageTSConfig = str_replace('acronym', 'abbreviation', $page['TSconfig']); + if ($updatedPageTSConfig == $page['TSconfig']) { + unset($pages[$index]); + } else { + $pages[$index]['TSconfig'] = $updatedPageTSConfig; + } + } + return $pages; + } + + /** + * updates the pages records with updateable Page TSconfig properties + * + * @param array $pages Page records to update, fetched by getTemplates() and filtered by + * @param array $dbQueries Pointer where to insert all DB queries made, so they can be shown to the user if wanted + * @param string $customMessages Pointer to output custom messages + */ + protected function updatePages($pages, &$dbQueries, &$customMessages) + { + $db = $this->getDatabaseConnection(); + foreach ($pages as $page) { + $table = 'pages'; + $where = 'uid =' . $page['uid']; + $field_values = array( + 'TSconfig' => $page['TSconfig'] + ); + $db->exec_UPDATEquery($table, $where, $field_values); + $dbQueries[] = str_replace(LF, ' ', $db->debug_lastBuiltQuery); + if ($db->sql_error()) { + $customMessages .= 'SQL-ERROR: ' . htmlspecialchars($db->sql_error()) . LF . LF; + } + } + } +} diff --git a/typo3/sysext/rtehtmlarea/ext_localconf.php b/typo3/sysext/rtehtmlarea/ext_localconf.php index 3a6273b52f64..dfa123994d43 100644 --- a/typo3/sysext/rtehtmlarea/ext_localconf.php +++ b/typo3/sysext/rtehtmlarea/ext_localconf.php @@ -42,6 +42,11 @@ if (TYPO3_MODE === 'BE') { $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['reports']['tx_reports']['status']['providers']['htmlArea RTE'][] = \TYPO3\CMS\Rtehtmlarea\Hook\StatusReportConflictsCheckHook::class; } +// Set warning in the Update Wizard of the Install Tool for deprecated Page TS Config properties +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['checkForDeprecatedRtePageTSConfigProperties'] = \TYPO3\CMS\Rtehtmlarea\Hook\Install\DeprecatedRteProperties::class; +// Set warning in the Update Wizard of the Install Tool for replacement of "acronym" button by "abbreviation" button +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['checkForRteAcronymButtonRenamedToAbbreviation'] = \TYPO3\CMS\Rtehtmlarea\Hook\Install\RteAcronymButtonRenamedToAbbreviation::class; + // Initialize plugin registration array $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rtehtmlarea']['plugins'] = array(); -- GitLab