commit 24a4821eea486272df27aace25e0e1af3812f78a Author: Artusamak Date: Fri Mar 30 11:23:28 2012 +0200 Issue #135464 Implements block visibility per language. diff --git a/core/modules/block/block.admin.inc b/core/modules/block/block.admin.inc index 8a5553d..631d37b 100644 --- a/core/modules/block/block.admin.inc +++ b/core/modules/block/block.admin.inc @@ -400,6 +400,38 @@ function block_admin_configure($form, &$form_state, $module, $delta) { ); } + // Configure the block visibility per language. + if (module_exists('language')) { + $default_langcode_options = db_query("SELECT langcode FROM {block_language} WHERE module = :module AND delta = :delta", array( + ':module' => $form['module']['#value'], + ':delta' => $form['delta']['#value'], + ))->fetchCol(); + + // Fetch the enabled languages. + $enabled_languages = language_list(TRUE); + foreach ($enabled_languages as $language) { + // @TODO $language->name is not wrapped with t(), it should be replaced + // by CMI translation implementation. + $langcodes_options[$language->langcode] = $language->name; + } + $form['visibility']['langcode'] = array( + '#type' => 'fieldset', + '#title' => t('Languages'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#group' => 'visibility', + '#weight' => 5, + '#access' => language_multilingual(), + ); + $form['visibility']['langcode']['langcodes'] = array( + '#type' => 'checkboxes', + '#title' => t('Show this block only for specific languages'), + '#default_value' => $default_langcode_options, + '#options' => $langcodes_options, + '#description' => t('Show this block only for the selected language(s). If you select no languages, the block will be visibile in all languages.'), + ); + } + // Per-role visibility. $default_role_options = db_query("SELECT rid FROM {block_role} WHERE module = :module AND delta = :delta", array( ':module' => $block->module, @@ -505,7 +537,7 @@ function block_admin_configure_submit($form, &$form_state) { } $query->execute(); - // Store regions per theme for this block + // Store regions per theme for this block. foreach ($form_state['values']['regions'] as $theme => $region) { db_merge('block') ->key(array('theme' => $theme, 'delta' => $form_state['values']['delta'], 'module' => $form_state['values']['module'])) @@ -517,6 +549,26 @@ function block_admin_configure_submit($form, &$form_state) { ->execute(); } + // Update the block visibility settings if we have settings to store + // for the existing languages. + if (module_exists('language') && isset($form_state['values']['langcodes'])) { + db_delete('block_language') + ->condition('module', $form_state['values']['module']) + ->condition('delta', $form_state['values']['delta']) + ->execute(); + $query = db_insert('block_language')->fields(array( + 'langcode', 'module', 'delta' + )); + foreach (array_filter($form_state['values']['langcodes']) as $langcode) { + $query->values(array( + 'langcode' => $langcode, + 'module' => $form_state['values']['module'], + 'delta' => $form_state['values']['delta'], + )); + } + $query->execute(); + } + module_invoke($form_state['values']['module'], 'block_save', $form_state['values']['delta'], $form_state['values']); } catch (Exception $e) { @@ -604,7 +656,7 @@ function block_add_block_form_submit($form, &$form_state) { } $query->execute(); - // Store regions per theme for this block + // Store regions per theme for this block. foreach ($form_state['values']['regions'] as $theme => $region) { db_merge('block') ->key(array('theme' => $theme, 'delta' => $delta, 'module' => $form_state['values']['module'])) @@ -616,6 +668,22 @@ function block_add_block_form_submit($form, &$form_state) { ->execute(); } + // Update the block visibility settings if we have settings to store + // for the existing languages. + if (module_exists('language') && isset($form_state['values']['langcodes'])) { + $query = db_insert('block_language')->fields(array( + 'langcode', 'module', 'delta' + )); + foreach (array_filter($form_state['values']['langcodes']) as $langcode) { + $query->values(array( + 'langcode' => $langcode, + 'module' => $form_state['values']['module'], + 'delta' => $form_state['values']['delta'], + )); + } + $query->execute(); + } + drupal_set_message(t('The block has been created.')); cache_clear_all(); $form_state['redirect'] = 'admin/structure/block'; @@ -659,6 +727,11 @@ function block_custom_block_delete_submit($form, &$form_state) { ->condition('module', 'block') ->condition('delta', $form_state['values']['bid']) ->execute(); + db_delete('block_language') + ->condition('module', 'block') + ->condition('delta', $form_state['values']['bid']) + ->execute(); + drupal_set_message(t('The block %name has been removed.', array('%name' => $form_state['values']['info']))); cache_clear_all(); $form_state['redirect'] = 'admin/structure/block'; diff --git a/core/modules/block/block.install b/core/modules/block/block.install index c2d4185..65d2b4a 100644 --- a/core/modules/block/block.install +++ b/core/modules/block/block.install @@ -166,6 +166,31 @@ function block_schema() { 'primary key' => array('bid'), ); + $schema['block_language'] = array( + 'description' => 'Sets up display criteria for blocks based on langcode', + 'fields' => array( + 'module' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'description' => "The block's origin module, from {block}.module.", + ), + 'delta' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'description' => "The block's unique delta within module, from {block}.delta.", + ), + 'langcode' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'description' => "The machine-readable name of this language from {language}.langcode.", + ), + ), + 'primary key' => array('module', 'delta'), + ); + $schema['cache_block'] = drupal_get_schema_unprocessed('system', 'cache'); $schema['cache_block']['description'] = 'Cache table for the Block module to store already built blocks, identified by module, delta, and various contexts which may change the block, such as theme, locale, and caching mode defined for the block.'; @@ -199,6 +224,37 @@ function block_update_8000() { } /** + * Creates table {block_language} for language visibility settings per block. + */ +function block_update_8001() { + $schema = array( + 'description' => 'Sets up display criteria for blocks based on langcode.', + 'fields' => array( + 'module' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'description' => "The block's origin module, from {block}.module.", + ), + 'delta' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'description' => "The block's unique delta within module, from {block}.delta.", + ), + 'langcode' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'description' => "The machine-readable name of this language from {language}.langcode.", + ), + ), + 'primary key' => array('module', 'delta'), + ); + db_create_table('block_language', $schema); +} + +/** * @} End of "addtogroup updates-7.x-to-8.x" * The next series of updates should start at 9000. */ diff --git a/core/modules/block/block.js b/core/modules/block/block.js index 72b5673..67cbb31 100644 --- a/core/modules/block/block.js +++ b/core/modules/block/block.js @@ -32,6 +32,17 @@ Drupal.behaviors.blockSettingsSummary = { return vals.join(', '); }); + $('fieldset#edit-langcode', context).drupalSetSummary(function (context) { + var vals = []; + $('input[type="checkbox"]:checked', context).each(function () { + vals.push($.trim($(this).next('label').text())); + }); + if (!vals.length) { + vals.push(Drupal.t('Not restricted')); + } + return vals.join(', '); + }); + $('fieldset#edit-role', context).drupalSetSummary(function (context) { var vals = []; $('input[type="checkbox"]:checked', context).each(function () { diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 25bd3b1..9bb591c 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -776,7 +776,7 @@ function _block_load_blocks() { * Removes the block if the visibility conditions are not met. */ function block_block_list_alter(&$blocks) { - global $user, $theme_key; + global $user, $language_content, $theme_key; // Build an array of roles for each block. $block_roles = array(); @@ -785,6 +785,13 @@ function block_block_list_alter(&$blocks) { $block_roles[$record->module][$record->delta][] = $record->rid; } + // Build an array of langcodes allowed per block. + $result = db_query('SELECT module, delta, langcode FROM {block_language}'); + $block_langcodes = array(); + foreach ($result as $record) { + $block_langcodes[$record->module][$record->delta][$record->langcode] = TRUE; + } + foreach ($blocks as $key => $block) { if (!isset($block->theme) || !isset($block->status) || $block->theme != $theme_key || $block->status != 1) { // This block was added by a contrib module, leave it in the list. @@ -854,6 +861,19 @@ function block_block_list_alter(&$blocks) { } if (!$page_match) { unset($blocks[$key]); + continue; + } + + // Language visibility settings. + // No language setting for this block, leave it in the list. + if (!isset($block_langcodes[$block->module][$block->delta])) { + continue; + } + // This block should not be displayed with the active language, remove + // from the list. + if (!isset($block_langcodes[$block->module][$block->delta][$language_content->langcode])) { + unset($blocks[$key]); + continue; } } } @@ -1021,7 +1041,8 @@ function block_admin_paths() { /** * Implements hook_modules_uninstalled(). * - * Cleans up {block} and {block_role} tables from modules' blocks. + * Cleans up {block}, {block_role} and {block_language} tables + * from modules' blocks. */ function block_modules_uninstalled($modules) { db_delete('block') @@ -1030,4 +1051,19 @@ function block_modules_uninstalled($modules) { db_delete('block_role') ->condition('module', $modules, 'IN') ->execute(); + db_delete('block_language') + ->condition('module', $modules, 'IN') + ->execute(); +} + +/** + * Implements hook_language_delete(). + * + * Delete the potential block visibility settings of the deleted language. + */ +function block_language_delete($language) { + // Remove the block visibility settings for the deleted language. + db_delete('block_language') + ->condition('langcode', $language->langcode) + ->execute(); } diff --git a/core/modules/language/language.test b/core/modules/language/language.test index 0a59937..51dd3d3 100644 --- a/core/modules/language/language.test +++ b/core/modules/language/language.test @@ -179,3 +179,192 @@ class LanguageListTest extends DrupalWebTestCase { $this->assertRaw(t('The %language (%langcode) language has been removed.', $t_args), t('The English language has been removed.')); } } + + +/** + * Functional tests for the language list configuration forms. + */ +class LanguageBlockVisibilityTest extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Language block visibility', + 'description' => 'Tests if a block can be configure to be only visibile on a particular language.', + 'group' => 'Language', + ); + } + + function setUp() { + parent::setUp('language', 'locale', 'block'); + } + + /** + * Tests the visibility settings for the blocks based on language. + */ + public function testLanguageBlockVisibility() { + // Create a new user, allow him to manage the blocks and the languages. + $admin_user = $this->drupalCreateUser(array( + 'administer languages', 'administer blocks', + )); + $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.')); + + // Check if the visibility setting is available. + $this->drupalGet('admin/structure/block/add'); + $this->assertField('langcodes[en]', t('Language visibility field is visible.')); + + // Create a new block. + $info_name = $this->randomString(10); + $body = ''; + for ($i = 0; $i <= 100; $i++) { + $body .= chr(rand(97, 122)); + } + $edit = array( + 'regions[stark]' => 'sidebar_first', + 'info' => $info_name, + 'title' => 'test', + 'body[value]' => $body, + ); + $this->drupalPost('admin/structure/block/add', $edit, t('Save block')); + + // Set visibility setting for one language. + $edit = array( + 'langcodes[en]' => TRUE, + ); + $this->drupalPost('admin/structure/block/manage/block/1/configure', $edit, t('Save block')); + + // Change the default language. + $edit = array( + 'site_default' => 'fr', + ); + $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration')); + + // Reset the static cache of the language list. + drupal_static_reset('language_list'); + + // Check that a page has a block + $this->drupalGet('', array('language' => language_load('en'))); + $this->assertText($body, t('The body of the custom block appears on the page.')); + + // Check that a page doesn't has a block for the current language anymore + $this->drupalGet('', array('language' => language_load('fr'))); + $this->assertNoText($body, t('The body of the custom block does not appear on the page.')); + } + + /** + * Tests if the visibility settings are clean from the database if the + * language is removed. + */ + public function testLanguageBlockVisibilityLanguageDelete() { + // Create a new user, allow him to manage the blocks and the languages. + $admin_user = $this->drupalCreateUser(array( + 'administer languages', 'administer blocks', + )); + $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.')); + + // Create a new block. + $info_name = $this->randomString(10); + $body = ''; + for ($i = 0; $i <= 100; $i++) { + $body .= chr(rand(97, 122)); + } + $edit = array( + 'regions[stark]' => 'sidebar_first', + 'info' => $info_name, + 'title' => 'test', + 'body[value]' => $body, + ); + $this->drupalPost('admin/structure/block/add', $edit, t('Save block')); + + // Set visibility setting for one language. + $edit = array( + 'langcodes[fr]' => TRUE, + ); + $this->drupalPost('admin/structure/block/manage/block/1/configure', $edit, t('Save block')); + + // Check that we have an entry in the database after saving the setting. + $count = db_query('SELECT COUNT(langcode) FROM {block_language} WHERE module = :module AND delta = :delta', array( + ':module' => 'block', + ':delta' => '1' + ))->fetchField(); + $this->assertTrue($count == 1, t('The block language visibility has an entry in the database.')); + + // Delete the language. + $this->drupalPost('admin/config/regional/language/delete/fr', array(), t('Delete')); + + // Check that the setting related to this language has been deleted. + $count = db_query('SELECT COUNT(langcode) FROM {block_language} WHERE module = :module AND delta = :delta', array( + ':module' => 'block', + ':delta' => '1' + ))->fetchField(); + $this->assertTrue($count == 0, t('The block language visibility do not have an entry in the database.')); + } + + /** + * Tests if the visibility settings are clean from the database + * if the block is deleted. + */ + public function testLanguageBlockVisibilityBlockDelete() { + // Create a new user, allow him to manage the blocks and the languages. + $admin_user = $this->drupalCreateUser(array( + 'administer languages', 'administer blocks', + )); + $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.')); + + // Create a new block. + $info_name = $this->randomString(10); + $body = ''; + for ($i = 0; $i <= 100; $i++) { + $body .= chr(rand(97, 122)); + } + $edit = array( + 'regions[stark]' => 'sidebar_first', + 'info' => $info_name, + 'title' => 'test', + 'body[value]' => $body, + ); + $this->drupalPost('admin/structure/block/add', $edit, t('Save block')); + + // Set visibility setting for one language. + $edit = array( + 'langcodes[fr]' => TRUE, + ); + $this->drupalPost('admin/structure/block/manage/block/1/configure', $edit, t('Save block')); + + // Check that we have an entry in the database after saving the setting. + $count = db_query('SELECT COUNT(langcode) FROM {block_language} WHERE module = :module AND delta = :delta', array( + ':module' => 'block', + ':delta' => '1' + ))->fetchField(); + $this->assertTrue($count == 1, t('The block language visibility has an entry in the database.')); + + // Delete the custom block. + $this->drupalPost('admin/structure/block/manage/block/1/delete', array(), t('Delete')); + + // Check that the setting related to this block has been deleted. + $count = db_query('SELECT COUNT(langcode) FROM {block_language} WHERE module = :module AND delta = :delta', array( + ':module' => 'block', + ':delta' => '1' + ))->fetchField(); + $this->assertTrue($count == 0, t('The block language visibility do not have an entry in the database.')); + } +}