Index: modules/block/block.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.api.php,v retrieving revision 1.3 diff -u -p -r1.3 block.api.php --- modules/block/block.api.php 26 Jan 2009 14:08:42 -0000 1.3 +++ modules/block/block.api.php 31 May 2009 20:00:33 -0000 @@ -172,5 +172,51 @@ function hook_block_view($delta = '') { } /** + * Act on blocks prior to rendering. + * + * This hook allows you to add, remove or modify blocks in the block list. The + * block list contains the block definitions not the rendered blocks. The blocks + * are rendered after the modules have had a chance to manipulate the block + * list. + * Alternatively you can set $block->content here, which will override the + * content of the block and prevent hook_block_view() from running. + * + * @param $blocks + * An array of $blocks, keyed by $bid + * + * This example shows how to achieve language specific visibility setting for + * blocks. + */ +function hook_block_list_alter(&$blocks) { + global $language, $theme_key; + + $result = db_query('SELECT module, delta, language FROM {my_table}'); + $block_languages = array(); + foreach ($result as $record) { + $block_languages[$record->module][$record->delta][$record->language] = 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 ($block->theme != $theme_key || $block->status != 1) { + // This block was added by a contrib module, leave it in the list. + continue; + } + + if (!isset($block_languages[$block->module][$block->delta])) { + // No language setting for this block, leave it in the list. + continue; + } + + if (!isset($block_languages[$block->module][$block->delta][$language->language])) { + // This block should not be displayed with the active language, remove + // from the list. + unset($blocks[$key]); + } + } +} + +/** * @} End of "addtogroup hooks". */ Index: modules/block/block.module =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.module,v retrieving revision 1.341 diff -u -p -r1.341 block.module --- modules/block/block.module 31 May 2009 07:46:54 -0000 1.341 +++ modules/block/block.module 31 May 2009 20:08:56 -0000 @@ -579,30 +579,64 @@ function block_list($region) { * Load blocks information from the database. */ function _block_load_blocks() { - global $user, $theme_key; + global $theme_key; - $blocks = array(); - $rids = array_keys($user->roles); $query = db_select('block', 'b'); - $query->leftJoin('block_role', 'r', 'b.module = r.module AND b.delta = r.delta'); $result = $query - ->distinct() ->fields('b') ->condition('b.theme', $theme_key) ->condition('b.status', 1) - ->condition(db_or() - ->condition('r.rid', $rids, 'IN') - ->isNull('r.rid') - ) ->orderBy('b.region') ->orderBy('b.weight') ->orderBy('b.module') ->addTag('block_load') ->execute(); - foreach ($result as $block) { - if (!isset($blocks[$block->region])) { - $blocks[$block->region] = array(); - } + + $block_list = $result->fetchAllAssoc('bid'); + // Allow modules to modify the block list. + drupal_alter('block_list', $block_list); + + $blocks = array(); + foreach ($block_list as $block) { + $blocks[$block->region]["{$block->module}_{$block->delta}"] = $block; + } + return $blocks; +} + +/** + * Implement hook_block_list_alter(). + * + * Check the page, role and user specific visibilty settings. Remove the block + * if the visibility conditions are not met. + */ +function block_block_list_alter(&$blocks) { + global $user, $theme_key; + + // Build an array of roles for each block. + $block_roles = array(); + $result = db_query('SELECT module, delta, rid FROM {block_role}'); + foreach ($result as $record) { + $block_roles[$record->module][$record->delta][] = $record->rid; + } + + foreach ($blocks as $key => $block) { + if ($block->theme != $theme_key || $block->status != 1) { + // This block was added by a contrib module, leave it in the list. + continue; + } + + // If a block has no roles associated, it is displayed for every role. + // For blocks with roles associated, if none of the user's roles matches + // the settings from this block, remove it from the block list. + if (!isset($block_roles[$block->module][$block->delta])) { + // No roles associated. + } + elseif (!array_intersect($block_roles[$block->module][$block->delta], array_keys($user->roles))) { + // No match. + unset($blocks[$key]); + continue; + } + // Use the user's block visibility setting, if necessary. if ($block->custom != 0) { if ($user->uid && isset($user->block[$block->module][$block->delta])) { @@ -615,6 +649,10 @@ function _block_load_blocks() { else { $enabled = TRUE; } + if (!$enabled) { + unset($blocks[$key]); + continue; + } // Match path if necessary. if ($block->pages) { @@ -640,12 +678,10 @@ function _block_load_blocks() { else { $page_match = TRUE; } - $block->enabled = $enabled; - $block->page_match = $page_match; - $blocks[$block->region]["{$block->module}_{$block->delta}"] = $block; + if (!$page_match) { + unset($blocks[$key]); + } } - - return $blocks; } /** @@ -661,38 +697,39 @@ function _block_render_blocks($region_bl foreach ($region_blocks as $key => $block) { // Render the block content if it has not been created already. if (!isset($block->content)) { - // Erase the block from the static array - we'll put it back if it has content. + // Erase the block from the static array - we'll put it back if it has + // content. unset($region_blocks[$key]); - if ($block->enabled && $block->page_match) { - // Try fetching the block from cache. Block caching is not compatible with - // node_access modules. We also preserve the submission of forms in blocks, - // by fetching from cache only if the request method is 'GET' (or 'HEAD'). - if (!count(module_implements('node_grants')) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && ($cid = _block_get_cache_id($block)) && ($cache = cache_get($cid, 'cache_block'))) { - $array = $cache->data; - } - else { - $array = module_invoke($block->module, 'block_view', $block->delta); - if (isset($cid)) { - cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY); - } + // Try fetching the block from cache. Block caching is not compatible + // with node_access modules. We also preserve the submission of forms in + // blocks, by fetching from cache only if the request method is 'GET' + // (or 'HEAD'). + if (!count(module_implements('node_grants')) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && ($cid = _block_get_cache_id($block)) && ($cache = cache_get($cid, 'cache_block'))) { + $array = $cache->data; + } + else { + $array = module_invoke($block->module, 'block_view', $block->delta); + if (isset($cid)) { + cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY); } + } - if (isset($array) && is_array($array)) { - foreach ($array as $k => $v) { - $block->$k = $v; - } + if (isset($array) && is_array($array)) { + foreach ($array as $k => $v) { + $block->$k = $v; + } + } + if (isset($block->content) && $block->content) { + // Override default block title if a custom display title is present. + if ($block->title) { + // Check plain here to allow module generated titles to keep any + // markup. + $block->subject = $block->title == '' ? '' : check_plain($block->title); } - if (isset($block->content) && $block->content) { - // Override default block title if a custom display title is present. - if ($block->title) { - // Check plain here to allow module generated titles to keep any markup. - $block->subject = $block->title == '' ? '' : check_plain($block->title); - } - if (!isset($block->subject)) { - $block->subject = ''; - } - $region_blocks["{$block->module}_{$block->delta}"] = $block; + if (!isset($block->subject)) { + $block->subject = ''; } + $region_blocks["{$block->module}_{$block->delta}"] = $block; } } } Index: modules/block/block.test =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.test,v retrieving revision 1.19 diff -u -p -r1.19 block.test --- modules/block/block.test 31 May 2009 07:46:54 -0000 1.19 +++ modules/block/block.test 31 May 2009 19:44:56 -0000 @@ -100,6 +100,37 @@ class BlockTestCase extends DrupalWebTes } /** + * Test block visibility. + */ + function testBlockVisibility() { + $block = array(); + $block['title'] = 'Syndicate'; + $block['module'] = 'node'; + $block['delta'] = 'syndicate'; + + // Set the block to be hidden on any user path, and to be shown only to + // authenticated users. + $edit = array(); + $edit['pages'] = 'user*'; + $edit['roles[2]'] = TRUE; + $this->drupalPost('admin/build/block/configure/' . $block['module'] . '/' . $block['delta'], $edit, t('Save block')); + + // Move block to the left sidebar. + $this->moveBlockToRegion($block, $this->regions[1]); + + $this->drupalGet(''); + $this->assertText('Syndicate', t('Block was displayed on the front page.')); + + $this->drupalGet('user*'); + $this->assertNoText('Syndicate', t('Block was not displayed according to block visibility rules.')); + + // Confirm that the block is not displayed to anonymous users. + $this->drupalLogout(); + $this->drupalGet(''); + $this->assertNoText('Syndicate', t('Block was not displayed to anonymous users.')); + } + + /** * Test configuring and moving a module-define block to specific regions. */ function testBlock() {