block.module 22.6 KB
Newer Older
Dries's avatar
 
Dries committed
1
<?php
2
// $Id$
Dries's avatar
 
Dries committed
3

Dries's avatar
 
Dries committed
4 5 6 7 8
/**
 * @file
 * Controls the boxes that are displayed around the main content.
 */

9 10 11 12
/**
 * Denotes that a block is not enabled in any region and should not
 * be shown.
 */
13 14
define('BLOCK_REGION_NONE', -1);

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/**
 * Constants defining cache granularity for blocks.
 *
 * Modules specify the caching patterns for their blocks using binary
 * combinations of these constants in their hook_block(op 'list'):
 *   $block[delta]['cache'] = BLOCK_CACHE_PER_ROLE | BLOCK_CACHE_PER_PAGE;
 * BLOCK_CACHE_PER_ROLE is used as a default when no caching pattern is
 * specified.
 *
 * The block cache is cleared in cache_clear_all(), and uses the same clearing
 * policy than page cache (node, comment, user, taxonomy added or updated...).
 * Blocks requiring more fine-grained clearing might consider disabling the
 * built-in block cache (BLOCK_NO_CACHE) and roll their own.
 *
 * Note that user 1 is excluded from block caching.
 */

/**
 * The block should not get cached. This setting should be used:
 * - for simple blocks (notably those that do not perform any db query),
 * where querying the db cache would be more expensive than directly generating
 * the content.
 * - for blocks that change too frequently.
 */
define('BLOCK_NO_CACHE', -1);

/**
* The block can change depending on the roles the user viewing the page belongs to.
* This is the default setting, used when the block does not specify anything.
*/
define('BLOCK_CACHE_PER_ROLE', 0x0001);

/**
* The block can change depending on the user viewing the page.
* This setting can be resource-consuming for sites with large number of users,
* and thus should only be used when BLOCK_CACHE_PER_ROLE is not sufficient.
*/
define('BLOCK_CACHE_PER_USER', 0x0002);

/**
* The block can change depending on the page being viewed.
*/
define('BLOCK_CACHE_PER_PAGE', 0x0004);

/**
 * The block is the same for every user on every page where it is visible.
 */
define('BLOCK_CACHE_GLOBAL', 0x0008);

Dries's avatar
 
Dries committed
64 65 66
/**
 * Implementation of hook_help().
 */
