I have been having a play with this module and while it displays blocks added to regions from the admin block screen, it does does not output any blocks on the page that are told to display in regions using context.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

bleen’s picture

subscribing

kungfuchris’s picture

I managed to find a workaround for this. I am interested in getting some feedback from the Drupal community if this is a correct approach.

Here goes....

Context module specifies that it’s the responsibility of themes implementing theme_blocks() to take advantage of context blocks visibility on their own. But the esi module does not do this. The workaround for this is to alter the theme registry (see snippet 1) by implementing hook_theme_registry_alter() in your module and override the default theme function for theme(‘blocks’).

In your custom theme function for theme(‘blocks’) you can them add the code from the esi theme function for theme blocks, along with the call to context_block_list() which is context alternative for Drupal’s block_list() that retrieves context enabled blocks.

The esi function that handles getting themed HTML for a particular block from an esi tag (_esi__get_block()) in esi.inc also calls the Drupal block_list() which ignores context. An implementation of module_invoke_all() e.g. $blocks = module_invoke_all(‘context_enabled_blocks’, $blcoks, $region), after it calls block_list() would allow our modules to return context enabled blocks . (see snippet 3)

I have found that even though this works to display context enable blocks when using the esi module, there is still a lot more to investigate as context blocks only seem to appear if the context is set to sitewide.

Maybe I am missing something, but hoping this gets the conversation started as there is a lot of buzz about esi happening around various discussion groups but very little case studies of a concrete implementation.

<?php
// Snippet 1
function custom_module_theme_registry_alter(&$theme_registry) {
  // override the default theme function for theme('blocks').
  if (module_exists('context')){
		$theme_registry['blocks']['function'] = 'custom_module_blocks';
	}
}
?>
<?php
// Snippet 2
function custom_module_blocks($region){

// Taken from context_blocks();
// Bail if this region is disabled.

$disabled_regions = context_active_values('theme_regiontoggle');
  if (!empty($disabled_regions) && in_array($region, $disabled_regions)) {echo 'here';
    return '';
  }

  $output = '';
  // load our helper file.
  require_once(drupal_get_path('module', 'esi') . '/esi.inc');
  if ($list = context_block_list($region)) {
    foreach ($list as $key => $block) {
      // if ESI's enabled on the block, add an ESI tag instead of block content.
      $esi_config = _esi__block_settings($block->module, $block->delta);
      $output .= $esi_config->enabled
        ? theme('esi_tag', $block)
        : theme('block', $block);
    }
  }
   // Add any content assigned to this region through drupal_set_content() calls.
  $output .= drupal_get_content($region);
  return $output;
}
?>
<?php
// Snippet 3
function custom_module_context_enabled_blocks($blocks, $region){
    $blocks = context_block_list($region);
    return $blocks;
}
?>
wwhurley’s picture

A better implementation would be to use something like the following:

/**
 * Implements hook_context_plugins to define our custom block context plugin
 * 
 * @return array
 */
function esi_context_plugins() {
  $plugins = array();
  $plugins['context_reaction_esi_block'] = array(
    'handler' => array(
      'path' => drupal_get_path('module', 'esi') .'/plugins',
      'file' => 'context_reaction_esi_block.inc',
      'class' => 'context_reaction_esi_block',
      'parent' => 'context_reaction_block',
  ),
  );

  return $plugins;
}

/**
 * Implements hook_context_registry_alter to use our custom block context plugin
 * to output ESI tags instead of the block
 * 
 * @param $registry array
 */
function esi_context_registry_alter(&$registry) {
  if (isset($registry['reactions']['block'])) {
    $registry['reactions']['block']['plugin'] = 'context_reaction_esi_block';
  }
}

And then have

