diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index cf0e3dd..667c3ab 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -2476,15 +2476,26 @@ function language_list($flags = Language::STATE_CONFIGURABLE) { // Fill in master language list based on current configuration. $default = language_default(); if (language_multilingual() || module_exists('language')) { - // Use language module configuration if available. - $languages = db_query('SELECT * FROM {language} ORDER BY weight ASC, name ASC')->fetchAllAssoc('langcode', PDO::FETCH_ASSOC); + // Use language module configuration if available. We can not use + // entity_load_multiple() because this breaks during updates. + // @todo Use entity_load_by_properties() https://drupal.org/node/2026227 + $language_entities = config_get_storage_names_with_prefix('language.entity'); // Initialize default property so callers have an easy reference and can // save the same object without data loss. - foreach ($languages as $langcode => $info) { - $info['default'] = ($langcode == $default->langcode); - $languages[$langcode] = new Language($info); + foreach ($language_entities as $langcode_config_name) { + $langcode = substr($langcode_config_name, strlen('language.entity.')); + $info = config($langcode_config_name)->get(); + $languages[$langcode] = new Language(array( + 'default' => ($info['id'] == $default->langcode), + 'name' => $info['label'], + 'langcode' => $info['id'], + 'direction' => $info['direction'], + 'locked' => $info['locked'], + 'weight' => $info['weight'], + )); } + Language::sort($languages); } else { // No language module, so use the default language only. diff --git a/core/lib/Drupal/Core/Language/Language.php b/core/lib/Drupal/Core/Language/Language.php index 01eb199..6e54b2d 100644 --- a/core/lib/Drupal/Core/Language/Language.php +++ b/core/lib/Drupal/Core/Language/Language.php @@ -146,4 +146,22 @@ public function extend($obj) { $this->$var = $value; } } + + /** + * Sort language objects. + * + * @param array $languages + * The array of language objects keyed by langcode. + */ + public static function sort($languages) { + uasort($languages, function ($a, $b) { + $a_weight = isset($a->weight) ? $a->weight : 0; + $b_weight = isset($b->weight) ? $b->weight : 0; + if ($a_weight == $b_weight) { + return strnatcasecmp($a->name, $b->name); + } + return ($a_weight < $b_weight) ? -1 : 1; + }); + } + } diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php index 20ef9f0..4328892 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php @@ -64,7 +64,7 @@ function testConfigLocaleOverride() { */ function testConfigLocaleUserOverride() { $this->installSchema('system', 'variable'); - $this->installSchema('language', 'language'); + $this->installConfig(array('language')); language_save(new Language(array( 'name' => 'French', 'langcode' => 'fr', @@ -154,7 +154,7 @@ function testConfigLocaleUserOverride() { */ function testConfigLocaleLanguageOverride() { $this->installSchema('system', 'variable'); - $this->installSchema('language', 'language'); + $this->installConfig(array('language')); language_save(new Language(array( 'name' => 'French', 'langcode' => 'fr', @@ -231,7 +231,7 @@ function testConfigLocaleUserAndGlobalOverride() { $conf['config_test.system']['404'] = 'global herp'; $this->installSchema('system', 'variable'); - $this->installSchema('language', 'language'); + $this->installConfig(array('language')); language_save(new Language(array( 'name' => 'French', 'langcode' => 'fr', diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php index ae7e582..83b4aa6 100644 --- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php +++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php @@ -60,9 +60,8 @@ function setUp() { parent::setUp(); $this->installSchema('system', array('variable', 'url_alias')); $this->installSchema('user', array('users')); - $this->installSchema('language', array('language')); $this->installSchema('entity_test', array('entity_test')); - $this->installConfig(array('field')); + $this->installConfig(array('field', 'language')); // Add English as a language. $english = new Language(array( diff --git a/core/modules/language/config/language.entity.en.yml b/core/modules/language/config/language.entity.en.yml new file mode 100644 index 0000000..3c56306 --- /dev/null +++ b/core/modules/language/config/language.entity.en.yml @@ -0,0 +1,7 @@ +id: en +label: English +direction: '0' +weight: '0' +locked: '0' +status: '1' +langcode: en diff --git a/core/modules/language/config/language.entity.und.yml b/core/modules/language/config/language.entity.und.yml new file mode 100644 index 0000000..f0cb10d --- /dev/null +++ b/core/modules/language/config/language.entity.und.yml @@ -0,0 +1,7 @@ +id: und +label: 'Not specified' +direction: '0' +weight: '1' +locked: '1' +status: '1' +langcode: en diff --git a/core/modules/language/config/language.entity.zxx.yml b/core/modules/language/config/language.entity.zxx.yml new file mode 100644 index 0000000..ef4b3c5 --- /dev/null +++ b/core/modules/language/config/language.entity.zxx.yml @@ -0,0 +1,7 @@ +id: zxx +label: 'Not applicable' +direction: '0' +weight: '2' +locked: '1' +status: '1' +langcode: en diff --git a/core/modules/language/language.install b/core/modules/language/language.install index 17828fd..9a7ded5 100644 --- a/core/modules/language/language.install +++ b/core/modules/language/language.install @@ -5,6 +5,9 @@ * Install, update and uninstall functions for the language module. */ +use Drupal\Component\Uuid\Uuid; +use Drupal\Core\Language\Language; + /** * Implements hook_install(). * @@ -12,14 +15,6 @@ * system on multilingual sites without needing any preliminary configuration. */ function language_install() { - // Add the default language at first so that language_list() returns this in - // language_special_languages(). - $default_language = language_save(language_default()); - $languages = language_default_locked_languages($default_language->weight); - foreach ($languages as $language) { - language_save($language); - } - // Enable URL language detection for each configurable language type. require_once DRUPAL_ROOT . '/core/includes/language.inc'; foreach (language_types_get_configurable(FALSE) as $type) { @@ -51,55 +46,6 @@ function language_uninstall() { } /** - * Implements hook_schema(). - */ -function language_schema() { - $schema['language'] = array( - 'description' => 'List of all available languages in the system.', - 'fields' => array( - 'langcode' => array( - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - 'description' => "Language code, e.g. 'de' or 'en-US'.", - ), - 'name' => array( - 'type' => 'varchar', - 'length' => 64, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Language name.', - ), - 'direction' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Direction of language (Left-to-Right = 0, Right-to-Left = 1).', - ), - 'weight' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Weight, used in lists of languages.', - ), - 'locked' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'A boolean indicating whether the administrator can edit or delete the language.', - ), - ), - 'primary key' => array('langcode'), - 'indexes' => array( - 'list' => array('weight', 'name'), - ), - ); - return $schema; -} - -/** * Implements hook_enable(). */ function language_enable() { @@ -127,3 +73,35 @@ function language_requirements($phase) { language_negotiation_include(); } } + +/** + * @addtogroup updates-7.x-to-8.x + * @{ + */ + +/** + * Migrate all languages to configuration. + * + * @ingroup config_upgrade + */ +function language_update_8000() { + $result = db_query('SELECT * FROM {language}'); + $uuid = new Uuid(); + foreach ($result as $language) { + config('language.entity.' . $language->langcode) + ->set('id', $language->langcode) + ->set('uuid', $uuid->generate()) + ->set('label', $language->name) + ->set('direction', $language->direction) + ->set('weight', $language->weight) + ->set('locked', $language->locked) + ->set('status', 1) + ->set('langcode', Language::LANGCODE_NOT_SPECIFIED) + ->save(); + } +} + +/** + * @} End of "addtogroup updates-7.x-to-8.x". + * The next series of updates should start at 9000. + */ diff --git a/core/modules/language/language.module b/core/modules/language/language.module index f18d494..8bd14db 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -484,23 +484,35 @@ function language_get_default_langcode($entity_type, $bundle) { * API function to add or update a language. * * @param $language - * Language object with properties corresponding to 'language' table columns. + * Language object with properties corresponding to the 'language' + * configuration properties. */ function language_save($language) { - $language->is_new = !(bool) db_query_range('SELECT 1 FROM {language} WHERE langcode = :langcode', 0, 1, array(':langcode' => $language->langcode))->fetchField(); + $language_entity = entity_load('language_entity', $language->langcode); + if (!$language_entity) { + $language_entity = entity_create('language_entity', array( + 'id' => $language->langcode, + )); + } + $language->is_new = $language_entity->isNew(); // Let other modules modify $language before saved. module_invoke_all('language_presave', $language); + // Assign language properties to language entity. + $language_entity->label = isset($language->name) ? $language->name : ''; + $language_entity->direction = isset($language->direction) ? $language->direction : '0'; + $language_entity->locked = isset($language->locked) ? $language->locked : '0'; + $language_entity->weight = isset($language->weight) ? $language->weight : '0'; + // Save the record and inform others about the change. + $language_entity->save(); $t_args = array('%language' => $language->name, '%langcode' => $language->langcode); if ($language->is_new) { - drupal_write_record('language', $language); module_invoke_all('language_insert', $language); watchdog('language', 'The %language (%langcode) language has been created.', $t_args); } else { - drupal_write_record('language', $language, array('langcode')); module_invoke_all('language_update', $language); watchdog('language', 'The %language (%langcode) language has been updated.', $t_args); } @@ -536,7 +548,14 @@ function language_save($language) { * @see language_multilingual() */ function language_update_count() { - variable_set('language_count', db_query('SELECT COUNT(langcode) FROM {language} WHERE locked = 0')->fetchField()); + $count = 0; + $languages = entity_load_multiple('language_entity', NULL, TRUE); + foreach ($languages as $language) { + if (!$language->locked) { + $count++; + } + } + variable_set('language_count', $count); } /** @@ -556,9 +575,7 @@ function language_delete($langcode) { module_invoke_all('language_delete', $language); // Remove the language. - db_delete('language') - ->condition('langcode', $language->langcode) - ->execute(); + entity_delete_multiple('language_entity', array($language->langcode)); language_update_count(); @@ -814,16 +831,23 @@ function language_set_browser_drupal_langcode_mappings($mappings) { * Updates locked system language weights. */ function language_update_locked_weights() { + $max_weight = 0; + // Get maximum weight to update the system languages to keep them on bottom. - $max_weight = db_query('SELECT MAX(weight) FROM {language} WHERE locked = 0')->fetchField(); + $languages = entity_load_multiple('language_entity', NULL, TRUE); + foreach ($languages as $language) { + if (!$language->locked && $language->weight > $max_weight) { + $max_weight = $language->weight; + } + } + // Loop locked languages to maintain the existing order. foreach (language_list(Language::STATE_LOCKED) as $language) { $max_weight++; // Update system languages weight. - db_update('language') - ->fields(array('weight' => $max_weight)) - ->condition('langcode', $language->langcode) - ->execute(); + config('language.entity.' . $language->langcode) + ->set('weight', $max_weight) + ->save(); } } diff --git a/core/modules/language/language.views.inc b/core/modules/language/language.views.inc deleted file mode 100644 index 2ec7e3a..0000000 --- a/core/modules/language/language.views.inc +++ /dev/null @@ -1,108 +0,0 @@ - 'langcode', - 'title' => t('Language'), - 'help' => t('A language used in drupal.'), - ); - - $data['language']['langcode'] = array( - 'title' => t('Language code'), - 'help' => t("Language code, e.g. 'de' or 'en-US'."), - 'field' => array( - 'id' => 'standard', - ), - 'filter' => array( - 'id' => 'string' - ), - 'argument' => array( - 'id' => 'string', - ), - 'sort' => array( - 'id' => 'standard', - ), - ); - - $data['language']['name'] = array( - 'title' => t('Language name'), - 'help' => t("Language name, e.g. 'German' or 'English'."), - 'field' => array( - 'id' => 'standard', - ), - 'filter' => array( - 'id' => 'string' - ), - 'argument' => array( - 'id' => 'string', - ), - 'sort' => array( - 'id' => 'standard', - ), - ); - - $data['language']['direction'] = array( - 'title' => t('Direction'), - 'help' => t('Direction of language (Left-to-Right = 0, Right-to-Left = 1).'), - 'field' => array( - 'id' => 'numeric', - ), - 'filter' => array( - 'id' => 'numeric' - ), - 'argument' => array( - 'id' => 'numeric', - ), - 'sort' => array( - 'id' => 'standard', - ), - ); - - $data['language']['weight'] = array( - 'title' => t('Weight'), - 'help' => t('Weight, used in lists of languages.'), - 'field' => array( - 'id' => 'numeric', - ), - 'filter' => array( - 'id' => 'numeric' - ), - 'argument' => array( - 'id' => 'numeric', - ), - 'sort' => array( - 'id' => 'standard', - ), - ); - - $data['language']['locked'] = array( - 'title' => t('Locked'), - 'help' => t('A boolean indicating whether the administrator can edit or delete the language.'), - 'field' => array( - 'id' => 'boolean', - ), - 'filter' => array( - 'id' => 'boolean', - ), - 'argument' => array( - 'id' => 'numeric', - ), - 'sort' => array( - 'id' => 'standard', - ), - ); - - return $data; -} diff --git a/core/modules/language/lib/Drupal/language/LanguageInterface.php b/core/modules/language/lib/Drupal/language/LanguageInterface.php new file mode 100644 index 0000000..35db887 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageInterface.php @@ -0,0 +1,17 @@ +installSchema('language', 'language'); // This is needed for language_default(). // @todo remove this when language_default() no longer needs variable_get(). $this->installSchema('system', 'variable'); + $this->installConfig(array('language')); // Setup English. language_save(language_default()); diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php index 770337c..4bce384 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php @@ -159,7 +159,16 @@ protected function checkConfigurableLanguageWeight($state = 'by default') { * Maximum weight of configurable languages. */ protected function getHighestConfigurableLanguageWeight(){ - return db_query('SELECT MAX(weight) FROM {language} WHERE locked = 0')->fetchField(); + $max_weight = 0; + + $languages = entity_load_multiple('language_entity', NULL, TRUE); + foreach ($languages as $language) { + if (!$language->locked && $language->weight > $max_weight) { + $max_weight = $language->weight; + } + } + + return $max_weight; } } diff --git a/core/modules/language/lib/Drupal/language/Tests/Views/LanguageTestBase.php b/core/modules/language/lib/Drupal/language/Tests/Views/LanguageTestBase.php index 9057a5a..9bc79dd 100644 --- a/core/modules/language/lib/Drupal/language/Tests/Views/LanguageTestBase.php +++ b/core/modules/language/lib/Drupal/language/Tests/Views/LanguageTestBase.php @@ -24,9 +24,8 @@ protected function setUp() { parent::setUp(); - $this->installSchema('language', 'language'); $this->installSchema('system', 'variable'); - + $this->installConfig(array('language')); // Create English and another language beside English. $language = new Language(array('langcode' => 'en')); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php index a5ed01b..d7ce9ab 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php @@ -173,7 +173,10 @@ function testPluralEditExport() { // Look up editing page for this plural string and check fields. $path = 'admin/config/regional/translate/'; - $this->drupalGet($path); + $search = array( + 'langcode' => 'hr', + ); + $this->drupalPost($path, $search, t('Filter')); // Labels for plural editing elements. $this->assertText('Singular form'); $this->assertText('First plural form'); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php index d778211..8b774bd 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php @@ -31,7 +31,7 @@ function setUp() { parent::setUp(); $this->installSchema('user', array('users_roles')); $this->installSchema('system', array('variable', 'url_alias')); - $this->installSchema('language', 'language'); + $this->installConfig(array('language')); // Create the default languages. $default_language = language_save(language_default()); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php index cd6fa31..94d762f 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php @@ -56,8 +56,9 @@ public static function getInfo() { function setUp() { parent::setUp(); $this->installSchema('entity_test', array('entity_test_mulrev', 'entity_test_mulrev_property_data', 'entity_test_mulrev_property_revision')); - $this->installSchema('language', array('language')); $this->installSchema('system', array('variable')); + $this->installConfig(array('language')); + $figures = drupal_strtolower($this->randomName()); $greetings = drupal_strtolower($this->randomName()); foreach (array($figures => 'shape', $greetings => 'text') as $field_name => $field_type) { diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php index a235add..9d724a4 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php @@ -31,7 +31,6 @@ public static function getInfo() { function setUp() { parent::setUp(); $this->installSchema('system', 'variable'); - $this->installSchema('language', 'language'); $this->installSchema('entity_test', array( 'entity_test_mul', 'entity_test_mul_property_data', @@ -41,6 +40,7 @@ function setUp() { 'entity_test_mulrev_property_data', 'entity_test_mulrev_property_revision', )); + $this->installConfig(array('language')); // Create the test field. entity_test_install(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php index 0143d1a..6b3383b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php @@ -39,8 +39,9 @@ function testEnableWithoutDependency() { $this->assertModules(array('content_translation', 'language'), TRUE); - // Assert that the language tables were enabled. - $this->assertTableCount('language', TRUE); + // Assert that the language YAML files were created. + $storage = $this->container->get('config.storage'); + $this->assertTrue(count($storage->listAll('language.entity.')) > 0, 'Language config entity files exist.'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php index 506ec4c..c0e732e 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php @@ -42,6 +42,21 @@ public function testLanguageUpgrade() { db_update('users')->fields(array('language' => 'ca'))->condition('uid', '1')->execute(); $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.'); + // Check that the configuration for the 'Catalan' language is correct. + $config = $this->container->get('config.factory')->get('language.entity.ca')->get(); + // We cannot predict the value of the UUID, we just check it's present. + $this->assertFalse(empty($config['uuid'])); + unset($config['uuid']); + $this->assertEqual($config, array( + 'id' => 'ca', + 'label' => 'Catalan', + 'direction' => 0, + 'weight' => 0, + 'locked' => 0, + 'status' => 1, + 'langcode' => Language::LANGCODE_NOT_SPECIFIED, + )); + // Ensure Catalan was properly upgraded to be the new default language. $this->assertTrue(language_default()->langcode == 'ca', 'Catalan is the default language'); $languages = language_list(Language::STATE_ALL); diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardPluginBaseUnitTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardPluginBaseUnitTest.php index b191e9a..4c5f95c 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardPluginBaseUnitTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardPluginBaseUnitTest.php @@ -43,8 +43,8 @@ public static function getInfo() { protected function setUp() { parent::setUp(); - $this->installSchema('language', 'language'); $this->installSchema('system', 'variable'); + $this->installConfig(array('language')); $this->enableModules(array('views_ui'));