67 68
function block_help($path, $arg) {
  switch ($path) {
Dries's avatar
 
Dries committed
69
    case 'admin/help#block':
drumm's avatar
drumm committed
70 71 72
      $output = '<p>'. t('Blocks are boxes of content that may be rendered into certain regions of your web pages, for example, into sidebars. Blocks are usually generated automatically by modules (e.g., Recent Forum Topics), but administrators can also define custom blocks.') .'</p>';
      $output .= '<p>'. t('The region each block appears in depends on both which theme you are using (some themes allow greater control over block placement than others), and on the settings in the block administration section.') .'</p>';
      $output .= '<p>'. t('The block administration screen lets you specify the vertical placement of the blocks within a region. You do this by assigning a weight to each block. Lighter blocks (those having a smaller weight) "float up" towards the top of the region; heavier ones "sink".') .'</p>';
73
      $output .= t("<p>A block's visibility depends on:</p>
74
<ul>
75 76
<li>Its region placement. Blocks with no region assigned to them are never shown.</li>
<li>Its throttle checkbox when throttle module is enabled. Throttled blocks are hidden during high server loads.</li>
drumm's avatar
drumm committed
77 78 79
<li>Its page visibility settings. Blocks can be configured to be visible/hidden on certain pages.</li>
<li>Its custom visibility settings. Blocks can be configured to be visible only when specific conditions are true.</li>
<li>Its user visibility settings. Administrators can choose to let users decide whether to show/hide certain blocks.</li>
80
<li>Its user-role visibility settings. Administrators can choose to let blocks be visible only for certain user roles.</li>
drumm's avatar
drumm committed
81
<li>Its function. Some dynamic blocks, such as those generated by modules, will be displayed only on certain pages.</li>
82
</ul>
83
");
84
      $output .= '<h3>'. t('Module blocks') .'</h3>';
85
      $output .= '<p>'. t('Some modules generate blocks that become available when the modules are enabled. These blocks can be administered via the <a href="@admin-block">blocks administration page</a>.</p>', array('@admin-block' => url('admin/build/block'))) .'</p>';
86
      $output .= '<h3>'. t('Administrator defined blocks') .'</h3>';
drumm's avatar
drumm committed
87
      $output .= '<p>'. t('Administrators can also define custom blocks. These blocks consist of a title, a description, and a body which can be as long as you wish. Block content can be in any of the input formats supported for other content.') .'</p>';
88
      $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="@block">Block page</a>.', array('@block' => 'http://drupal.org/handbook/modules/block/')) .'</p>';
89
      return $output;
90
    case 'admin/build/block':
91
      return t('<p>Blocks are boxes of content that may be rendered into certain regions of your web pages, for example, into sidebars. They are usually generated automatically by modules, but administrators can create blocks manually.</p>
drumm's avatar
drumm committed
92
<p>Only enabled blocks are shown. You can position blocks by specifying which area of the page they should appear in (e.g., a sidebar).  Highlighted labels on this page show the regions into which blocks can be rendered. You can specify where within a region a block will appear by adjusting its weight.</p>
93 94
<p>If you want certain blocks to disable themselves temporarily during high server loads, check the "Throttle" box. You can configure the auto-throttle on the <a href="@throttle">throttle configuration page</a> after having enabled the throttle module.</p>
<p>You can configure the behaviour of each block (for example, specifying on which pages and for what users it will appear) by clicking the "configure" link for each block.</p>', array('@throttle' => url('admin/settings/throttle')));
95
    case 'admin/build/block/add':
96
      return '<p>'. t('Here you can create a new block. Once you have created this block you must make it active and give it a place on the page using <a href="@overview">blocks</a>. The description is used in the "block" column on the <a href="@overview">blocks</a> page.', array('@overview' => url('admin/build/block'))) .'</p>';
Dries's avatar
 
Dries committed
97
  }
Dries's avatar
 
Dries committed
98 99
}

100 101 102 103 104 105
/**
 * Implementation of hook_theme()
 */
function block_theme() {
  return array(
    'block_admin_display' => array(
106
      'template' => 'block-admin-display',
107 108 109 110 111
      'arguments' => array('form' => NULL),
    ),
  );
}

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
/**
 * Process variables for block-admin-display.tpl.php.
 *
 * The $variables array contains the following arguments:
 * - $form
 *
 * @see block-admin-display.tpl.php
 * @see theme_block_admin_display()
 */
function template_preprocess_block_admin_display(&$variables) {
  global $theme_key;

  $variables['throttle'] = module_exists('throttle');
  $block_regions = system_region_list($theme_key);

  // Highlight regions on page to provide visual reference.
  foreach ($block_regions as $key => $value) {
    drupal_set_content($key, '<div class="block-region">'. $value .'</div>');
  }

  // Setup to track previous region in loop.
  $last_region = '';
  foreach (element_children($variables['form']) as $i) {
    $block = &$variables['form'][$i];

    // Only take form elements that are blocks.
    if (isset($block['info'])) {
      // Fetch region for current block.
      $region = $block['region']['#default_value'];

      // Track first block listing to insert region header inside block_admin_display.tpl.php.
      $is_region_first = FALSE;
      if ($last_region != $region) {
        $is_region_first = TRUE;
        // Set region title. Block regions already translated.
        if ($region != BLOCK_REGION_NONE) {
          $region_title = drupal_ucfirst($block_regions[$region]);
        }
        else {
          $region_title = t('Disabled');
        }
      }

      $variables['block_listing'][$i]->is_region_first = $is_region_first;
      $variables['block_listing'][$i]->region_title = $region_title;
      $variables['block_listing'][$i]->block_title =  drupal_render($block['info']);
      $variables['block_listing'][$i]->region_select = drupal_render($block['region']) . drupal_render($block['theme']);
      $variables['block_listing'][$i]->weight_select = drupal_render($block['weight']);
      $variables['block_listing'][$i]->throttle_check = $variables['throttle'] ? drupal_render($block['throttle']) : '';
      $variables['block_listing'][$i]->configure_link = drupal_render($block['configure']);
      $variables['block_listing'][$i]->delete_link = !empty($block['delete']) ? drupal_render($block['delete']) : '';

      $last_region = $region;
    }
  }

  $variables['form_submit'] = drupal_render($variables['form']);
}

Dries's avatar
 
Dries committed
171 172 173
/**
 * Implementation of hook_perm().
 */
Dries's avatar
 
Dries committed
174
function block_perm() {
175
  return array('administer blocks', 'use PHP for block visibility');
Dries's avatar
 
Dries committed
176 177
}

Dries's avatar
 
Dries committed
178
/**
Dries's avatar
 
Dries committed
179
 * Implementation of hook_menu().
Dries's avatar
 
Dries committed
180
 */
181 182
function block_menu() {
  $items['admin/build/block'] = array(
183 184
    'title' => 'Blocks',
    'description' => 'Configure what block content appears in your site\'s sidebars and other regions.',
185 186 187
    'page callback' => 'drupal_get_form',
    'page arguments' => array('block_admin_display'),
    'access arguments' => array('administer blocks'),
188
    'file' => 'block.admin.inc',
189 190
  );
  $items['admin/build/block/list'] = array(
191
    'title' => 'List',
192 193 194 195
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/build/block/configure'] = array(
196
    'title' => 'Configure block',
197 198
    'page arguments' => array('block_admin_configure'),
    'type' => MENU_CALLBACK,
199
    'file' => 'block.admin.inc',
200 201
  );
  $items['admin/build/block/delete'] = array(
202
    'title' => 'Delete block',
203 204
    'page arguments' => array('block_box_delete'),
    'type' => MENU_CALLBACK,
205
    'file' => 'block.admin.inc',
206 207
  );
  $items['admin/build/block/add'] = array(
208
    'title' => 'Add block',
209 210
    'page arguments' => array('block_add_block_form'),
    'type' => MENU_LOCAL_TASK,
211
    'file' => 'block.admin.inc',
212 213 214 215
  );
  $default = variable_get('theme_default', 'garland');
  foreach (list_themes() as $key => $theme) {
    $items['admin/build/block/list/'. $key] = array(
216
      'title' => check_plain($theme->info['name']),
217
      'page arguments' => array('block_admin_display', $key),
218
      'type' => $key == $default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
219
      'weight' => $key == $default ? -10 : 0,
220
      'file' => 'block.admin.inc',
221
    );
Dries's avatar
 
Dries committed
222
  }
Dries's avatar
 
Dries committed
223
  return $items;
Dries's avatar
 
Dries committed
224 225
}

Dries's avatar
 
Dries committed
226 227 228 229 230
/**
 * Implementation of hook_block().
 *
 * Generates the administrator-defined blocks for display.
 */
231 232 233
function block_block($op = 'list', $delta = 0, $edit = array()) {
  switch ($op) {
    case 'list':
234 235
      $blocks = array();

236
      $result = db_query('SELECT bid, info FROM {boxes} ORDER BY info');
237
      while ($block = db_fetch_object($result)) {
238
        $blocks[$block->bid]['info'] = $block->info;
239 240
        // Not worth caching.
        $blocks[$block->bid]['cache'] = BLOCK_NO_CACHE;
241 242 243 244
      }
      return $blocks;

    case 'configure':
245 246 247 248
      $box = array('format' => FILTER_FORMAT_DEFAULT);
      if ($delta) {
        $box = block_box_get($delta);
      }
249 250 251 252 253 254 255 256 257 258
      if (filter_access($box['format'])) {
        return block_box_form($box);
      }
      break;

    case 'save':
      block_box_save($edit, $delta);
      break;

    case 'view':
259
      $block = db_fetch_object(db_query('SELECT body, format FROM {boxes} WHERE bid = %d', $delta));
260
      $data['content'] = check_markup($block->body, $block->format, FALSE);
261
      return $data;
262 263 264
  }
}

Dries's avatar
 
Dries committed
265
/**
Dries's avatar
 
Dries committed
266
 * Update the 'blocks' DB table with the blocks currently exported by modules.
Dries's avatar
 
Dries committed
267
 *
Dries's avatar
 
Dries committed
268
 * @return
269
 *   Blocks currently exported by modules.
Dries's avatar
 
Dries committed
270
 */
271
function _block_rehash() {
272 273
  global $theme_key;

274
  init_theme();
275 276

  $result = db_query("SELECT * FROM {blocks} WHERE theme = '%s'", $theme_key);
Dries's avatar
 
Dries committed
277 278 279 280
  while ($old_block = db_fetch_object($result)) {
    $old_blocks[$old_block->module][$old_block->delta] = $old_block;
  }

281
  $blocks = array();
Dries's avatar
 
Dries committed
282 283

  foreach (module_list() as $module) {
Dries's avatar
 
Dries committed
284
    $module_blocks = module_invoke($module, 'block', 'list');
Dries's avatar
 
Dries committed
285 286
    if ($module_blocks) {
      foreach ($module_blocks as $delta => $block) {
Dries's avatar
 
Dries committed
287 288
        $block['module'] = $module;
        $block['delta']  = $delta;
289 290
        // If no cache pattern is specified, we use PER_ROLE as a default.
        $block['cache'] = isset($block['cache']) ? $block['cache'] : BLOCK_CACHE_PER_ROLE;
291
        // If previously written to database, load values.
292
        if (!empty($old_blocks[$module][$delta])) {
Dries's avatar
 
Dries committed
293 294 295
          $block['status'] = $old_blocks[$module][$delta]->status;
          $block['weight'] = $old_blocks[$module][$delta]->weight;
          $block['region'] = $old_blocks[$module][$delta]->region;
296 297
          $block['visibility'] = $old_blocks[$module][$delta]->visibility;
          $block['pages'] = $old_blocks[$module][$delta]->pages;
Dries's avatar
 
Dries committed
298 299
          $block['custom'] = $old_blocks[$module][$delta]->custom;
          $block['throttle'] = $old_blocks[$module][$delta]->throttle;
300
          $block['title'] = $old_blocks[$module][$delta]->title;
Dries's avatar
 
Dries committed
301
        }
302
        // Otherwise, use any set values, or else substitute defaults.
Dries's avatar
 
Dries committed
303
        else {
304
          $properties = array('status' => 0, 'weight' => 0, 'region' => 'left', 'pages' => '', 'custom' => 0, 'title' => '');
305
          foreach ($properties as $property => $default) {
306
            if (!isset($block[$property])) {
307 308 309
              $block[$property] = $default;
            }
          }
Dries's avatar
 
Dries committed
310 311 312 313 314 315 316
        }

        $blocks[] = $block;
      }
    }
  }

Dries's avatar
Dries committed
317
  db_lock_table('blocks');
318 319 320 321 322
  // Remove all blocks from table.
  db_query("DELETE FROM {blocks} WHERE theme = '%s'", $theme_key);

  // Reinsert new set of blocks into table.
  foreach ($blocks as $block) {
323 324 325 326
    $block += array(
      'visibility' => NULL,
      'throttle' => NULL,
    );
327
    db_query("INSERT INTO {blocks} (module, delta, theme, status, weight, region, visibility, pages, custom, throttle, title, cache) VALUES ('%s', '%s', '%s', %d, %d, '%s', %d, '%s', %d, %d, '%s', %d)", $block['module'], $block['delta'], $theme_key, $block['status'], $block['weight'], $block['region'], $block['visibility'], $block['pages'], $block['custom'], $block['throttle'], $block['title'], $block['cache']);
328 329 330
  }
  db_unlock_tables();

Dries's avatar
 
Dries committed
331 332
  return $blocks;
}
Dries's avatar
 
Dries committed
333

334
function block_box_get($bid) {
335
  return db_fetch_array(db_query("SELECT bx.*, bl.title FROM {boxes} bx INNER JOIN {blocks} bl ON bx.bid = bl.delta WHERE bl.module = 'block' AND bx.bid = %d", $bid));
Dries's avatar
 
Dries committed
336 337
}

338 339 340
/**
 * Define the custom block form.
 */
341
function block_box_form($edit = array()) {
342 343 344 345
  $edit += array(
    'info' => '',
    'body' => '',
  );
346 347 348
  $form['info'] = array(
    '#type' => 'textfield',
    '#title' => t('Block description'),
349
    '#default_value' => $edit['info'],
350
    '#maxlength' => 64,
351
    '#description' => t('A brief description of your block. Used on the <a href="@overview">block overview page</a>.', array('@overview' => url('admin/build/block'))),
352 353 354
    '#required' => TRUE,
    '#weight' => -19,
  );
355 356
  $form['body_field']['#weight'] = -17;
  $form['body_field']['body'] = array(
357 358
    '#type' => 'textarea',
    '#title' => t('Block body'),
359
    '#default_value' => $edit['body'],
360 361 362 363
    '#rows' => 15,
    '#description' => t('The content of the block as shown to the user.'),
    '#weight' => -17,
  );
364 365 366
  if (!isset($edit['format'])) {
    $edit['format'] = FILTER_FORMAT_DEFAULT;
  }
367
  $form['body_field']['format'] = filter_form($edit['format'], -16);
368

369
  return $form;
370 371
}

372
function block_box_save($edit, $delta) {
373 374
  if (!filter_access($edit['format'])) {
    $edit['format'] = FILTER_FORMAT_DEFAULT;
Dries's avatar
 
Dries committed
375 376
  }

377 378
  db_query("UPDATE {boxes} SET body = '%s', info = '%s', format = %d WHERE bid = %d", $edit['body'], $edit['info'], $edit['format'], $delta);

379
  return TRUE;
380 381
}

Dries's avatar
 
Dries committed
382 383 384 385 386 387
/**
 * Implementation of hook_user().
 *
 * Allow users to decide which custom blocks to display when they visit
 * the site.
 */
388
function block_user($type, $edit, &$user, $category = NULL) {
389
  global $user;
390
  switch ($type) {
391
    case 'form':
392
      if ($category == 'account') {
393
        $result = db_query("SELECT DISTINCT b.* FROM {blocks} b LEFT JOIN {blocks_roles} r ON b.module = r.module AND b.delta = r.delta WHERE b.status = 1 AND b.custom != 0 AND (r.rid IN (%s) OR r.rid IS NULL) ORDER BY b.weight, b.module", implode(',', array_keys($user->roles)));
394
        $form['block'] = array('#type' => 'fieldset', '#title' => t('Block configuration'), '#weight' => 3, '#collapsible' => TRUE, '#tree' => TRUE);
395 396 397
        while ($block = db_fetch_object($result)) {
          $data = module_invoke($block->module, 'block', 'list');
          if ($data[$block->delta]['info']) {
398
            $return = TRUE;
399
            $form['block'][$block->module][$block->delta] = array('#type' => 'checkbox', '#title' => check_plain($data[$block->delta]['info']), '#default_value' => isset($user->block[$block->module][$block->delta]) ? $user->block[$block->module][$block->delta] : ($block->custom == 1));
400
          }
Kjartan's avatar
Kjartan committed
401 402
        }

403
        if (!empty($return)) {
404
          return $form;
405
        }
406
      }
Dries's avatar
 
Dries committed
407 408

      break;
Dries's avatar
 
Dries committed
409
    case 'validate':
410
      if (empty($edit['block'])) {
Dries's avatar
 
Dries committed
411
        $edit['block'] = array();
412 413
      }
      return $edit;
414 415 416
  }
}

417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
/**
 * 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 <i>module</i>_<i>delta</i>.
 *   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
 *   Add a proper primary key (bid) to the blocks table so we don't have
 *   to mess around with this <i>module</i>_<i>delta</i> construct.
 *   Currently, the blocks table has no primary key defined!
 */
function block_list($region) {
  global $user, $theme_key;

437 438
  static $blocks = array();

439
  if (!count($blocks)) {
440
    $result = db_query(db_rewrite_sql("SELECT DISTINCT b.* FROM {blocks} b LEFT JOIN {blocks_roles} r ON b.module = r.module AND b.delta = r.delta WHERE b.theme = '%s' AND b.status = 1 AND (r.rid IN (%s) OR r.rid IS NULL) ORDER BY b.region, b.weight, b.module", 'b', 'bid'), $theme_key, implode(',', array_keys($user->roles)));
441
    while ($block = db_fetch_object($result)) {
442
      if (!isset($blocks[$block->region])) {
443 444
        $blocks[$block->region] = array();
      }
445
      // Use the user's block visibility setting, if necessary
446 447 448
      if ($block->custom != 0) {
        if ($user->uid && isset($user->block[$block->module][$block->delta])) {
          $enabled = $user->block[$block->module][$block->delta];
449 450
        }
        else {
451
          $enabled = ($block->custom == 1);
452 453 454 455
        }
      }
      else {
        $enabled = TRUE;
456
      }
457 458

      // Match path if necessary
459 460
      if ($block->pages) {
        if ($block->visibility < 2) {
461
          $path = drupal_get_path_alias($_GET['q']);
462
          $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1'. preg_quote(variable_get('site_frontpage', 'node'), '/') .'\2'), preg_quote($block->pages, '/')) .')$/';
463 464 465 466 467 468 469 470 471
          // Compare with the internal and path alias (if any).
          $page_match = preg_match($regexp, $path);
          if ($path != $_GET['q']) {
            $page_match = $page_match || preg_match($regexp, $_GET['q']);
          }
          // 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);
472 473
        }
        else {
474
          $page_match = drupal_eval($block->pages);
475
        }
476 477
      }
      else {
478
        $page_match = TRUE;
479
      }
480

481
      if ($enabled && $page_match) {
482 483
        // Check the current throttle status and see if block should be displayed
        // based on server load.
484
        if (!($block->throttle && (module_invoke('throttle', 'status') > 0))) {
485 486 487 488 489 490 491 492 493 494 495 496 497
          // 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'.
          if (!count(module_implements('node_grants')) && $_SERVER['REQUEST_METHOD'] == 'GET' && ($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);
            }
          }

498
          if (isset($array) && is_array($array)) {
499 500 501
            foreach ($array as $k => $v) {
              $block->$k = $v;
            }
502
          }
Dries's avatar
 
Dries committed
503
        }
504
        if (isset($block->content) && $block->content) {
505 506 507 508 509
          // 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 == '<none>' ? '' : check_plain($block->title);
          }
510
          $blocks[$block->region]["{$block->module}_{$block->delta}"] = $block;
511 512 513 514
        }
      }
    }
  }