<?php
class context_reaction_esi_block extends context_reaction_block {
  protected function build_block($block, $reset = FALSE) {
    $esi_config = _esi__block_settings($block->module, $block->delta);

    if ($esi_config->enabled) {
      $block->content = theme('esi_tag', $block);
    }
    else {
      $array = module_invoke($block->module, 'block', 'view', $block->delta);
      if (isset($array) && is_array($array)) {
        foreach ($array as $k => $v) {
          $block->$k = $v;
        }
      }
    }

    if (!empty($block->content)) {
      // Only query for custom block title if block core compatibility is enabled.
      if (!variable_get('context_reaction_block_disable_core', FALSE)) {
        global $user, $theme_key;
        $block->title = db_result(db_query("SELECT title FROM {blocks} WHERE module = '%s' AND delta = '%s' AND theme = '%s'", $block->module, $block->delta, $theme_key));
      }
      // Override default block title if a custom display title is present.
      if (!empty($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 = '';
      }
    }
    return $block;
  }
}

However to make that work the callback for the block has to change to:

/**
 * Get the themed HTML for a particular block
 *
 * @param String $theme
 *   The name of the theme: e.g. "garland"
 * @param String $region
 *   The name of the region the block is in: e.g. "left"
 * @param String $module
 * @param String $delta
 */
function _esi__get_block($theme, $region, $module, $delta) {
  global $theme_key;
  $theme_key = $theme;
  init_theme();

  $block = db_fetch_object(db_query("SELECT * FROM {blocks} WHERE module = '%s' AND delta = '%s' AND theme = '%s'", $module, $delta, $theme));
  $block->context = $region;
  $block->status = 1;
  $array = module_invoke($block->module, 'block', 'view', $block->delta);
  if (isset($array) && is_array($array)) {
    foreach ($array as $k => $v) {
      $block->$k = $v;
    }
  }

  if (!empty($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 = '';
  }

  return $block;
}

That all works, though you have to be careful about blocks that claim to be BLOCK_CACHE_NO_CACHE when they really don't need to be ... I'm looking at you, Boxes. Which brings me to an interesting question about why BLOCK_CACHE_PER_PAGE is implemented as a hash on the callback but BLOCK_CACHE_PER_USER and BLOCK_CACHE_PER_ROLE are handled as cookies.

We have a working version of this in production on a site. If there is any interest I could probably roll it as a patch to the existing release and/or fork the repo and do it there.

msonnabaum’s picture

Hmm, I couldn't get #3 to work, but I'd love to see a proper patch so I know I'm not missing something here.

theapi’s picture

Removing the function esi_theme_registry_alter(&$theme_registry) got #3 working for me.

wwhurley’s picture

Thanks for keeping me honest, PeterC. I did completely miss that step. Anyone know anything about the hash for BLOCK_CACHE_PER_PAGE and cookies for BLOCK_CACHE_PER_USER and BLOCK_CACHE_PER_ROLE?

manarth’s picture

The BLOCK_CACHE_PER_PAGE setting is used to provide a different ESI URL for each page where the block is displayed. This ensures Varnish will cache a different version of the block for each page it's displayed on.

The BLOCK_CACHE_PER_USER and BLOCK_CACHE_PER_ROLE cookies work with a custom vcl_hash routine which splits each block into a separate per-user/per-role cache-bin within Varnish. This means that a per-user block (such as a welcome message saying "Hello Bob") is only displayed to the user, and similarly for role-based block control.

BLOCK_CACHE_PER_PAGE can be implemented as a static hash because the block's contents only changes on a page-URL basis. Blocks which use BLOCK_CACHE_PER_ROLE and BLOCK_CACHE_PER_USER can vary their contents according to the user-session, so Varnish must be capable of varying them by cookie data.

kungfuchris’s picture

Hi whurleyf1,

Thanks for your post. It's working with one exception. It only works for blocks that has a sitewide context. Have you managed to get this working on blocks not set to sitewide context?

msonnabaum’s picture

Status: Active » Needs review
FileSize
4.67 KB

Here's a patch. Seems to be working for me.

AgaPe’s picture

Tried a patch from #9. I have layouts assigned to pages using context_layouts module, a submodule of context, and using the patch does include an esi tag for the blocks but does not use the proper template chosen using context_lauouts.

mikeytown2’s picture

Version: 6.x-1.0-beta1 » 6.x-2.x-dev
Status: Needs review » Fixed

#9 is in the 2.x branch. This should be fixed. If I am mistaken please re-open.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

askibinski’s picture

Status: Closed (fixed) » Needs work

last -dev results in a fatal error, because context_reaction_esi_block.inc is in the module root folder and should be in /plugins.

askibinski’s picture

And

      $block->content = theme('esi_tag', $block);

should be:

      $block->content = theme('esi_tag', 'block', $block);

or else $type is empty and src won't get build

mikeytown2’s picture

Status: Needs work » Fixed
FileSize
4.99 KB

This patch has been committed.

mikeytown2’s picture

This one as well

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.