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 7 Feb 2009 00:33:37 -0000 @@ -172,5 +172,33 @@ function hook_block_view($delta = '') { } /** + * Act on blocks prior to rendering. + * + * This hook alows you to remove blocks from the list to display if they do not + * meet certain conditions. 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 $module . $delta + */ +function hook_block_load(&$blocks) { + global $language; + $result = db_query('SELECT module, delta, language FROM {my_table}')->fetchAll(); + $block_languages = array(); + foreach ($result as $record) { + $block_languages[$record->module . $record->delta][] = $record->language; + } + // If a block has no language associations, we assume it's language neutral. + // So build an array only of blocks with such associations. + $blocks_by_language = array_keys(array_intersect_key($blocks, $block_languages)); + foreach ($blocks_by_language as $block) { + if (!in_array($language->language, $block_languages[$block])) { + unset($blocks[$block]); + } + } +} + +/** * @} End of "addtogroup hooks". */ Index: modules/block/block.module =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.module,v retrieving revision 1.321 diff -u -p -r1.321 block.module --- modules/block/block.module 3 Feb 2009 12:30:14 -0000 1.321 +++ modules/block/block.module 7 Feb 2009 00:33:37 -0000 @@ -471,15 +471,55 @@ function block_list($region) { * Load blocks information from the database. */ function _block_load_blocks() { - global $user, $theme_key; + global $theme_key; + + $queried_blocks = array(); + $result = db_query('SELECT * FROM {block} WHERE theme = :theme AND status = 1 ORDER BY region, weight, module', array(':theme' => $theme_key)); + foreach ($result as $record) { + $queried_blocks[$record->module . $record->delta] = $record; + } + + // Invoke hook_block_load() to filter out any blocks not matched by + // block visibility rules. + foreach (module_implements('block_load') as $module) { + $function = $module . '_block_load'; + $function($queried_blocks); + } $blocks = array(); - $rids = array_keys($user->roles); - $result = db_query(db_rewrite_sql("SELECT DISTINCT b.* FROM {block} b LEFT JOIN {block_role} r ON b.module = r.module AND b.delta = r.delta WHERE b.theme = '%s' AND b.status = 1 AND (r.rid IN (" . db_placeholders($rids) . ") OR r.rid IS NULL) ORDER BY b.region, b.weight, b.module", 'b', 'bid'), array_merge(array($theme_key), $rids)); - while ($block = db_fetch_object($result)) { - if (!isset($blocks[$block->region])) { - $blocks[$block->region] = array(); + foreach ($queried_blocks as $key => $block) { + $blocks[$block->region][$block->module . '_' . $block->delta] = $block; + } + + return $blocks; +} + +/** + * Implementation of hook_block_load(). + */ +function block_block_load(&$blocks) { + global $user; + + // Build an array of roles for each block. + $blocks_roles = array(); + $result = db_query('SELECT module, delta, rid FROM {block_role}')->fetchAll(); + foreach ($result as $record) { + $blocks_roles[$record->module . $record->delta][] = $record->rid; + } + + // If a block has no roles associated, it is displayed for every role. + $blocks_by_role = array_keys(array_intersect_key($blocks, $blocks_roles)); + + // For blocks with roles associated, if none of the user's roles matches + // the settings from this block, remove it from the array. + foreach ($blocks_by_role as $block) { + if (!array_intersect($blocks_roles[$block], array_keys($user->roles))) { + unset($blocks[$block]); } + } + + foreach ($blocks as $key => $block) { + // Use the user's block visibility setting, if necessary. if ($block->custom != 0) { if ($user->uid && isset($user->block[$block->module][$block->delta])) { @@ -492,6 +532,10 @@ function _block_load_blocks() { else { $enabled = TRUE; } + if (!$enabled) { + unset($blocks[$key]); + continue; + } // Match path if necessary. if ($block->pages) { @@ -514,12 +558,11 @@ function _block_load_blocks() { else { $page_match = TRUE; } - $block->enabled = $enabled; - $block->page_match = $page_match; - $blocks[$block->region]["{$block->module}_{$block->delta}"] = $block; - } - return $blocks; + if (!$page_match) { + unset($blocks[$key]); + } + } } /** @@ -535,38 +578,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.10 diff -u -p -r1.10 block.test --- modules/block/block.test 21 Jan 2009 16:58:41 -0000 1.10 +++ modules/block/block.test 7 Feb 2009 00:33:37 -0000 @@ -82,6 +82,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() {