515
  // Create an empty array if there were no entries
516
  if (!isset($blocks[$region])) {
517 518
    $blocks[$region] = array();
  }
519 520
  return $blocks[$region];
}
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572

/**
 * Assemble the cache_id to use for a given block.
 *
 * The cache_id string reflects the viewing context for the current block
 * instance, obtained by concatenating the relevant context information
 * (user, page, ...) according to the block's cache settings (BLOCK_CACHE_*
 * constants). Two block instances can use the same cached content when
 * they share the same cache_id.
 *
 * Theme and language contexts are automatically differenciated.
 *
 * @param $block
 * @return
 *   The string used as cache_id for the block.
 */
function _block_get_cache_id($block) {
  global $theme, $base_root, $user;

  // User 1 being out of the regular 'roles define permissions' schema,
  // it brings too many chances of having unwanted output get in the cache
  // and later be served to other users. We therefore exclude user 1 from
  // block caching.
  if (variable_get('block_cache', 0) && $block->cache != BLOCK_NO_CACHE && $user->uid != 1) {
    $cid_parts = array();

    // Start with common sub-patterns: block identification, theme, language.
    $cid_parts[] = $block->module;
    $cid_parts[] = $block->delta;
    $cid_parts[] = $theme;
    if (module_exists('locale')) {
      global $language;
      $cid_parts[] = $language->language;
    }

    // 'PER_ROLE' and 'PER_USER' are mutually exclusive. 'PER_USER' can be a
    // resource drag for sites with many users, so when a module is being
    // equivocal, we favor the less expensive 'PER_ROLE' pattern.
    if ($block->cache & BLOCK_CACHE_PER_ROLE) {
      $cid_parts[] = 'r.'. implode(',', array_keys($user->roles));
    }
    elseif ($block->cache & BLOCK_CACHE_PER_USER) {
      $cid_parts[] = "u.$user->uid";
    }

    if ($block->cache & BLOCK_CACHE_PER_PAGE) {
      $cid_parts[] = $base_root . request_uri();
    }

    return implode(':', $cid_parts);
  }
}