[FEATURE] Check database default charset 03/47103/5
authorJonas Götze <jonnsn@gmail.com>
Sat, 5 Mar 2016 14:23:43 +0000 (15:23 +0100)
committerAndreas Wolf <andreas.wolf@typo3.org>
Sat, 5 Mar 2016 16:57:10 +0000 (17:57 +0100)
Checks the default database charset during installation and provides
Upgrade Wizard to fix default charset.

Resolves: #74319
Releases: master
Change-Id: I645c0dc321a719e2149b8474e4292c80811fd82c
Reviewed-on: https://review.typo3.org/47103
Reviewed-by: Eugen Lang <laeu1011+gerrit@gmail.com>
Tested-by: Eugen Lang <laeu1011+gerrit@gmail.com>
Reviewed-by: Andreas Wolf <andreas.wolf@typo3.org>
Tested-by: Andreas Wolf <andreas.wolf@typo3.org>
typo3/sysext/core/Documentation/Changelog/master/Feature-74319-CheckDefaultDatabaseCharacterSetDuringInstallationAndProvideUpdateWizardForFixingDefaultCharacterSetOtherThanUtf-8.rst [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Step/DatabaseSelect.php
typo3/sysext/install/Classes/Controller/Action/Tool/UpgradeWizard.php
typo3/sysext/install/Classes/SystemEnvironment/DatabaseCheck.php
typo3/sysext/install/Classes/Updates/DatabaseCharsetUpdate.php [new file with mode: 0644]

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-74319-CheckDefaultDatabaseCharacterSetDuringInstallationAndProvideUpdateWizardForFixingDefaultCharacterSetOtherThanUtf-8.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-74319-CheckDefaultDatabaseCharacterSetDuringInstallationAndProvideUpdateWizardForFixingDefaultCharacterSetOtherThanUtf-8.rst
new file mode 100644 (file)
index 0000000..03bb219
--- /dev/null
@@ -0,0 +1,21 @@
+======================================================================================================================================================
+Feature: #74319 - check default database character set during Installation and provide Update wizard for fixing default character set other than utf-8
+======================================================================================================================================================
+
+
+Description
+===========
+
+If you install TYPO3 to an existing database with a default charset other than utf-8, TYPO3 will create tables with the default charset of that database.
+The install tool should check the default charset and notify the user if it is not utf-8.
+
+Furthermore the install tool should check for this issue too and provide an update wizard to fix this (=set the default charset to utf-8 and NOT convert existing tables to utf-8).
+A default charset set other than utf-8 leads to non-utf-8 tables when updating the database via the install tool or installing extensions.
+
+
+Impact
+======
+
+During installation on database select the default charset of the database is checked. If it is not utf-8 the installation will not proceed and the user is notified of the issue.
+
+For existing installations the install tool also provides an environment check and an upgrade wizard which changes the default database character set. The update wizard will NOT convert any existing tables though!
\ No newline at end of file
index e892f0b..99c6302 100644 (file)
@@ -41,6 +41,7 @@ class DatabaseSelect extends AbstractStepAction
         $localConfigurationPathValuePairs = array();
         /** @var $configurationManager \TYPO3\CMS\Core\Configuration\ConfigurationManager */
         $configurationManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
+        $canProceed = true;
         if ($postValues['type'] === 'new') {
             $newDatabaseName = $postValues['new'];
             if ($this->isValidDatabaseName($newDatabaseName)) {
@@ -79,6 +80,19 @@ class DatabaseSelect extends AbstractStepAction
             if (!$isInitialInstallation || empty($existingTables)) {
                 $localConfigurationPathValuePairs['DB/database'] = $postValues['existing'];
             }
+            // check if database charset is utf-8
+            $defaultDatabaseCharset = $this->getDefaultDatabaseCharset();
+            // also allow utf8mb4
+            if (substr($defaultDatabaseCharset, 0, 4) !== 'utf8') {
+                $errorStatus = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
+                $errorStatus->setTitle('Invalid Charset');
+                $errorStatus->setMessage(
+                    'Your database uses character set "' . $defaultDatabaseCharset . '", ' .
+                    'but only "utf8" is supported with TYPO3. You probably want to change this before proceeding.'
+                );
+                $result[] = $errorStatus;
+                $canProceed = false;
+            }
         } else {
             /** @var $errorStatus \TYPO3\CMS\Install\Status\ErrorStatus */
             $errorStatus = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
@@ -87,7 +101,7 @@ class DatabaseSelect extends AbstractStepAction
             $result[] = $errorStatus;
         }
 
-        if (!empty($localConfigurationPathValuePairs)) {
+        if ($canProceed && !empty($localConfigurationPathValuePairs)) {
             $configurationManager->setLocalConfigurationValuesByPathValuePairs($localConfigurationPathValuePairs);
         }
 
@@ -192,4 +206,25 @@ class DatabaseSelect extends AbstractStepAction
     {
         return strlen($databaseName) <= 50 && preg_match('/^[a-zA-Z0-9\$_]*$/', $databaseName);
     }
+
+    /**
+     * Retrieves the default character set of the database.
+     *
+     * @return string
+     */
+    protected function getDefaultDatabaseCharset()
+    {
+        $result = $this->databaseConnection->admin_query('SHOW VARIABLES LIKE "character_set_database"');
+        $row = $this->databaseConnection->sql_fetch_assoc($result);
+
+        $key = $row['Variable_name'];
+        $value = $row['Value'];
+        $databaseCharset = '';
+
+        if ($key == 'character_set_database') {
+            $databaseCharset = $value;
+        }
+
+        return $databaseCharset;
+    }
 }
index 1f96ee5..ca48e92 100644 (file)
@@ -45,7 +45,14 @@ class UpgradeWizard extends Action\AbstractAction
             $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'] = [];
         }
 
-        // To make sure initialUpdateDatabaseSchema is first wizard, it is added here instead of ext_localconf.php
+        // To make sure DatabaseCharsetUpdate and initialUpdateDatabaseSchema are first wizards, they are added here instead of ext_localconf.php
+        $databaseCharsetUpdateObject = $this->getUpdateObjectInstance(\TYPO3\CMS\Install\Updates\DatabaseCharsetUpdate::class, 'databaseCharsetUpdate');
+        if ($databaseCharsetUpdateObject->shouldRenderWizard()) {
+            $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'] = array_merge(
+                array('databaseCharsetUpdate' => \TYPO3\CMS\Install\Updates\DatabaseCharsetUpdate::class),
+                $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']
+            );
+        }
         $initialUpdateDatabaseSchemaUpdateObject = $this->getUpdateObjectInstance(\TYPO3\CMS\Install\Updates\InitialDatabaseSchemaUpdate::class, 'initialUpdateDatabaseSchema');
         if ($initialUpdateDatabaseSchemaUpdateObject->shouldRenderWizard()) {
             $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'] = array_merge(
index e283c14..7ec87f5 100644 (file)
@@ -50,6 +50,7 @@ class DatabaseCheck
         }
         $statusArray[] = $this->checkMysqlVersion();
         $statusArray[] = $this->checkInvalidSqlModes();
