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/language/language.install b/core/modules/language/language.install index ecf637d..6e958c5 100644 --- a/core/modules/language/language.install +++ b/core/modules/language/language.install @@ -71,6 +71,30 @@ function language_schema() { 'list' => array('weight', 'name'), ), ); + $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'), + ); return $schema; } @@ -101,3 +125,34 @@ function language_update_8000() { variable_set('language_default', $language_default); } } + +/** + * Creates table {block_language} for langcode visibility settings per block. + */ +function language_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); +} diff --git a/core/modules/language/language.module b/core/modules/language/language.module index 20f7c62..f7ab739 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -186,6 +186,17 @@ function language_delete($langcode) { } /** + * Implements hook_modules_uninstalled(). + * + * Cleans up {block_language} table from modules' blocks. + */ +function language_modules_uninstalled($modules) { + db_delete('block_language') + ->condition('module', $modules, 'IN') + ->execute(); +} + +/** * Implements hook_css_alter(). * * This function checks all CSS files currently added via drupal_add_css() and @@ -211,3 +222,113 @@ function language_css_alter(&$css) { } } } + +/** + * Implements hook_form_FORM_ID_alter(). + * + * Adds per language block visibility options to block add form. + * + * @see language_form_block_admin_configure_submit() + * @see block_admin_configure() + * @see language_form_block_admin_configure_alter() + */ + +function language_form_block_add_block_form_alter(&$form, &$form_state) { + language_form_block_admin_configure_alter($form, $form_state); +} + +/** + * Implements hook_form_FORM_ID_alter(). + * + * Adds per language block visibility options to block configuration form. + * + * @see language_form_block_admin_configure_submit() + * @see block_admin_configure() + * @see language_form_block_add_block_form_alter() + */ +function language_form_block_admin_configure_alter(&$form, &$form_state) { + $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('If you select no languages, the block will be visibile in all languages.'), + ); + $form['#submit'][] = 'language_form_block_admin_configure_submit'; +} + +/** + * Form submission handler for locale_form_block_admin_configure_alter(). + */ +function language_form_block_admin_configure_submit($form, &$form_state) { + 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(); +} + +/** + * Implements hook_block_list_alter(). + * + * Hide the blocks that have been disabled for langcodes visibility setting. + */ +function language_block_list_alter(&$blocks) { + global $language_content, $theme_key; + + $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) { + // Any module using this alter should inspect the data before changing it, + // to ensure it is what they expect. + 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. + continue; + } + + if (!isset($block_langcodes[$block->module][$block->delta])) { + // No language setting for this block, leave it in the list. + continue; + } + + if (!isset($block_langcodes[$block->module][$block->delta][$language_content->langcode])) { + // This block should not be displayed with the active language, remove + // from the list. + unset($blocks[$key]); + } + } +} diff --git a/core/modules/language/language.test b/core/modules/language/language.test index 0a59937..217b81c 100644 --- a/core/modules/language/language.test +++ b/core/modules/language/language.test @@ -179,3 +179,80 @@ 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.')); + } +}