Commit 13830d02 authored by catch's avatar catch

Issue #1301040 by Gábor Hojtsy: Move language listing functionality from...

Issue #1301040 by Gábor Hojtsy: Move language listing functionality from locale.module to a new language.module.
parent 37edfd76
......@@ -2686,8 +2686,8 @@ function language_list($field = 'language') {
// Init language list
if (!isset($languages)) {
$default = language_default();
if (drupal_multilingual() || module_exists('locale')) {
$languages['language'] = db_query('SELECT * FROM {languages} ORDER BY weight ASC, name ASC')->fetchAllAssoc('language');
if (drupal_multilingual() || module_exists('language')) {
$languages['language'] = db_query('SELECT * FROM {language} ORDER BY weight ASC, name ASC')->fetchAllAssoc('language');
}
else {
// No locale module, so use the default language only.
......
......@@ -1146,7 +1146,7 @@ function install_find_translations() {
foreach ($files as $key => $file) {
// Strip off the file name component before the language code.
$files[$key]->langcode = preg_replace('!^(.+\.)?([^\.]+)$!', '\2', $file->name);
// Language codes cannot exceed 12 characters to fit into the {languages}
// Language codes cannot exceed 12 characters to fit into the {language}
// table.
if (strlen($files[$key]->langcode) > 12) {
unset($files[$key]);
......@@ -1432,7 +1432,7 @@ function install_import_translations(&$install_state) {
'name' => $langcode,
'default' => TRUE,
);
locale_language_save($language);
language_save($language);
}
else {
// A known predefined language, details will be filled in properly.
......@@ -1440,7 +1440,7 @@ function install_import_translations(&$install_state) {
'language' => $langcode,
'default' => TRUE,
);
locale_language_save($language);
language_save($language);
}
// Collect files to import for this language.
......
......@@ -534,63 +534,6 @@ function locale_string_is_safe($string) {
return decode_entities($string) == decode_entities(filter_xss($string, array('a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'ins', 'kbd', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var')));
}
/**
* API function to add or update a language.
*
* @param $language
* Language object with properties corresponding to 'languages' table columns.
*/
function locale_language_save($language) {
$language->is_new = !(bool) db_query_range('SELECT 1 FROM {languages} WHERE language = :language', 0, 1, array(':language' => $language->language))->fetchField();
// If name was not set, we add a predefined language.
if (!isset($language->name)) {
include_once DRUPAL_ROOT . '/core/includes/standard.inc';
$predefined = standard_language_list();
$language->name = $predefined[$language->language][0];
$language->direction = isset($predefined[$language->language][2]) ? $predefined[$language->language][2] : LANGUAGE_LTR;
}
// Set to enabled for the default language and unless specified otherwise.
if (!empty($language->default) || !isset($language->enabled)) {
$language->enabled = TRUE;
}
// Let other modules modify $language before saved.
module_invoke_all('locale_language_presave', $language);
// Save the record and inform others about the change.
if ($language->is_new) {
drupal_write_record('languages', $language);
module_invoke_all('locale_language_insert', $language);
watchdog('locale', 'The %language (%langcode) language has been created.', array('%language' => $language->name, '%langcode' => $language->language));
}
else {
drupal_write_record('languages', $language, array('language'));
module_invoke_all('locale_language_update', $language);
watchdog('locale', 'The %language (%langcode) language has been updated.', array('%language' => $language->name, '%langcode' => $language->language));
}
if (!empty($language->default)) {
// Set the new version of this language as default in a variable.
$default_language = language_default();
variable_set('language_default', $language);
}
// Update language count based on enabled language count.
variable_set('language_count', db_query('SELECT COUNT(language) FROM {languages} WHERE enabled = 1')->fetchField());
// Kill the static cache in language_list().
drupal_static_reset('language_list');
// @todo move these two cache clears out. See http://drupal.org/node/1293252
// Changing the language settings impacts the interface.
cache_clear_all('*', 'cache_page', TRUE);
// Force JavaScript translation file re-creation for the modified language.
_locale_invalidate_js($language->language);
return $language;
}
/**
* Parses a JavaScript file, extracts strings wrapped in Drupal.t() and
* Drupal.formatPlural() and inserts them into the database.
......@@ -865,34 +808,6 @@ function _locale_rebuild_js($langcode = NULL) {
}
}
/**
* @defgroup locale-api-predefined List of predefined languages
* @{
* API to provide a list of predefined languages.
*/
/**
* Prepares the language code list for a select form item with only the unsupported ones
*/
function _locale_prepare_predefined_list() {
include_once DRUPAL_ROOT . '/core/includes/standard.inc';
$languages = language_list();
$predefined = standard_language_list();
foreach ($predefined as $key => $value) {
if (isset($languages[$key])) {
unset($predefined[$key]);
continue;
}
$predefined[$key] = t($value[0]);
}
asort($predefined);
return $predefined;
}
/**
* @} End of "locale-api-languages-predefined"
*/
/**
* Get list of all predefined and custom countries.
*
......
......@@ -100,6 +100,91 @@ function update_prepare_d8_bootstrap() {
'description' => $has_required_schema ? '' : 'Please update your Drupal 7 installation to the most recent version before attempting to upgrade to Drupal 8',
),
);
if ($has_required_schema) {
// Update the environment for the language bootstrap if needed.
update_prepare_d8_language();
}
}
}
/**
* Prepare Drupal 8 language changes for the bootstrap if needed.
*/
function update_prepare_d8_language() {
if (db_table_exists('languages')) {
$languages = db_select('languages', 'l')
->fields('l')
->execute();
$plurals = array();
$javascript = array();
$prefixes = array();
$domains = array();
foreach ($languages as $language) {
$plurals[$language->language] = array(
'plurals' => $language->plurals,
'formula' => $language->formula,
);
$javascript[$language->language] = $language->javascript;
$prefixes[$language->language] = $language->prefix;
$domains[$language->language] = $language->domain;
}
variable_set('locale_translation_plurals', $plurals);
variable_set('locale_translation_javascript', $javascript);
variable_set('locale_language_negotiation_url_prefixes', $prefixes);
variable_set('locale_language_negotiation_url_domains', $domains);
// Drop now unneeded columns.
db_drop_field('languages', 'plurals');
db_drop_field('languages', 'formula');
db_drop_field('languages', 'javascript');
db_drop_field('languages', 'prefix');
db_drop_field('languages', 'domain');
db_drop_field('languages', 'native');
// Rename the languages table to language.
db_rename_table('languages', 'language');
// Finally install/enable the language module. We need to use the update
// specific version of this function to ensure schema conflicts don't happen
// due to our updated data.
$modules = array('language');
update_module_add_to_system($modules);
update_module_enable($modules);
}
}
/**
* Adds modules to the system table in a Drupal core update.
*
* @param $modules
* Array of module names.
*/
function update_module_add_to_system($modules = array()) {
// Insert module data, so we can enable the module. Calling a full module
// list rebuild so early is costly and complex, so we just have a stop-gap.
$info_defaults = array(
'dependencies' => array(),
'description' => '',
'package' => 'Other',
'version' => NULL,
'php' => DRUPAL_MINIMUM_PHP,
'files' => array(),
'bootstrap' => 0,
);
foreach ($modules as $module) {
$module_info = drupal_parse_info_file('core/modules/' . $module . '/' . $module . '.info');
db_insert('system')
->fields(array(
'filename' => 'core/modules/' . $module . '/' . $module . '.module',
'name' => $module,
'type' => 'module',
'status' => 0,
'bootstrap' => 0,
'schema_version' => -1,
'weight' => 0,
'info' => serialize($module_info + $info_defaults),
))
->execute();
}
}
......@@ -145,6 +230,9 @@ function update_module_enable(array $modules) {
->fields(array('schema_version' => 0, 'status' => 1))
->execute();
// system_list_reset() is in module.inc but that would only be available
// once the variable bootstrap is done.
require_once DRUPAL_ROOT . '/core/includes/module.inc';
system_list_reset();
// @todo: figure out what to do about hook_install() and hook_enable().
}
......
......@@ -168,7 +168,7 @@ function comment_schema() {
'description' => "The comment author's home page address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on.",
),
'language' => array(
'description' => 'The {languages}.language of this comment.',
'description' => 'The {language}.language of this comment.',
'type' => 'varchar',
'length' => 12,
'not null' => TRUE,
......
......@@ -63,23 +63,23 @@
*/
/**
* Implements hook_locale_language_insert().
* Implements hook_language_insert().
*/
function field_locale_language_insert() {
function field_language_insert() {
field_info_cache_clear();
}
/**
* Implements hook_locale_language_update().
* Implements hook_language_update().
*/
function field_locale_language_update() {
function field_language_update() {
field_info_cache_clear();
}
/**
* Implements hook_locale_language_delete().
* Implements hook_language_delete().
*/
function field_locale_language_delete() {
function field_language_delete() {
field_info_cache_clear();
}
......
......@@ -2648,7 +2648,7 @@ class FieldTranslationsTestCase extends FieldTestCase {
'language' => 'l' . $i,
'name' => $this->randomString(),
);
locale_language_save($language);
language_save($language);
}
}
......
This diff is collapsed.
<?php
/**
* @file
* Hooks provided by the Language module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* React to a language about to be added or updated in the system.
*
* @param $language
* A language object.
*/
function hook_language_presave($language) {
if ($language->default) {
// React to a new default language.
example_new_default_language($language);
}
}
/**
* React to a language that was just added to the system.
*
* @param $language
* A language object.
*/
function hook_language_insert($language) {
example_refresh_permissions();
}
/**
* React to a language that was just updated in the system.
*
* @param $language
* A language object.
*/
function hook_language_update($language) {
example_refresh_permissions();
}
/**
* Allow modules to react before the deletion of a language.
*
* @param $language
* The language object of the language that is about to be deleted.
*/
function hook_language_delete($language) {
// On nodes with this language, unset the language
db_update('node')
->fields(array('language' => ''))
->condition('language', $language->language)
->execute();
}
/**
* @} End of "addtogroup hooks".
*/
name = Language
description = Lets you configure a number of languages to be used on your website.
package = Core
version = VERSION
core = 8.x
configure = admin/config/regional/language
files[] = language.test
<?php
/**
* @file
* Install, update and uninstall functions for the language module.
*/
/**
* Implements hook_install().
*/
function language_install() {
// Add the default language to the database too.
language_save(language_default());
}
/**
* Implements hook_uninstall().
*/
function language_uninstall() {
// Clear variables.
variable_del('language_default');
variable_del('language_count');
// Re-initialize the language system so successive calls to t() and other
// functions will not expect languages to be present.
drupal_language_initialize();
}
/**
* Implements hook_schema().
*/
function language_schema() {
$schema['language'] = array(
'description' => 'List of all available languages in the system.',
'fields' => array(
'language' => 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).',
),
'enabled' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Enabled flag (1 = Enabled, 0 = Disabled).',
),
'weight' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Weight, used in lists of languages.',
),
),
'primary key' => array('language'),
'indexes' => array(
'list' => array('weight', 'name'),
),
);
return $schema;
}
<?php
/**
* @file
* Add language handling functionality to Drupal.
*/
/**
* Implements hook_help().
*/
function language_help($path, $arg) {
switch ($path) {
case 'admin/help#language':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Language module allows you to maintain a list of languages used on your Drupal site for providing language information for content and for interface translation (using the Locale module). For more information, see the online handbook entry for <a href="@language">Language module</a>.', array('@language' => 'http://drupal.org/handbook/modules/language/')) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Configuring the list of languages') . '</dt>';
$output .= '<dd>' . t('<a href="@configure-languages">Configure the list of languages</a> either using the built-in language list or providing any custom languages you wish.', array('@configure-languages' => url('admin/config/regional/language'))) . '</dd>';
$output .= '</dl>';
return $output;
case 'admin/config/regional/language':
return '<p>' . t('With multiple languages enabled, registered users may select their preferred language and authors can assign a specific language to content.') . '</p>';
case 'admin/config/regional/language/add':
return '<p>' . t('Add a language to be supported by your site. If your desired language is not available, pick <em>Custom language...</em> at the end and provide a language code and other details manually.') . '</p>';
}
}
/**
* Implements hook_menu().
*/
function language_menu() {
$items['admin/config/regional/language'] = array(
'title' => 'Languages',
'description' => 'Configure languages for content and the user interface.',
'page callback' => 'drupal_get_form',
'page arguments' => array('language_admin_overview_form'),
'access arguments' => array('administer languages'),
'file' => 'language.admin.inc',
'weight' => -10,
);
$items['admin/config/regional/language/list'] = array(
'title' => 'List',
'weight' => 0,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/config/regional/language/add'] = array(
'title' => 'Add language',
'page callback' => 'drupal_get_form',
'page arguments' => array('language_admin_add_form'),
'access arguments' => array('administer languages'),
'weight' => 5,
'type' => MENU_LOCAL_ACTION,
'file' => 'language.admin.inc',
);
$items['admin/config/regional/language/edit/%language'] = array(
'title' => 'Edit language',
'page callback' => 'drupal_get_form',
'page arguments' => array('language_admin_edit_form', 5),
'access arguments' => array('administer languages'),
'file' => 'language.admin.inc',
);
$items['admin/config/regional/language/delete/%language'] = array(
'title' => 'Confirm delete',
'page callback' => 'drupal_get_form',
'page arguments' => array('language_admin_delete_form', 5),
'access arguments' => array('administer languages'),
'file' => 'language.admin.inc',
);
return $items;
}
/**
* Implements hook_permission().
*/
function language_permission() {
return array(
'administer languages' => array(
'title' => t('Administer languages'),
),
);
}
/**
* Implements hook_theme().
*/
function language_theme() {
return array(
'language_admin_overview_form_table' => array(
'render element' => 'form',
'file' => 'language.admin.inc',
),
'language_admin_operations' => array(
'render element' => 'elements',
'file' => 'language.admin.inc',
),
);
}
/**
* Loads a language object from the database.
*
* @param $langcode
* The language code.
*
* @return
* A fully-populated language object or FALSE.
*/
function language_load($langcode) {
$languages = language_list();
return isset($languages[$langcode]) ? $languages[$langcode] : FALSE;
}
/**
* API function to add or update a language.
*
* @param $language
* Language object with properties corresponding to 'language' table columns.
*/
function language_save($language) {
$language->is_new = !(bool) db_query_range('SELECT 1 FROM {language} WHERE language = :language', 0, 1, array(':language' => $language->language))->fetchField();
// If name was not set, we add a predefined language.
if (!isset($language->name)) {
include_once DRUPAL_ROOT . '/core/includes/standard.inc';
$predefined = standard_language_list();
$language->name = $predefined[$language->language][0];
$language->direction = isset($predefined[$language->language][2]) ? $predefined[$language->language][2] : LANGUAGE_LTR;
}
// Set to enabled for the default language and unless specified otherwise.
if (!empty($language->default) || !isset($language->enabled)) {
$language->enabled = TRUE;
}
// Let other modules modify $language before saved.
module_invoke_all('language_presave', $language);
// Save the record and inform others about the change.
$t_args = array('%language' => $language->name, '%langcode' => $language->language);
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('language'));
module_invoke_all('language_update', $language);
watchdog('language', 'The %language (%langcode) language has been updated.', $t_args);
}
if (!empty($language->default)) {
// Set the new version of this language as default in a variable.
$default_language = language_default();
variable_set('language_default', $language);
}
// Update language count based on enabled language count.
variable_set('language_count', db_query('SELECT COUNT(language) FROM {language} WHERE enabled = 1')->fetchField());
// Kill the static cache in language_list().
drupal_static_reset('language_list');
return $language;
}
/**
* Delete a language.
*
* @param $langcode
* Language code of the language to be deleted.
* @return
* TRUE if language is successfully deleted. Otherwise FALSE.
*/
function language_delete($langcode) {
$languages = language_list();
if (isset($languages[$langcode])) {
$language = $languages[$langcode];
module_invoke_all('language_delete', $language);
// Remove the language.
db_delete('language')
->condition('language', $language->language)
->execute();
if ($language->enabled) {
variable_set('language_count', variable_get('language_count', 1) - 1);
}
drupal_static_reset('language_list');
$t_args = array('%language' => $language->name, '%langcode' => $language->language);
watchdog('language', 'The %language (%langcode) language has been removed.', $t_args);
return TRUE;
}
return FALSE;
}
<?php
/**
* @file
* Tests for language.module.
*
* The test file includes:
* - a functional test for the language configuration forms;
*/
/**
* Functional tests for the language list configuration forms.
*/
class LanguageListTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Language list configuration',
'description' => 'Adds a new language and tests changing its status and the default language.',
'group' => 'Language',
);
}
function setUp() {
parent::setUp('language');
}
/**
* Functional tests for adding, editing and deleting languages.
*/
function testLanguageList() {
global $base_url;
// User to add and remove language.
$admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
$this->drupalLogin($admin_user);
// Add predefined language.
$edit = array(
'predefined_langcode' => 'fr',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
$this->assertText('French', t('Language added successfully.'));
$this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
// Add custom language.
$langcode = 'xx';
$name = $this->randomName(16);
$edit = array(
'predefined_langcode' => 'custom',
'langcode' => $langcode,
'name' => $name,
'direction' => '0',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
$this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
$this->assertRaw('"edit-site-default-' . $langcode .'"', t('Language code found.'));
$this->assertText(t($name), t('Test language added.'));
// Check if we can change the default language.
$path = 'admin/config/regional/language';
$this->drupalGet($path);
$this->assertFieldChecked('edit-site-default-en', t('English is the default language.'));
// Change the default language.
$edit = array(
'site_default' => $langcode,
);
$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.'));
// Ensure we can't delete the default language.
$this->drupalGet('admin/config/regional/language/delete/' . $langcode);
$this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
$this->assertText(t('The default language cannot be deleted.'), t('Failed to delete the default language.'));
// Check if we can disable a language.
$edit = array(
'languages[en][enabled]' => FALSE,
);
$this->drupalPost($path, $edit, t('Save configuration'));
$this->assertNoFieldChecked('edit-languages-en-enabled', t('Language disabled.'));