#918808: delay the rendering of blocks to enable smart cache strategies.

From: Damien Tournoud <damien@commerceguys.com>


---
 block/block.module         |  115 +++++++++++++++++++++-----------------------
 dashboard/dashboard.module |    3 -
 update/update.module       |   61 +++++++++++++----------
 3 files changed, 88 insertions(+), 91 deletions(-)

diff --git modules/block/block.module modules/block/block.module
index 27e31bc..a63a503 100644
--- modules/block/block.module
+++ modules/block/block.module
@@ -330,8 +330,11 @@ function _block_get_renderable_array($list = array()) {
   $weight = 0;
   $build = array();
   foreach ($list as $key => $block) {
-    $build[$key] = $block->content;
-    unset($block->content);
+    $build[$key] = array(
+      '#pre_render' => array('_block_pre_render_block'),
+      '#block' => $block,
+      '#weight' => ++$weight,
+    );
 
     // Add contextual links for this block; skip the main content block, since
     // contextual links are basically output as tabs/local tasks already. Also
@@ -341,12 +344,6 @@ function _block_get_renderable_array($list = array()) {
     if ($key != 'system_main' && $key != 'system_help') {
       $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($block->module, $block->delta));
     }
-
-    $build[$key] += array(
-      '#block' => $block,
-      '#weight' => ++$weight,
-    );
-    $build[$key]['#theme_wrappers'][] ='block';
   }
   $build['#sorted'] = TRUE;
   return $build;
@@ -644,9 +641,6 @@ function block_list($region) {
   if (!isset($blocks[$region])) {
     $blocks[$region] = array();
   }
-  else {
-    $blocks[$region] = _block_render_blocks($blocks[$region]);
-  }
 
   return $blocks[$region];
 }
@@ -792,64 +786,63 @@ function block_block_list_alter(&$blocks) {
 }
 
 /**
- * Render the content and subject for a set of blocks.
+ * Pre-render function: render the content and subject for a block.
  *
- * @param $region_blocks
- *   An array of block objects such as returned for one region by _block_load_blocks().
- *
- * @return
- *   An array of visible blocks as expected by drupal_render().
- */
-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]);
-      // 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);
+ * @param $element
+ *   A renderable array.
+ */
+function _block_pre_render_block($element) {
+  $block = $element['#block'];
+
+  // Render the block content if it has not been created already.
+  if (!isset($block->content)) {
+    // 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);
 
-        // Allow modules to modify the block before it is viewed, via either
-        // hook_block_view_alter() or hook_block_view_MODULE_DELTA_alter().
-        drupal_alter(array('block_view', "block_view_{$block->module}_{$block->delta}"), $array, $block);
+      // Allow modules to modify the block before it is viewed, via either
+      // hook_block_view_alter() or hook_block_view_MODULE_DELTA_alter().
+      drupal_alter(array('block_view', "block_view_{$block->module}_{$block->delta}"), $array, $block);
 
-        if (isset($cid)) {
-          cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
-        }
+      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) {
-        // Normalize to the drupal_render() structure.
-        if (is_string($block->content)) {
-          $block->content = array('#markup' => $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 == '<none>' ? '' : check_plain($block->title);
-        }
-        if (!isset($block->subject)) {
-          $block->subject = '';
-        }
-        $region_blocks["{$block->module}_{$block->delta}"] = $block;
+    if (isset($array) && is_array($array)) {
+      foreach ($array as $k => $v) {
+        $block->$k = $v;
       }
     }
   }
-  return $region_blocks;
+
+  if (isset($block->content) && $block->content) {
+    // Normalize to the drupal_render() structure.
+    if (is_string($block->content)) {
+      $block->content = array('#markup' => $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 == '<none>' ? '' : check_plain($block->title);
+    }
+    if (!isset($block->subject)) {
+      $block->subject = '';
+    }
+
+    // Add the content renderable array to the main element.
+    $element['content'] = $block->content;
+    unset($block->content);
+    $element['#theme_wrappers'][] = 'block';
+  }
+  return $element;
 }
 
 /**
diff --git modules/dashboard/dashboard.module modules/dashboard/dashboard.module
index 2c6f1d3..cf936b4 100644
--- modules/dashboard/dashboard.module
+++ modules/dashboard/dashboard.module
@@ -478,8 +478,7 @@ function dashboard_show_block_content($module, $delta) {
     ->fetchObject();
   $block_object->enabled = $block_object->page_match = TRUE;
   $blocks[$module . "_" . $delta] = $block_object;
-  $block_content = _block_render_blocks($blocks);
-  $build = _block_get_renderable_array($block_content);
+  $build = _block_get_renderable_array($blocks);
   $rendered_block = drupal_render($build);
   print $rendered_block;
   drupal_exit();
diff --git modules/update/update.module modules/update/update.module
index 746ec24..26ded2b 100644
--- modules/update/update.module
+++ modules/update/update.module
@@ -79,22 +79,6 @@ function update_help($path, $arg) {
     case 'admin/reports/updates':
       return '<p>' . t('Here you can find information about available updates for your installed modules and themes. Note that each module or theme is part of a "project", which may or may not have the same name, and might include multiple modules or themes within it.') . '</p>';
 
-    case 'admin/appearance':
-    case 'admin/modules':
-      module_load_install('update');
-      $status = update_requirements('runtime');
-      foreach (array('core', 'contrib') as $report_type) {
-        $type = 'update_' . $report_type;
-        if (isset($status[$type]['severity'])) {
-          if ($status[$type]['severity'] == REQUIREMENT_ERROR) {
-            drupal_set_message($status[$type]['description'], 'error');
-          }
-          elseif ($status[$type]['severity'] == REQUIREMENT_WARNING) {
-            drupal_set_message($status[$type]['description'], 'warning');
-          }
-        }
-      }
-
     case 'admin/appearance/update':
     case 'admin/appearance/install':
     case 'admin/modules/update':
@@ -128,23 +112,44 @@ function update_help($path, $arg) {
       }
       $output .= '</dl>';
       return $output;
+  }
+}
 
-    default:
-      // Otherwise, if we're on *any* admin page and there's a security
-      // update missing, print an error message about it.
-      if (arg(0) == 'admin' && strpos($path, '#') === FALSE
-          && user_access('administer site configuration')) {
-        module_load_install('update');
-        $status = update_requirements('runtime');
-        foreach (array('core', 'contrib') as $report_type) {
-          $type = 'update_' . $report_type;
-          if (isset($status[$type])
-              && isset($status[$type]['reason'])
-              && $status[$type]['reason'] === UPDATE_NOT_SECURE) {
+/**
+ * Implements hook_init().
+ */
+function update_init() {
+  if (arg(0) == 'admin' && user_access('administer site configuration')) {
+    module_load_install('update');
+    $status = update_requirements('runtime');
+
+    // If we are on the appearance or modules list, display a detailled report
+    // of the update status.
+    if (arg(1) == 'appearance' || arg(1) == 'modules') {
+      foreach (array('core', 'contrib') as $report_type) {
+        $type = 'update_' . $report_type;
+        if (isset($status[$type]['severity'])) {
+          if ($status[$type]['severity'] == REQUIREMENT_ERROR) {
             drupal_set_message($status[$type]['description'], 'error');
           }
+          elseif ($status[$type]['severity'] == REQUIREMENT_WARNING) {
+            drupal_set_message($status[$type]['description'], 'warning');
+          }
         }
       }
+    }
+    else {
+      // Otherwise, if we're on *any* admin page and there's a security
+      // update missing, print an error message about it.
+      foreach (array('core', 'contrib') as $report_type) {
+        $type = 'update_' . $report_type;
+        if (isset($status[$type])
+            && isset($status[$type]['reason'])
+            && $status[$type]['reason'] === UPDATE_NOT_SECURE) {
+          drupal_set_message($status[$type]['description'], 'error');
+        }
+      }
+    }
   }
 }
 
