diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 42de880..5aa0d17 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -4,6 +4,7 @@ use Drupal\Core\Database\Database; use Symfony\Component\ClassLoader\UniversalClassLoader; use Symfony\Component\ClassLoader\ApcUniversalClassLoader; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Language\Language; /** * @file @@ -207,11 +208,6 @@ const LANGUAGE_NOT_APPLICABLE = 'zxx'; const LANGUAGE_MULTIPLE = 'mul'; /** - * The type of language used to define the content language. - */ -const LANGUAGE_TYPE_CONTENT = 'language_content'; - -/** * The type of language used to select the user interface. */ const LANGUAGE_TYPE_INTERFACE = 'language_interface'; @@ -2532,7 +2528,6 @@ function drupal_language_initialize() { // Remove after these issues: // - $language_interface: http://drupal.org/node/1510686 // - $language_url: http://drupal.org/node/1512310 - // - $language_content: http://drupal.org/node/1512308 foreach ($types as $type) { $GLOBALS[$type] = $container->get($type); } @@ -2554,7 +2549,7 @@ function language_types_get_all() { function language_types_get_default() { return array( LANGUAGE_TYPE_INTERFACE => TRUE, - LANGUAGE_TYPE_CONTENT => FALSE, + Language::CONTENT => FALSE, LANGUAGE_TYPE_URL => FALSE, ); } diff --git a/core/includes/common.inc b/core/includes/common.inc index 19e0d59..0fa6037 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -1,6 +1,7 @@ get(LANGUAGE_TYPE_CONTENT)->langcode; + $langcode = $langcode ? $langcode : drupal_container()->get(Language::CONTENT)->langcode; $output = "\n"; $output .= ' ' . check_plain($title) . "\n"; diff --git a/core/includes/language.inc b/core/includes/language.inc index 8e5f1ac..f183617 100644 --- a/core/includes/language.inc +++ b/core/includes/language.inc @@ -5,6 +5,8 @@ * Multiple language handling functionality. */ +use Drupal\Core\Language\Language; + /** * No language negotiation. The default language is used. */ @@ -420,7 +422,7 @@ function language_url_split_prefix($path, $languages) { * @return * An array of language codes. */ -function language_fallback_get_candidates($type = LANGUAGE_TYPE_CONTENT) { +function language_fallback_get_candidates($type = Language::CONTENT) { $fallback_candidates = &drupal_static(__FUNCTION__); if (!isset($fallback_candidates)) { diff --git a/core/lib/Drupal/Core/Language/Language.php b/core/lib/Drupal/Core/Language/Language.php index 9b9223d..c546712 100644 --- a/core/lib/Drupal/Core/Language/Language.php +++ b/core/lib/Drupal/Core/Language/Language.php @@ -17,6 +17,12 @@ namespace Drupal\Core\Language; * @see language_default() */ class Language { + + /** + * The type of language used to define the content language. + */ + const CONTENT = 'language_content'; + // Properties within the Language are set up as the default language. public $name = 'English'; public $langcode = 'en'; diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module old mode 100644 new mode 100755 index 58ea4a6..66ae314 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -10,6 +10,7 @@ */ use Drupal\node\Node; +use Drupal\Core\Language\Language; /** * Comment is awaiting approval. @@ -949,7 +950,7 @@ function comment_prepare_thread(&$comments) { */ function comment_view(Comment $comment, Node $node, $view_mode = 'full', $langcode = NULL) { if (!isset($langcode)) { - $langcode = drupal_container()->get(LANGUAGE_TYPE_CONTENT)->langcode; + $langcode = drupal_container()->get(Language::CONTENT)->langcode; } // Populate $comment->content with a render() array. @@ -1016,7 +1017,7 @@ function comment_view(Comment $comment, Node $node, $view_mode = 'full', $langco */ function comment_build_content(Comment $comment, Node $node, $view_mode = 'full', $langcode = NULL) { if (!isset($langcode)) { - $langcode = drupal_container()->get(LANGUAGE_TYPE_CONTENT)->langcode; + $langcode = drupal_container()->get(Language::CONTENT)->langcode; } // Remove previously built content, if exists. @@ -1673,7 +1674,8 @@ function comment_forms() { * @ingroup forms */ function comment_form($form, &$form_state, Comment $comment) { - global $user, $language_content; + global $user; + $language_content = drupal_container()->get(Language::CONTENT); // During initial form build, add the comment entity to the form state for // use during form building and processing. During a rebuild, use what is in diff --git a/core/modules/field/field.multilingual.inc b/core/modules/field/field.multilingual.inc index f054bf1..5052928 100644 --- a/core/modules/field/field.multilingual.inc +++ b/core/modules/field/field.multilingual.inc @@ -5,6 +5,8 @@ * Functions implementing Field API multilingual support. */ +use Drupal\Core\Language\Language; + /** * @defgroup field_language Field Language API * @{ @@ -247,7 +249,7 @@ function field_valid_language($langcode, $default = TRUE) { if (in_array($langcode, $enabled_languages)) { return $langcode; } - global $language_content; + $language_content = drupal_container()->get(Language::CONTENT); return $default ? language_default()->langcode : $language_content->langcode; } diff --git a/core/modules/language/language.module b/core/modules/language/language.module index 7acc783..02324f2 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -5,6 +5,8 @@ * Add language handling functionality to Drupal. */ +use Drupal\Core\Language\Language; + /** * Implements hook_help(). */ @@ -276,7 +278,7 @@ function language_language_types_info() { 'name' => t('User interface text'), 'description' => t('Order of language detection methods for user interface text. If a translation of user interface text is available in the detected language, it will be displayed.'), ), - LANGUAGE_TYPE_CONTENT => array( + Language::CONTENT => array( 'name' => t('Content'), 'description' => t('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'), 'fixed' => array(LANGUAGE_NEGOTIATION_INTERFACE), @@ -296,7 +298,7 @@ function language_language_negotiation_info() { $negotiation_info = array(); $negotiation_info[LANGUAGE_NEGOTIATION_URL] = array( - 'types' => array(LANGUAGE_TYPE_CONTENT, LANGUAGE_TYPE_INTERFACE, LANGUAGE_TYPE_URL), + 'types' => array(Language::CONTENT, LANGUAGE_TYPE_INTERFACE, LANGUAGE_TYPE_URL), 'callbacks' => array( 'negotiation' => 'language_from_url', 'language_switch' => 'language_switcher_url', @@ -340,7 +342,7 @@ function language_language_negotiation_info() { ); $negotiation_info[LANGUAGE_NEGOTIATION_INTERFACE] = array( - 'types' => array(LANGUAGE_TYPE_CONTENT), + 'types' => array(Language::CONTENT), 'callbacks' => array('negotiation' => 'language_from_interface'), 'file' => $file, 'weight' => 8, diff --git a/core/modules/language/tests/language_test.module b/core/modules/language/tests/language_test.module old mode 100644 new mode 100755 index cf8f39c..1588887 --- a/core/modules/language/tests/language_test.module +++ b/core/modules/language/tests/language_test.module @@ -5,6 +5,8 @@ * Mock module for language layer tests. */ +use Drupal\Core\Language\Language; + /** * Implements hook_boot(). * @@ -47,9 +49,9 @@ function language_test_language_types_info() { /** * Implements hook_language_types_info_alter(). */ -function language_test_language_types_info_alter(array &$language_types) { - if (variable_get('language_test_content_language_type', FALSE)) { - unset($language_types[LANGUAGE_TYPE_CONTENT]['fixed']); +function locale_test_language_types_info_alter(array &$language_types) { + if (variable_get('locale_test_content_language_type', FALSE)) { + unset($language_types[Language::CONTENT]['fixed']); } } @@ -70,7 +72,7 @@ function language_test_language_negotiation_info() { return array( 'test_language_negotiation_method' => array( 'name' => t('Test'), - 'types' => array(LANGUAGE_TYPE_CONTENT, 'test_language_type', 'fixed_test_language_type'), + 'types' => array(Language::CONTENT, 'test_language_type', 'fixed_test_language_type'), ) + $info, 'test_language_negotiation_method_ts' => array( 'name' => t('Type-specific test'), diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install index b29bdee..5f6bfef 100644 --- a/core/modules/locale/locale.install +++ b/core/modules/locale/locale.install @@ -5,6 +5,8 @@ * Install, update and uninstall functions for the locale module. */ +use Drupal\Core\Language\Language; + /** * Implements hook_uninstall(). */ @@ -448,7 +450,7 @@ function locale_update_8007() { // is not available its functionality is rebuild. $language_types = variable_get('language_types', array( LANGUAGE_TYPE_INTERFACE => TRUE, - LANGUAGE_TYPE_CONTENT => FALSE, + Language::CONTENT => FALSE, LANGUAGE_TYPE_URL => FALSE, )); foreach ($language_types as $language_type => $configurable) { diff --git a/core/modules/locale/locale.test b/core/modules/locale/locale.test old mode 100644 new mode 100755 index 5e252bf..348d8bf --- a/core/modules/locale/locale.test +++ b/core/modules/locale/locale.test @@ -20,7 +20,73 @@ * - a functional test fot language types/negotiation info. */ -use Drupal\simpletest\WebTestBase; +use Drupal\Core\Language\Language; + +/** + * Functional tests for language configuration's effect on negotiation setup. + */ +class LocaleConfigurationTest extends WebTestCase { + public static function getInfo() { + return array( + 'name' => 'Language negotiation autoconfiguration', + 'description' => 'Adds and configures languages to check negotiation changes.', + 'group' => 'Locale', + ); + } + + function setUp() { + parent::setUp('locale'); + } + + /** + * Functional tests for adding, editing and deleting languages. + */ + function testLanguageConfiguration() { + global $base_url; + + // User to add and remove language. + $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages')); + $this->drupalLogin($admin_user); + + // Check if the Default English language has no path prefix. + $this->drupalGet('admin/config/regional/language/detection/url'); + $this->assertFieldByXPath('//input[@name="prefix[en]"]', '', t('Default English has no path prefix.')); + + // Add predefined language. + $edit = array( + 'predefined_langcode' => 'fr', + ); + $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); + $this->assertText('French'); + $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.')); + + // Check if the Default English language has no path prefix. + $this->drupalGet('admin/config/regional/language/detection/url'); + $this->assertFieldByXPath('//input[@name="prefix[en]"]', '', t('Default English has no path prefix.')); + // Check if French has a path prefix. + $this->drupalGet('admin/config/regional/language/detection/url'); + $this->assertFieldByXPath('//input[@name="prefix[fr]"]', 'fr', t('French has a path prefix.')); + + // Check if we can change the default language. + $this->drupalGet('admin/config/regional/language'); + $this->assertFieldChecked('edit-site-default-en', t('English is the default language.')); + // Change the default language. + $edit = array( + 'site_default' => 'fr', + ); + $this->drupalPost(NULL, $edit, t('Save configuration')); + $this->assertNoFieldChecked('edit-site-default-en', t('Default language updated.')); + $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.')); + + // Check if a valid language prefix is added afrer changing the default + // language. + $this->drupalGet('admin/config/regional/language/detection/url'); + $this->assertFieldByXPath('//input[@name="prefix[en]"]', 'en', t('A valid path prefix has been added to the previous default language.')); + // Check if French still has a path prefix. + $this->drupalGet('admin/config/regional/language/detection/url'); + $this->assertFieldByXPath('//input[@name="prefix[fr]"]', 'fr', t('French still has a path prefix.')); + } +} /** * Functional tests for JavaScript parsing for translatable strings. @@ -1550,7 +1616,7 @@ class LocaleUninstallFunctionalTest extends WebTestBase { drupal_load('module', 'locale'); variable_set('language_types', language_types_get_default() + array('language_custom' => TRUE)); variable_set('language_negotiation_' . LANGUAGE_TYPE_INTERFACE, language_language_negotiation_info()); - variable_set('language_negotiation_' . LANGUAGE_TYPE_CONTENT, language_language_negotiation_info()); + variable_set('language_negotiation_' . Language::CONTENT, language_language_negotiation_info()); variable_set('language_negotiation_' . LANGUAGE_TYPE_URL, language_language_negotiation_info()); // Change language negotiation settings. @@ -1580,7 +1646,7 @@ class LocaleUninstallFunctionalTest extends WebTestBase { $this->assertTrue(count(language_types_get_all()) == count(language_types_get_default()), t('Language types reset')); $language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) == LANGUAGE_NEGOTIATION_DEFAULT; $this->assertTrue($language_negotiation, t('Interface language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set')))); - $language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_CONTENT) == LANGUAGE_NEGOTIATION_DEFAULT; + $language_negotiation = language_negotiation_method_get_first(Language::CONTENT) == LANGUAGE_NEGOTIATION_DEFAULT; $this->assertTrue($language_negotiation, t('Content language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set')))); $language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_URL) == LANGUAGE_NEGOTIATION_DEFAULT; $this->assertTrue($language_negotiation, t('URL language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set')))); @@ -2050,7 +2116,7 @@ class LocaleMultilingualFieldsFunctionalTest extends WebTestBase { $this->assertTrue($assert, t('Field language correctly changed.')); // Enable content language URL detection. - language_negotiation_set(LANGUAGE_TYPE_CONTENT, array(LANGUAGE_NEGOTIATION_URL => 0)); + language_negotiation_set(Language::CONTENT, array(LANGUAGE_NEGOTIATION_URL => 0)); // Test multilingual field language fallback logic. $this->drupalGet("it/node/$node->nid"); @@ -2261,3 +2327,158 @@ class LocaleDateFormatsFunctionalTest extends WebTestBase { $this->assertText($french_date, t('French date format appears')); } } + +/** + * Functional test for language types/negotiation info. + */ +class LocaleLanguageNegotiationInfoFunctionalTest extends WebTestCase { + + public static function getInfo() { + return array( + 'name' => 'Language negotiation info', + 'description' => 'Tests alterations to language types/negotiation info.', + 'group' => 'Locale', + ); + } + + function setUp() { + parent::setUp('locale'); + require_once DRUPAL_ROOT .'/core/includes/language.inc'; + $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'view the administration theme')); + $this->drupalLogin($admin_user); + $this->drupalPost('admin/config/regional/language/add', array('predefined_langcode' => 'it'), t('Add language')); + } + + /** + * Tests alterations to language types/negotiation info. + */ + function testInfoAlterations() { + // Enable language type/negotiation info alterations. + variable_set('locale_test_language_types', TRUE); + variable_set('locale_test_language_negotiation_info', TRUE); + $this->languageNegotiationUpdate(); + + // Check that fixed language types are properly configured without the need + // of saving the language negotiation settings. + $this->checkFixedLanguageTypes(); + + // Make the content language type configurable by updating the language + // negotiation settings with the proper flag enabled. + variable_set('locale_test_content_language_type', TRUE); + $this->languageNegotiationUpdate(); + $type = Language::CONTENT; + $language_types = variable_get('language_types', language_types_get_default()); + $this->assertTrue($language_types[$type], t('Content language type is configurable.')); + + // Enable some core and custom language negotiation methods. The test + // language type is supposed to be configurable. + $test_type = 'test_language_type'; + $interface_method_id = LANGUAGE_NEGOTIATION_INTERFACE; + $test_method_id = 'test_language_negotiation_method'; + $form_field = $type . '[enabled]['. $interface_method_id .']'; + $edit = array( + $form_field => TRUE, + $type . '[enabled][' . $test_method_id . ']' => TRUE, + $test_type . '[enabled][' . $test_method_id . ']' => TRUE, + ); + $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings')); + + // Remove the interface language negotiation method by updating the language + // negotiation settings with the proper flag enabled. + variable_set('locale_test_language_negotiation_info_alter', TRUE); + $this->languageNegotiationUpdate(); + $negotiation = variable_get("language_negotiation_$type", array()); + $this->assertFalse(isset($negotiation[$interface_method_id]), t('Interface language negotiation method removed from the stored settings.')); + $this->assertNoFieldByXPath("//input[@name=\"$form_field\"]", NULL, t('Interface language negotiation method unavailable.')); + + // Check that type-specific language negotiation methods can be assigned + // only to the corresponding language types. + foreach (language_types_get_configurable() as $type) { + $form_field = $type . '[enabled][test_language_negotiation_method_ts]'; + if ($type == $test_type) { + $this->assertFieldByXPath("//input[@name=\"$form_field\"]", NULL, t('Type-specific test language negotiation method available for %type.', array('%type' => $type))); + } + else { + $this->assertNoFieldByXPath("//input[@name=\"$form_field\"]", NULL, t('Type-specific test language negotiation method unavailable for %type.', array('%type' => $type))); + } + } + + // Check language negotiation results. + $this->drupalGet(''); + $last = variable_get('locale_test_language_negotiation_last', array()); + foreach (language_types_get_all() as $type) { + $langcode = $last[$type]; + $value = $type == Language::CONTENT || strpos($type, 'test') !== FALSE ? 'it' : 'en'; + $this->assertEqual($langcode, $value, t('The negotiated language for %type is %language', array('%type' => $type, '%language' => $langcode))); + } + + // Disable locale_test and check that everything is set back to the original + // status. + $this->languageNegotiationUpdate('disable'); + + // Check that only the core language types are available. + foreach (language_types_get_all() as $type) { + $this->assertTrue(strpos($type, 'test') === FALSE, t('The %type language is still available', array('%type' => $type))); + } + + // Check that fixed language types are properly configured, even those + // previously set to configurable. + $this->checkFixedLanguageTypes(); + + // Check that unavailable language negotiation methods are not present in + // the negotiation settings. + $negotiation = variable_get("language_negotiation_$type", array()); + $this->assertFalse(isset($negotiation[$test_method_id]), t('The disabled test language negotiation method is not part of the content language negotiation settings.')); + + // Check that configuration page presents the correct options and settings. + $this->assertNoRaw(t('Test language detection'), t('No test language type configuration available.')); + $this->assertNoRaw(t('This is a test language negotiation method'), t('No test language negotiation method available.')); + } + + /** + * Update language types/negotiation information. + * + * Manually invoke locale_modules_enabled()/locale_modules_disabled() since + * they would not be invoked after enabling/disabling locale_test the first + * time. + */ + protected function languageNegotiationUpdate($op = 'enable') { + static $last_op = NULL; + $modules = array('locale_test'); + + // Enable/disable locale_test only if we did not already before. + if ($last_op != $op) { + $function = "module_{$op}"; + $function($modules); + // Reset hook implementation cache. + module_implements_reset(); + } + + drupal_static_reset('language_types_info'); + drupal_static_reset('language_negotiation_info'); + $function = "language_modules_{$op}d"; + if (function_exists($function)) { + $function($modules); + } + + $this->drupalGet('admin/config/regional/language/detection'); + } + + /** + * Check that language negotiation for fixed types matches the stored one. + */ + protected function checkFixedLanguageTypes() { + drupal_static_reset('language_types_info'); + foreach (language_types_info() as $type => $info) { + if (isset($info['fixed'])) { + $negotiation = variable_get("language_negotiation_$type", array()); + $equal = count($info['fixed']) == count($negotiation); + while ($equal && list($id) = each($negotiation)) { + list(, $info_id) = each($info['fixed']); + $equal = $info_id == $id; + } + $this->assertTrue($equal, t('language negotiation for %type is properly set up', array('%type' => $type))); + } + } + } +} diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 3c3881d..10b2e6d 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -4,6 +4,7 @@ use Drupal\Core\Database\Query\AlterableInterface; use Drupal\Core\Database\Query\SelectExtender; use Drupal\Core\Database\Query\SelectInterface; use Drupal\node\Node; +use Drupal\Core\Language\Language; /** * @file @@ -1130,7 +1131,7 @@ function node_revision_delete($revision_id) { */ function node_view(Node $node, $view_mode = 'full', $langcode = NULL) { if (!isset($langcode)) { - $langcode = $GLOBALS['language_content']->langcode; + $langcode = drupal_container()->get(Language::CONTENT)->langcode; } // Populate $node->content with a render() array. @@ -1192,7 +1193,7 @@ function node_view(Node $node, $view_mode = 'full', $langcode = NULL) { */ function node_build_content(Node $node, $view_mode = 'full', $langcode = NULL) { if (!isset($langcode)) { - $langcode = $GLOBALS['language_content']->langcode; + $langcode = drupal_container()->get(Language::CONTENT)->langcode; } // Remove previously built content, if exists. @@ -2377,7 +2378,8 @@ function node_block_list_alter(&$blocks) { * @see node_menu() */ function node_feed($nids = FALSE, $channel = array()) { - global $base_url, $language_content; + global $base_url; + $language_content = drupal_container()->get(Language::CONTENT); if ($nids === FALSE) { $nids = db_select('node', 'n') diff --git a/core/modules/system/language.api.php b/core/modules/system/language.api.php index 9f28619..b9334f4 100644 --- a/core/modules/system/language.api.php +++ b/core/modules/system/language.api.php @@ -50,11 +50,13 @@ function hook_language_init() { * The language type the links will switch. * @param $path * The current path. + * + * @see Drupal\Core\Language\Language */ function hook_language_switch_links_alter(array &$links, $type, $path) { global $language_interface; - if ($type == LANGUAGE_TYPE_CONTENT && isset($links[$language_interface->langcode])) { + if ($type == Language::CONTENT && isset($links[$language_interface->langcode])) { foreach ($links[$language_interface->langcode] as $link) { $link['attributes']['class'][] = 'active-language'; } diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module old mode 100644 new mode 100755 index 7fdb8a2..3f6b9da --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -6,8 +6,7 @@ */ use Drupal\node\Node; -use Drupal\taxonomy\Term; -use Drupal\taxonomy\Vocabulary; +use Drupal\Core\Language\Language; /** * Denotes that no term in the vocabulary has a parent. @@ -584,7 +583,7 @@ function taxonomy_term_delete_multiple(array $tids) { */ function taxonomy_term_view(Term $term, $view_mode = 'full', $langcode = NULL) { if (!isset($langcode)) { - $langcode = $GLOBALS['language_content']->langcode; + $langcode = drupal_container()->get(Language::CONTENT)->langcode; } field_attach_prepare_view('taxonomy_term', array($term->tid => $term), $view_mode, $langcode); diff --git a/core/modules/user/user.module b/core/modules/user/user.module index cfde270..3caa2cd 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1,6 +1,7 @@ langcode; + $langcode = drupal_container()->get(Language::CONTENT)->langcode; } // Retrieve all profile fields and attach to $account->content. @@ -2398,7 +2399,7 @@ function user_view($account, $view_mode = 'full', $langcode = NULL) { */ function user_build_content($account, $view_mode = 'full', $langcode = NULL) { if (!isset($langcode)) { - $langcode = $GLOBALS['language_content']->langcode; + $langcode = drupal_container()->get(Language::CONTENT)->langcode; } // Remove previously built content, if exists.