We use the context module for Drupal in every site we build. It’s a great way to do things like create separate “sections” of a site, with different behaviors and layouts based on an arbitrary set of “conditions” or triggers. For example, we may want Blog posts to have a different layout than other parts of a site.

In a current project, we needed a condition not supplied by context by default. Thankfully, it’s pretty painless to extend context with custom conditions. An article from OpenSourcery pointed us in the right direction, and an article from Treehouse Agency gave us a Drupal 7 example, but otherwise we didn’t find a ton of documentation out there, so we thought we’d share our technique.

To quote from the context API, adding a new condition (or reaction) is a 4-step process:

  1. Implement hook_context_plugins() to define your plugins, classes, and class hierarchy.
  2. Implement hook_context_registry() to define your conditions and/or reactions and map them to plugins.
  3. Write your condition or reaction plugin class.
  4. Add in a Drupal integration point for your plugin.

Our example: Add a context condition that checks against a specific field’s value

Our site provides a field to content editors for many node types that lets them select a layout for the node. The field is of the “List (text)” type with only a few, known values. We want to add a context condition that checks against the value of the node field, and can then react in many ways.

Step 1: Implement hook_context_plugins

This code goes in your module.

/**
 * Implements hook_context_plugins().
 *
 */
function mymodule_context_plugins() {
  $plugins = array();
  $plugins['mymodule_context_condition_myfield'] = array(
    'handler' => array(
      'path' => drupal_get_path('module', 'mymodule'),
      'file' => 'mymodule_context_condition_myfield.inc',
      'class' => 'mymodule_context_condition_myfield',
      'parent' => 'context_condition',
    ),
  );
  return $plugins;
}

Step 2: Implement hook_context_registry

This code goes in your module.

/**
 * Implements hook_context_registry().
 *
 */
function mymodule_context_registry() {
  return array(
    'conditions' => array(
      'myfield' => array(
        'title' => t('myfield field'),
        'description' => t('Set this context based on the value of myfield field.'),
        'plugin' => 'mymodule_context_condition_myfield',
      ),
    ),
  );
}

Step 3: Write the condition plugin class

This code goes in your plugin file. As defined in mymodule_context_plugins above, this file should be in your module’s directory, and be named “mymodule_context_condition_myfield.inc”.

/**
 * Expose the myfield field value as a context condition.
 */
class mymodule_context_condition_myfield extends context_condition {
  function condition_values() {
    $values = array();
 
    // Get the allowed options from our field, and return these to context
    // as the values for our condition.
    $field = field_info_field('field_myfield');
    $field_values = list_allowed_values($field);
 
    foreach($field_values as $field_key => $field_value) {
      $values[$field_key] = check_plain($field_value);
    }
    return $values;
  }
 
  function execute($node) {
    // Grab the value this node has stored for our field.
    if ($items = field_get_items('node', $node, 'field_myfield', $node->language)) {
 
      // See if any of the field's values satisfy the condition.
      foreach ($items as $item) {
        foreach ($this->get_contexts($item['value']) as $context) {
          $this->condition_met($context, $item['value']);
        }
      }
    }
  }
}

Step 4: Add a Drupal integration point for the plugin

You might hook into Drupal at any number of different points. We want to check our condition when viewing nodes, so hook_node_view is a good choice.

This code goes in your module.

/**
 * Implements hook_node_view().
 */
function mymodule_node_view($node, $view_mode) {
  // Fire our context plugin when viewing nodes.
  if ($view_mode == 'full') {
    if ($plugin = context_get_plugin('condition', 'myfield')) {
      $plugin->execute($node);
    }
  }
}

That’s it! Hopefully this example gets you thinking about many other ways to extend context. Thanks, context developers, for such a powerful and extensible module!