+        $statusArray[] = $this->checkMysqlDatabaseUtf8Status();
         return $statusArray;
     }
 
@@ -110,6 +111,41 @@ class DatabaseCheck
         return $status;
     }
 
+    /**
+     * Checks the character set of the database and reports an error if it is not utf-8.
+     *
+     * @return Status\StatusInterface
+     */
+    protected function checkMysqlDatabaseUtf8Status()
+    {
+        $result = $this->getDatabaseConnection()->admin_query('SHOW VARIABLES LIKE "character_set_database"');
+        $row = $this->getDatabaseConnection()->sql_fetch_assoc($result);
+
+        $key = $row['Variable_name'];
+        $value = $row['Value'];
+
+        if ($key !== 'character_set_database') {
+            $status = new Status\ErrorStatus();
+            $status->setTitle('MySQL database character set check failed');
+            $status->setMessage(
+                'Checking database character set failed, got key "' . $key . '" instead of "character_set_database"'
+            );
+        }
+        // also allow utf8mb4
+        if (substr($value, 0, 4) !== 'utf8') {
+            $status = new Status\ErrorStatus();
+            $status->setTitle('MySQL database character set wrong');
+            $status->setMessage(
+                'Your database uses character set "' . $value . '", but only "utf8" is supported with TYPO3.'
+            );
+        } else {
+            $status = new Status\OkStatus();
+            $status->setTitle('Your database uses utf-8. All good.');
+        }
+
+        return $status;
+    }
+
     /**
      * Returns an array with the current sql mode settings
      *
diff --git a/typo3/sysext/install/Classes/Updates/DatabaseCharsetUpdate.php b/typo3/sysext/install/Classes/Updates/DatabaseCharsetUpdate.php
new file mode 100644 (file)
index 0000000..2e75ec0
--- /dev/null
@@ -0,0 +1,109 @@
+<?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!
+ */
+
+/**
+ * Move "wizard done" flags to system registry
+ */
+class DatabaseCharsetUpdate extends AbstractUpdate
+{
+    /**
+     * @var string
+     */
+    protected $title = 'Set default database charset to utf-8';
+
+    /**
+     * 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)
+    {
+        $result = false;
+        $description = 'Sets the default database charset to utf-8 to ensure new tables are created with correct charset.
+        WARNING: This will NOT convert any existing data.';
+
+        if($this->isDbalEnabled()) {
+            return $result;
+        }
+        // check if database charset is utf-8
+        $defaultDatabaseCharset = $this->getDefaultDatabaseCharset();
+        // also allow utf8mb4
+        if (substr($defaultDatabaseCharset, 0, 4) !== 'utf8') {
+            $result = true;
+        }
+
+        return $result;
+    }
+
+    /**
+     * 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)
+    {
+        $result = true;
+        $db = $this->getDatabaseConnection();
+        $query = 'ALTER DATABASE `' . $GLOBALS['TYPO3_CONF_VARS']['DB']['database'] . '` DEFAULT CHARACTER SET utf8';
+        $db->admin_query($query);
+        $dbQueries[] = $query;
+        if ($db->sql_error()) {
+            $customMessages = 'SQL-ERROR: ' . htmlspecialchars($db->sql_error());
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * Return TRUE if dbal and adodb extension is loaded
+     *
+     * @return bool TRUE if dbal and adodb is loaded
+     */
+    protected function isDbalEnabled()
+    {
+        if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('adodb')
+            && \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('dbal')
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Retrieves the default character set of the database.
+     *
+     * @return string
+     */
+    protected function getDefaultDatabaseCharset()
+    {
+        $db = $this->getDatabaseConnection();
+        $result = $db->admin_query('SHOW VARIABLES LIKE "character_set_database"');
+        $row = $db->sql_fetch_assoc($result);
+
+        $key = $row['Variable_name'];
+        $value = $row['Value'];
+        $databaseCharset = '';
+
+        if ($key == 'character_set_database') {
+            $databaseCharset = $value;
+        }
+
+        return $databaseCharset;
+    }
+}