From 6d78a98a04487683dc28f1649076ebeaea21b8d6 Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 2 Jul 2020 12:36:13 +1200 Subject: [PATCH] Set locales for all domains when enabling multilingual In the process of trying to test a possible wordpress regression I found that enabling mutlilingual on a domain other than domain 1 breaks the site. The reason is that without only domain 1 is being updated with the new language and locales. Since I'm on domain 2 when I do this I can no longer load any page as the locales value has not been set and the queries look for option_value.label not option_value.label_us. Since 'no locale' is no longer valid the locale needs to be set on all sites. Note the code change is primarily an extraction inn order to only run the table update part once --- CRM/Core/I18n/Schema.php | 115 +++++++++++++-------- tests/phpunit/CRM/Core/I18n/SchemaTest.php | 8 ++ 2 files changed, 78 insertions(+), 45 deletions(-) diff --git a/CRM/Core/I18n/Schema.php b/CRM/Core/I18n/Schema.php index 13318083bb..d3e1adb11d 100644 --- a/CRM/Core/I18n/Schema.php +++ b/CRM/Core/I18n/Schema.php @@ -44,56 +44,32 @@ class CRM_Core_I18n_Schema { * the first locale to create (migrate to). */ public static function makeMultilingual($locale) { - $domain = new CRM_Core_DAO_Domain(); - $domain->find(TRUE); - - // break early if the db is already multi-lang - if ($domain->locales) { - return; - } - - $dao = new CRM_Core_DAO(); - - // build the column-adding SQL queries - $columns = CRM_Core_I18n_SchemaStructure::columns(); - $indices = CRM_Core_I18n_SchemaStructure::indices(); - $queries = []; - foreach ($columns as $table => $hash) { - // drop old indices - if (isset($indices[$table])) { - foreach ($indices[$table] as $index) { - if (CRM_Core_BAO_SchemaHandler::checkIfIndexExists($table, $index['name'])) { - $queries[] = "DROP INDEX {$index['name']} ON {$table}"; - } - } - } - // deal with columns - foreach ($hash as $column => $type) { - $queries[] = "ALTER TABLE {$table} ADD {$column}_{$locale} {$type}"; - if (CRM_Core_BAO_SchemaHandler::checkIfFieldExists($table, $column)) { - $queries[] = "UPDATE {$table} SET {$column}_{$locale} = {$column}"; - $queries[] = "ALTER TABLE {$table} DROP {$column}"; - } + $isUpdateDone = FALSE; + $domain = new CRM_Core_BAO_Domain(); + $domain->find(); + $domains = []; + while ($domain->fetch()) { + // We need to build an array to iterate through here as something later down clears + // the cache on the fetch results & causes only the first to be retrieved. + $domains[] = clone $domain; + } + foreach ($domains as $domain) { + // skip if the domain is already multi-lang. + if ($domain->locales) { + continue; } - // add view - $queries[] = self::createViewQuery($locale, $table, $dao); + if (!$isUpdateDone) { + $isUpdateDone = self::alterTablesToSupportMultilingual($locale); + } - // add new indices - $queries = array_merge($queries, array_values(self::createIndexQueries($locale, $table))); - } + // update civicrm_domain.locales + $domain->locales = $locale; + $domain->save(); - // execute the queries without i18n rewriting - foreach ($queries as $query) { - $dao->query($query, FALSE); + // CRM-21627 Updates the $dbLocale + CRM_Core_BAO_ConfigSetting::applyLocale(Civi::settings($domain->id), $domain->locales); } - - // update civicrm_domain.locales - $domain->locales = $locale; - $domain->save(); - - // CRM-21627 Updates the $dbLocale - CRM_Core_BAO_ConfigSetting::applyLocale(Civi::settings($domain->id), $domain->locales); } /** @@ -606,4 +582,53 @@ class CRM_Core_I18n_Schema { } } + /** + * Alter tables to the structure to support multilingual. + * + * This alters the db structure to use language specific field names for + * localised fields and adds the relevant views. + * + * @param string $locale + * + * @return bool + */ + protected static function alterTablesToSupportMultilingual($locale): bool { + $dao = new CRM_Core_DAO(); + + // build the column-adding SQL queries + $columns = CRM_Core_I18n_SchemaStructure::columns(); + $indices = CRM_Core_I18n_SchemaStructure::indices(); + $queries = []; + foreach ($columns as $table => $hash) { + // drop old indices + if (isset($indices[$table])) { + foreach ($indices[$table] as $index) { + if (CRM_Core_BAO_SchemaHandler::checkIfIndexExists($table, $index['name'])) { + $queries[] = "DROP INDEX {$index['name']} ON {$table}"; + } + } + } + // deal with columns + foreach ($hash as $column => $type) { + $queries[] = "ALTER TABLE {$table} ADD {$column}_{$locale} {$type}"; + if (CRM_Core_BAO_SchemaHandler::checkIfFieldExists($table, $column)) { + $queries[] = "UPDATE {$table} SET {$column}_{$locale} = {$column}"; + $queries[] = "ALTER TABLE {$table} DROP {$column}"; + } + } + + // add view + $queries[] = self::createViewQuery($locale, $table, $dao); + + // add new indices + $queries = array_merge($queries, array_values(self::createIndexQueries($locale, $table))); + } + + // execute the queries without i18n rewriting + foreach ($queries as $query) { + $dao->query($query, FALSE); + } + return TRUE; + } + } diff --git a/tests/phpunit/CRM/Core/I18n/SchemaTest.php b/tests/phpunit/CRM/Core/I18n/SchemaTest.php index 445e1c4736..d3a756e41d 100644 --- a/tests/phpunit/CRM/Core/I18n/SchemaTest.php +++ b/tests/phpunit/CRM/Core/I18n/SchemaTest.php @@ -40,9 +40,17 @@ class CRM_Core_I18n_SchemaTest extends CiviUnitTestCase { * @param string $expectedRewrite * * @dataProvider translateTables + * @throws \CRM_Core_Exception */ public function testI18nSchemaRewrite($table, $expectedRewrite) { CRM_Core_I18n_Schema::makeMultilingual('en_US'); + $domains = $this->callAPISuccess('Domain', 'get')['values']; + foreach ($domains as $domain) { + // If the DB is multilingual the locales value must be not-null for all domains + // to ensure the db can be accessed (I suspect it must be the same for all locales but + // if null then the database layer attempts to access non-existent fields 'label' not label_en_us'. + $this->assertEquals('en_US', $domain['locales']); + } $skip_tests = FALSE; if (in_array($table, ['civicrm_option_group', 'civicrm_event'])) { $skip_tests = TRUE; -- 2.25.1