' . t('Blocks are boxes of content rendered into an area, or region, of a web page. The default theme Garland, for example, implements the regions "left sidebar", "right sidebar", "content", "header", and "footer", and a block may appear in any one of these areas. The blocks administration page provides a drag-and-drop interface for assigning a block to a region, and for controlling the order of blocks within regions.', array('@blocks' => url('admin/build/block'))) . '
'; $output .= '' . t('Although blocks are usually generated automatically by modules (like the User login block, for example), administrators can also define custom blocks. Custom blocks have a title, description, and body. The body of the block can be as long as necessary, and can contain content supported by any available text format.', array('@text-format' => url('admin/settings/filter'))) . '
'; $output .= '' . t('When working with blocks, remember that:') . '
'; $output .= '' . t('For more information, see the online handbook entry for Block module.', array('@block' => 'http://drupal.org/handbook/modules/block/')) . '
'; return $output; case 'admin/build/block': $output = '' . t('This page provides a drag-and-drop interface for assigning a block to a region, and for controlling the order of blocks within regions. To change the region or order of a block, grab a drag-and-drop handle under the Block column and drag the block to a new location in the list. (Grab a handle by clicking and holding the mouse while hovering over a handle icon.) Since not all themes implement the same regions, or display regions in the same way, blocks are positioned on a per-theme basis. Remember that your changes will not be saved until you click the Save blocks button at the bottom of the page.') . '
'; $output .= '' . t('Click the configure link next to each block to configure its specific title and visibility settings. Use the add block page to create a custom block.', array('@add-block' => url('admin/build/block/add'))) . '
'; return $output; case 'admin/build/block/add': return '' . t('Use this page to create a new custom block. New blocks are disabled by default, and must be moved to a region on the blocks administration page to be visible.', array('@blocks' => url('admin/build/block'))) . '
'; } } /** * Implementation of hook_theme(). */ function block_theme() { return array( 'block' => array( 'arguments' => array('block' => NULL), 'template' => 'block', ), 'block_admin_display_form' => array( 'template' => 'block-admin-display-form', 'file' => 'block.admin.inc', 'arguments' => array('form' => NULL), ), ); } /** * Implementation of hook_perm(). */ function block_perm() { return array( 'administer blocks' => array( 'title' => t('Administer blocks'), 'description' => t('Select which blocks are displayed, and arrange them on the page.'), ), 'use PHP for block visibility' => array( 'title' => t('Use PHP for block visibility'), 'description' => t('Enter PHP code in the field for block visibility settings. %warning', array('%warning' => t('Warning: Give to trusted roles only; this permission has security implications.'))), ), ); } /** * Implementation of hook_menu(). */ function block_menu() { $items['admin/build/block'] = array( 'title' => 'Blocks', 'description' => 'Configure what block content appears in your site\'s sidebars and other regions.', 'page callback' => 'block_admin_display', 'access arguments' => array('administer blocks'), ); $items['admin/build/block/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/build/block/list/js'] = array( 'title' => 'JavaScript List Form', 'page callback' => 'block_admin_display_js', 'access arguments' => array('administer blocks'), 'type' => MENU_CALLBACK, ); $items['admin/build/block/configure'] = array( 'title' => 'Configure block', 'page callback' => 'drupal_get_form', 'page arguments' => array('block_admin_configure'), 'access arguments' => array('administer blocks'), 'type' => MENU_CALLBACK, ); $items['admin/build/block/delete'] = array( 'title' => 'Delete block', 'page callback' => 'drupal_get_form', 'page arguments' => array('block_box_delete'), 'access arguments' => array('administer blocks'), 'type' => MENU_CALLBACK, ); $items['admin/build/block/add'] = array( 'title' => 'Add block', 'page callback' => 'drupal_get_form', 'page arguments' => array('block_add_block_form'), 'access arguments' => array('administer blocks'), 'type' => MENU_LOCAL_TASK, ); $default = variable_get('theme_default', 'garland'); foreach (list_themes() as $key => $theme) { $items['admin/build/block/list/' . $key] = array( 'title' => check_plain($theme->info['name']), 'page arguments' => array($key), 'type' => $key == $default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, 'weight' => $key == $default ? -10 : 0, 'access callback' => '_block_themes_access', 'access arguments' => array($theme), ); } return $items; } /** * Menu item access callback - only admin or enabled themes can be accessed. */ function _block_themes_access($theme) { return user_access('administer blocks') && ($theme->status || $theme->name == variable_get('admin_theme', 0)); } /** * Implementation of hook_block_list(). */ function block_block_list() { $blocks = array(); $result = db_query('SELECT bid, info FROM {box} ORDER BY info'); while ($block = db_fetch_object($result)) { $blocks[$block->bid]['info'] = $block->info; // Not worth caching. $blocks[$block->bid]['cache'] = BLOCK_NO_CACHE; } return $blocks; } /** * Implementation of hook_block_configure(). */ function block_block_configure($delta = 0, $edit = array()) { $box = array('format' => FILTER_FORMAT_DEFAULT); if ($delta) { $box = block_box_get($delta); } if (filter_access($box['format'])) { return block_box_form($box); } } /** * Implementation of hook_block_save(). */ function block_block_save($delta = 0, $edit = array()) { block_box_save($edit, $delta); } /** * Implementation of hook_block_view(). * * Generates the administrator-defined blocks for display. */ function block_block_view($delta = 0, $edit = array()) { $block = db_fetch_object(db_query('SELECT body, format FROM {box} WHERE bid = %d', $delta)); $data['content'] = check_markup($block->body, $block->format, '', FALSE); return $data; } /** * Implementation of hook_page_alter(). * * Render blocks into their regions. */ function block_page_alter($page) { global $theme; // The theme system might not yet be initialized. We need $theme. init_theme(); // Populate all block regions $regions = system_region_list($theme); // Load all region content assigned via blocks. foreach (array_keys($regions) as $region) { // Prevent left and right regions from rendering blocks when 'show_blocks' == FALSE. if ($page['#show_blocks'] || ($region != 'left' && $region != 'right')) { // Assign blocks to region. if ($blocks = block_get_blocks_by_region($region)) { $page[$region]['blocks'] = $blocks; } // Append region description if we are rendering the block admin page. $item = menu_get_item(); if ($item['path'] == 'admin/build/block') { $description = '' . t('When caching is enabled, anonymous user sessions are only saved to the database when needed, so the "Who\'s online" block does not display the number of anonymous users.') . '
'; } } /** * Implementation of hook_form_FORM_ID_alter(). */ function block_form_system_themes_form_alter(&$form, &$form_state) { $form['#submit'][] = 'block_system_themes_form_submit'; } /** * Initialize blocks for enabled themes. */ function block_system_themes_form_submit(&$form, &$form_state) { if ($form_state['values']['op'] == t('Save configuration')) { if (is_array($form_state['values']['status'])) { foreach ($form_state['values']['status'] as $key => $choice) { if ($choice || $form_state['values']['theme_default'] == $key) { block_initialize_theme_blocks($key); } } } if ($form_state['values']['admin_theme'] && $form_state['values']['admin_theme'] != variable_get('admin_theme', 0)) { // If we're changing themes, make sure the theme has its blocks initialized. $result = db_result(db_query("SELECT COUNT(*) FROM {block} WHERE theme = '%s'", $form_state['values']['admin_theme'])); if (!$result) { block_initialize_theme_blocks($form_state['values']['admin_theme']); } } } } /** * Assign an initial, default set of blocks for a theme. * * This function is called the first time a new theme is enabled. The new theme * gets a copy of the default theme's blocks, with the difference that if a * particular region isn't available in the new theme, the block is assigned * to the new theme's default region. * * @param $theme * The name of a theme. */ function block_initialize_theme_blocks($theme) { // Initialize theme's blocks if none already registered. if (!(db_result(db_query("SELECT COUNT(*) FROM {block} WHERE theme = '%s'", $theme)))) { $default_theme = variable_get('theme_default', 'garland'); $regions = system_region_list($theme); $result = db_query("SELECT * FROM {block} WHERE theme = '%s'", $default_theme); while ($block = db_fetch_array($result)) { // If the region isn't supported by the theme, assign the block to the theme's default region. if (!array_key_exists($block['region'], $regions)) { $block['region'] = system_default_region($theme); } db_query("INSERT INTO {block} (module, delta, theme, status, weight, region, visibility, pages, custom, cache) VALUES ('%s', '%s', '%s', %d, %d, '%s', %d, '%s', %d, %d)", $block['module'], $block['delta'], $theme, $block['status'], $block['weight'], $block['region'], $block['visibility'], $block['pages'], $block['custom'], $block['cache']); } } } /** * Return all blocks in the specified region for the current user. * * @param $region * The name of a region. * * @return * An array of block objects, indexed with module_delta. * If you are displaying your blocks in one or two sidebars, you may check * whether this array is empty to see how many columns are going to be * displayed. * * @todo * Now that the blocks table has a primary key, we should use that as the * array key instead of module_delta. */ function block_list($region) { static $blocks = array(); if (!count($blocks)) { $blocks = _block_load_blocks(); } // Create an empty array if there were no entries. if (!isset($blocks[$region])) { $blocks[$region] = array(); } $blocks[$region] = _block_render_blocks($blocks[$region]); return $blocks[$region]; } /** * Load blocks information from the database. */ function _block_load_blocks() { global $user, $theme_key; $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(); } // Use the user's block visibility setting, if necessary. if ($block->custom != 0) { if ($user->uid && isset($user->block[$block->module][$block->delta])) { $enabled = $user->block[$block->module][$block->delta]; } else { $enabled = ($block->custom == 1); } } else { $enabled = TRUE; } // Match path if necessary. if ($block->pages) { if ($block->visibility < 2) { $path = drupal_get_path_alias($_GET['q']); // Compare with the internal and path alias (if any). $page_match = drupal_match_path($path, $block->pages); if ($path != $_GET['q']) { $page_match = $page_match || drupal_match_path($_GET['q'], $block->pages); } // When $block->visibility has a value of 0, the block is displayed on // all pages except those listed in $block->pages. When set to 1, it // is displayed only on those pages listed in $block->pages. $page_match = !($block->visibility xor $page_match); } else { $page_match = drupal_eval($block->pages); } } else { $page_match = TRUE; } $block->enabled = $enabled; $block->page_match = $page_match; $blocks[$block->region]["{$block->module}_{$block->delta}"] = $block; } return $blocks; } /** * Render the content and subject for a set of blocks. * * @param $region_blocks * An array of block objects such as returned for one region by _block_load_blocks(). * * @return * An array of visible blocks with subject and content rendered. */ function _block_render_blocks($region_blocks) { 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. 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); } } 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 == '