diff --git a/core/modules/language/language.install b/core/modules/language/language.install index 37f41d69cdb47e57f8aa22444bdb45b637e8bc29..2884d4bb5811a521e070d97de596cc431069ba3b 100644 --- a/core/modules/language/language.install +++ b/core/modules/language/language.install @@ -102,3 +102,22 @@ function language_schema() { ); return $schema; } + +/** + * Implements hook_enable(). + */ +function language_enable() { + // Update the language count, if the module was disabled before, the + // language_count variable was forced to 1. + language_update_count(); +} + +/** + * Implements hook_disable(). + */ +function language_disable() { + // Force the language_count variable to be 1, so that the when checking if the + // site is multilingual (for example in language_multilingual()), the result + // will be FALSE, because the language module is disabled. + variable_set('language_count', 1); +} diff --git a/core/modules/language/language.module b/core/modules/language/language.module index 3fb39e0535f97df5106a52ed04590887c6d37a97..d7d9c7176deb72d48b37b98df0733ab99a6b4ff5 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -157,6 +157,48 @@ function language_theme() { ); } +/** + * Implements hook_element_info_alter(). + */ +function language_element_info_alter(&$type) { + // Alter the language_select element so that it will be rendered like a select + // field. + if (isset($type['language_select'])) { + if (!isset($type['language_select']['#process'])) { + $type['language_select']['#process'] = array(); + } + if (!isset($type['language_select']['#theme_wrappers'])) { + $type['language_select']['#theme_wrappers'] = array(); + } + $type['language_select']['#process'] = array_merge($type['language_select']['#process'], array('language_process_language_select', 'form_process_select', 'ajax_process_form')); + $type['language_select']['#theme'] = 'select'; + $type['language_select']['#theme_wrappers'] = array_merge($type['language_select']['#theme_wrappers'], array('form_element')); + $type['language_select']['#languages'] = LANGUAGE_CONFIGURABLE; + $type['language_select']['#multiple'] = FALSE; + } +} + +/** + * Processes a language select list form element. + * + * @param array $element + * The form element to process. + * + * @return array $element + * The processed form element. + */ +function language_process_language_select($element) { + // Don't set the options if another module(translation for example) already + // set the options. + if (!isset($element['#options'])) { + $element['#options'] = array(); + foreach (language_list($element['#languages']) as $langcode => $language) { + $element['#options'][$langcode] = $language->locked ? t('- @name -', array('@name' => $language->name)) : $language->name; + } + } + return $element; +} + /** * API function to add or update a language. * @@ -197,7 +239,7 @@ function language_save($language) { } // Update language count based on unlocked language count. - variable_set('language_count', db_query('SELECT COUNT(langcode) FROM {language} WHERE locked = 0')->fetchField()); + language_update_count(); // Kill the static cache in language_list(). drupal_static_reset('language_list'); @@ -205,6 +247,17 @@ function language_save($language) { return $language; } +/** + * Updates the language_count variable. + * + * This is used to check if a site is multilingual or not. + * + * @see language_multilingual() + */ +function language_update_count() { + variable_set('language_count', db_query('SELECT COUNT(langcode) FROM {language} WHERE locked = 0')->fetchField()); +} + /** * Delete a language. * @@ -225,7 +278,7 @@ function language_delete($langcode) { ->condition('langcode', $language->langcode) ->execute(); - variable_set('language_count', variable_get('language_count', 1) - 1); + language_update_count(); drupal_static_reset('language_list'); diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php index ccf8daa3277eb03c50c42fc753573c48b23cbd08..c5763e8ec479dc3629f5248fdd48a13201e0a346 100644 --- a/core/modules/node/lib/Drupal/node/NodeFormController.php +++ b/core/modules/node/lib/Drupal/node/NodeFormController.php @@ -97,28 +97,13 @@ public function form(array $form, array &$form_state, EntityInterface $node) { $form['title']['#weight'] = -5; } - if (module_exists('language')) { - $languages = language_list(LANGUAGE_ALL); - $language_options = array(); - foreach ($languages as $langcode => $language) { - // Make locked languages appear special in the list. - $language_options[$langcode] = $language->locked ? t('- @name -', array('@name' => $language->name)) : $language->name; - } - - $form['langcode'] = array( - '#type' => 'select', - '#title' => t('Language'), - '#default_value' => $node->langcode, - '#options' => $language_options, - '#access' => !variable_get('node_type_language_hidden_' . $node->type, TRUE), - ); - } - else { - $form['langcode'] = array( - '#type' => 'value', - '#value' => $node->langcode, - ); - } + $form['langcode'] = array( + '#title' => t('Language'), + '#type' => 'language_select', + '#default_value' => $node->langcode, + '#languages' => LANGUAGE_ALL, + '#access' => !variable_get('node_type_language_hidden_' . $node->type, TRUE), + ); $form['additional_settings'] = array( '#type' => 'vertical_tabs', diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/LanguageSelectElementTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/LanguageSelectElementTest.php new file mode 100644 index 0000000000000000000000000000000000000000..df103c899f4dfabb7b428b3d62a2bc6190aa74e2 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Form/LanguageSelectElementTest.php @@ -0,0 +1,120 @@ + 'Language select form element', + 'description' => 'Checks that the language select form element prints and submits the right options.', + 'group' => 'Form API', + ); + } + + /** + * Tests that the options printed by the language select element are correct. + */ + function testLanguageSelectElementOptions() { + // Add some languages. + $language = (object) array( + 'langcode' => 'aaa', + 'name' => $this->randomName(), + ); + language_save($language); + + $language = (object) array( + 'langcode' => 'bbb', + 'name' => $this->randomName(), + ); + language_save($language); + + $this->drupalGet('form-test/language_select'); + // Check that the language fields were rendered on the page. + $ids = array('edit-languages-all' => LANGUAGE_ALL, + 'edit-languages-configurable' => LANGUAGE_CONFIGURABLE, + 'edit-languages-locked' => LANGUAGE_LOCKED, + 'edit-languages-config-and-locked' => LANGUAGE_CONFIGURABLE | LANGUAGE_LOCKED); + foreach ($ids as $id => $flags) { + $this->assertField($id, t('The @id field was found on the page.', array('@id' => $id))); + $options = array(); + foreach (language_list($flags) as $langcode => $language) { + $options[$langcode] = $language->locked ? t('- @name -', array('@name' => $language->name)) : $language->name; + } + $this->_testLanguageSelectElementOptions($id, $options); + } + + // Test that the #options were not altered by #languages. + $this->assertField('edit-language-custom-options', t('The @id field was found on the page.', array('@id' => 'edit-language-custom-options'))); + $this->_testLanguageSelectElementOptions('edit-language-custom-options', array('opt1' => 'First option', 'opt2' => 'Second option', 'opt3' => 'Third option')); + } + + /** + * Tests the case when the language select elements should not be printed. + * + * This happens when the language module is disabled. + */ + function testHiddenLanguageSelectElement() { + // Disable the language module, so that the language select field will not + // be rendered. + module_disable(array('language')); + $this->drupalGet('form-test/language_select'); + // Check that the language fields were rendered on the page. + $ids = array('edit-languages-all', 'edit-languages-configurable', 'edit-languages-locked', 'edit-languages-config-and-locked'); + foreach ($ids as $id) { + $this->assertNoField($id, t('The @id field was not found on the page.', array('@id' => $id))); + } + + // Check that the submitted values were the default values of the language + // field elements. + $edit = array(); + $this->drupalPost(NULL, $edit, t('Submit')); + $values = drupal_json_decode($this->drupalGetContent()); + $this->assertEqual($values['languages_all'], 'xx'); + $this->assertEqual($values['languages_configurable'], 'en'); + $this->assertEqual($values['languages_locked'], LANGUAGE_NOT_SPECIFIED); + $this->assertEqual($values['languages_config_and_locked'], 'dummy_value'); + $this->assertEqual($values['language_custom_options'], 'opt2'); + } + + /** + * Helper function to check the options of a language select form element. + * + * @param string $id + * The id of the language select element to check. + * + * @param array $options + * An array with options to compare with. + */ + protected function _testLanguageSelectElementOptions($id, $options) { + // Check that the options in the language field are exactly the same, + // including the order, as the languages sent as a parameter. + $elements = $this->xpath("//select[@id='" . $id . "']"); + $count = 0; + foreach ($elements[0]->option as $option) { + $count++; + $option_title = current($options); + $this->assertEqual((string) $option, $option_title); + next($options); + } + $this->assertEqual($count, count($options), t('The number of languages and the number of options shown by the language element are the same: @languages languages, @number options', array('@languages' => count($options), '@number' => $count))); + } +} diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 3c434aea35ff0a444a81e400fc022df96ca130b9..523c43331c2e89d130b9b0a9fcc09a352ad33944 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -504,6 +504,10 @@ function system_element_info() { '#theme' => 'select', '#theme_wrappers' => array('form_element'), ); + $types['language_select'] = array( + '#input' => TRUE, + '#default_value' => LANGUAGE_NOT_SPECIFIED, + ); $types['weight'] = array( '#input' => TRUE, '#delta' => 10, diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module index 44fc0ebd1c3ad59bc3bdb146750a2e3a2b390da7..dea57f7c65b01c40759f8f2481fc9fa7c5ad38dc 100644 --- a/core/modules/system/tests/modules/form_test/form_test.module +++ b/core/modules/system/tests/modules/form_test/form_test.module @@ -133,6 +133,12 @@ function form_test_menu() { 'page arguments' => array('form_test_select'), 'access callback' => TRUE, ); + $items['form-test/language_select'] = array( + 'title' => t('Language Select'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_language_select'), + 'access callback' => TRUE, + ); $items['form-test/placeholder-text'] = array( 'title' => 'Placeholder', 'page callback' => 'drupal_get_form', @@ -1223,6 +1229,42 @@ function form_test_select($form, &$form_state) { return $form; } +/** + * Builds a form to test the language select form element. + */ +function form_test_language_select() { + $form['#submit'] = array('_form_test_submit_values_json'); + + $form['languages_all'] = array( + '#type' => 'language_select', + '#languages' => LANGUAGE_ALL, + '#default_value' => 'xx', + ); + $form['languages_configurable'] = array( + '#type' => 'language_select', + '#languages' => LANGUAGE_CONFIGURABLE, + '#default_value' => 'en', + ); + $form['languages_locked'] = array( + '#type' => 'language_select', + '#languages' => LANGUAGE_LOCKED, + ); + $form['languages_config_and_locked'] = array( + '#type' => 'language_select', + '#languages' => LANGUAGE_CONFIGURABLE | LANGUAGE_LOCKED, + '#default_value' => 'dummy_value', + ); + $form['language_custom_options'] = array( + '#type' => 'language_select', + '#languages' => LANGUAGE_CONFIGURABLE | LANGUAGE_LOCKED, + '#options' => array('opt1' => 'First option', 'opt2' => 'Second option', 'opt3' => 'Third option'), + '#default_value' => 'opt2', + ); + + $form['submit'] = array('#type' => 'submit', '#value' => 'Submit'); + return $form; +} + /** * Builds a form to test #type 'number' and 'range' validation. * diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php index f8c25927f85a3b5eea64ee09686f0e6ce93962cb..dfa1c3fe0e5009c21a9bfeef1b02fb42913cb949 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php @@ -42,6 +42,13 @@ public function form(array $form, array &$form_state, EntityInterface $term) { '#weight' => 0, ); + $form['langcode'] = array( + '#type' => 'language_select', + '#title' => t('Language'), + '#languages' => LANGUAGE_ALL, + '#default_value' => $term->langcode, + ); + $form['vocabulary_machine_name'] = array( '#type' => 'value', '#value' => isset($term->vocabulary_machine_name) ? $term->vocabulary_machine_name : $vocabulary->name, diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermLanguageTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermLanguageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..75bcbcef7d15d7d64a3b17f049a0c3302f9c475b --- /dev/null +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermLanguageTest.php @@ -0,0 +1,76 @@ + 'Taxonomy term language', + 'description' => 'Tests the language functionality for the taxonomy terms.', + 'group' => 'Taxonomy', + ); + } + + function setUp() { + parent::setUp(); + + // Create an administrative user. + $this->admin_user = $this->drupalCreateUser(array('administer taxonomy')); + $this->drupalLogin($this->admin_user); + + // Create a vocabulary to which the terms will be assigned. + $this->vocabulary = $this->createVocabulary(); + } + + function testTermLanguage() { + // Add first some custom languages. + $language = (object) array( + 'langcode' => 'aa', + 'name' => $this->randomName(), + ); + language_save($language); + + $language = (object) array( + 'langcode' => 'bb', + 'name' => $this->randomName(), + ); + language_save($language); + + // Add a term. + $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add'); + // Check that we have the language selector. + $this->assertField('edit-langcode', t('The language selector field was found on the page')); + // Submit the term. + $edit = array( + 'name' => $this->randomName(), + 'langcode' => 'aa', + ); + $this->drupalPost(NULL, $edit, t('Save')); + $terms = taxonomy_term_load_multiple_by_name($edit['name']); + $term = reset($terms); + $this->assertEqual($term->langcode, $edit['langcode']); + + // Check if on the edit page the language is correct. + $this->drupalGet('taxonomy/term/' . $term->tid . '/edit'); + $this->assertOptionSelected('edit-langcode', $edit['langcode'], t('The term language was correctly selected.')); + + // Change the language of the term. + $edit['langcode'] = 'bb'; + $this->drupalPost('taxonomy/term/' . $term->tid . '/edit', $edit, t('Save')); + + // Check again that on the edit page the language is correct. + $this->drupalGet('taxonomy/term/' . $term->tid . '/edit'); + $this->assertOptionSelected('edit-langcode', $edit['langcode'], t('The term language was correctly selected.')); + } +} diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyLanguageTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyLanguageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..41dfa69e3849d455721705199ae4ce1c30319404 --- /dev/null +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyLanguageTest.php @@ -0,0 +1,71 @@ + 'Vocabulary language', + 'description' => 'Tests the language functionality for vocabularies.', + 'group' => 'Taxonomy', + ); + } + + function setUp() { + parent::setUp(); + + // Create an administrative user. + $this->admin_user = $this->drupalCreateUser(array('administer taxonomy')); + $this->drupalLogin($this->admin_user); + } + + function testVocabularyLanguage() { + // Add first some custom languages. + $language = (object) array( + 'langcode' => 'aa', + 'name' => $this->randomName(), + ); + language_save($language); + + $language = (object) array( + 'langcode' => 'bb', + 'name' => $this->randomName(), + ); + language_save($language); + + $this->drupalGet('admin/structure/taxonomy/add'); + // Check that we have the language selector available. + $this->assertField('edit-langcode', t('The language selector field was found on the page')); + + // Create the vocabulary. + $machine_name = drupal_strtolower($this->randomName()); + $edit['name'] = $this->randomName(); + $edit['description'] = $this->randomName(); + $edit['langcode'] = 'aa'; + $edit['machine_name'] = $machine_name; + $this->drupalPost(NULL, $edit, t('Save')); + + // Check the language on the edit page. + $this->drupalGet('admin/structure/taxonomy/' . $machine_name . '/edit'); + $this->assertOptionSelected('edit-langcode', $edit['langcode'], t('The vocabulary language was correctly selected.')); + + // Change the language and save again. + $edit['langcode'] = 'bb'; + $this->drupalPost(NULL, $edit, t('Save')); + + // Check again the language on the edit page. + $this->drupalGet('admin/structure/taxonomy/' . $machine_name . '/edit'); + $this->assertOptionSelected('edit-langcode', $edit['langcode'], t('The vocabulary language was correctly selected.')); + } +} diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php index 1a1f8820a3ebd662dcfd8efd0885639eaee29c02..667041e77fa0947ba937b8adabe5a368d7a4ee01 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php @@ -44,6 +44,12 @@ public function form(array $form, array &$form_state, EntityInterface $vocabular '#title' => t('Description'), '#default_value' => $vocabulary->description, ); + $form['langcode'] = array( + '#type' => 'language_select', + '#title' => t('Language'), + '#languages' => LANGUAGE_ALL, + '#default_value' => $vocabulary->langcode, + ); // Set the hierarchy to "multiple parents" by default. This simplifies the // vocabulary form and standardizes the term form. $form['hierarchy'] = array( diff --git a/core/modules/user/lib/Drupal/user/AccountFormController.php b/core/modules/user/lib/Drupal/user/AccountFormController.php index 31acf0d031cdcee94fee339daab678eb572a82c2..31ca843b3285ac28453fc83172b6e126b7fad5d9 100644 --- a/core/modules/user/lib/Drupal/user/AccountFormController.php +++ b/core/modules/user/lib/Drupal/user/AccountFormController.php @@ -203,45 +203,26 @@ public function form(array $form, array &$form_state, EntityInterface $account) $form['#validate'][] = 'user_validate_picture'; - if (module_exists('language') && language_multilingual()) { - $languages = language_list(); - - // If the user is being created, we set the user language to the page language. - $user_preferred_language = $register ? $language_interface : user_preferred_language($account); - - $names = array(); - foreach ($languages as $langcode => $item) { - $names[$langcode] = $item->name; - } - - // Is default the interface language? - $interface_language_is_default = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_DEFAULT; - $form['language'] = array( - '#type' => 'fieldset', - '#title' => t('Language settings'), - // Display language selector when either creating a user on the admin - // interface or editing a user account. - '#access' => !$register || user_access('administer users'), - ); - - $form['language']['preferred_langcode'] = array( - '#type' => (count($names) <= 5 ? 'radios' : 'select'), - '#title' => t('Language'), - '#default_value' => $user_preferred_language->langcode, - '#options' => $names, - '#description' => $interface_language_is_default ? t("This account's preferred language for e-mails and site presentation.") : t("This account's preferred language for e-mails."), - ); - } - else { - $form['language'] = array( - '#type' => 'container', - ); + $user_preferred_language = $register ? $language_interface : user_preferred_language($account); + + // Is default the interface language? + include_once DRUPAL_ROOT . '/core/includes/language.inc'; + $interface_language_is_default = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_DEFAULT; + $form['language'] = array( + '#type' => language_multilingual() ? 'fieldset' : 'container', + '#title' => t('Language settings'), + // Display language selector when either creating a user on the admin + // interface or editing a user account. + '#access' => !$register || user_access('administer users'), + ); - $form['language']['preferred_langcode'] = array( - '#type' => 'value', - '#value' => language_default()->langcode, - ); - } + $form['language']['preferred_langcode'] = array( + '#type' => 'language_select', + '#title' => t('Language'), + '#languages' => LANGUAGE_CONFIGURABLE, + '#default_value' => $user_preferred_language->langcode, + '#description' => $interface_language_is_default ? t("This account's preferred language for e-mails and site presentation.") : t("This account's preferred language for e-mails."), + ); // User entities contain both a langcode property (for identifying the // language of the entity data) and a preferred_langcode property (see diff --git a/core/modules/user/lib/Drupal/user/Tests/UserLanguageCreationTest.php b/core/modules/user/lib/Drupal/user/Tests/UserLanguageCreationTest.php index b90d26d6562b7a7702c0299396ffda97d016a9dc..37ffef7af1bc9207930e291bc4e67226006c761c 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserLanguageCreationTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserLanguageCreationTest.php @@ -62,7 +62,7 @@ function testLocalUserCreation() { // Check if the language selector is available on admin/people/create and // set to the currently active language. $this->drupalGet($langcode . '/admin/people/create'); - $this->assertFieldChecked("edit-preferred-langcode-$langcode", t('Global language set in the language selector.')); + $this->assertOptionSelected("edit-preferred-langcode", $langcode, t('Global language set in the language selector.')); // Create a user with the admin/people/create form and check if the correct // language is set. @@ -104,7 +104,7 @@ function testLocalUserCreation() { $this->drupalLogin($admin_user); $this->drupalGet($user_edit); - $this->assertFieldChecked("edit-preferred-langcode-$langcode", t('Language selector is accessible and correct language is selected.')); + $this->assertOptionSelected("edit-preferred-langcode", $langcode, t('Language selector is accessible and correct language is selected.')); // Set pass_raw so we can login the new user. $user->pass_raw = $this->randomName(10); @@ -117,6 +117,6 @@ function testLocalUserCreation() { $this->drupalLogin($user); $this->drupalGet($user_edit); - $this->assertFieldChecked("edit-preferred-langcode-$langcode", t('Language selector is accessible and correct language is selected.')); + $this->assertOptionSelected("edit-preferred-langcode", $langcode, t('Language selector is accessible and correct language is selected.')); } } diff --git a/core/modules/user/lib/Drupal/user/Tests/UserLanguageTest.php b/core/modules/user/lib/Drupal/user/Tests/UserLanguageTest.php index 08dc9f558682f90c02ce8eef2c7e34ba2493a4b4..f0155b69bb281bc5267351de16103b39d76235e0 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserLanguageTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserLanguageTest.php @@ -71,8 +71,7 @@ function testUserLanguageConfiguration() { // Ensure form was submitted successfully. $this->assertText(t('The changes have been saved.'), t('Changes were saved.')); // Check if language was changed. - $elements = $this->xpath('//input[@id=:id]', array(':id' => 'edit-preferred-langcode-' . $langcode)); - $this->assertTrue(isset($elements[0]) && !empty($elements[0]['checked']), t('Default language successfully updated.')); + $this->assertOptionSelected('edit-preferred-langcode', $langcode, t('Default language successfully updated.')); $this->drupalLogout(); }