The joy of Extending Context

If you are like me then you love the Context module by Development Seed. This module has made it easy to customize the page based upon certain conditions. Sometimes you are in the need to extend the functionality of context. Extending the Context module is very easy. We had the need to use a publish and expire date/time for a context.

If you are like me then you love the Context module by Development Seed. This module has made it easy to customize the page based upon certain conditions. Sometimes you are in the need to extend the functionality of context. Extending the Context module is very easy. We had the need to use a publish and expire date/time for a context. Using the doxygen docs we can see the highly structured classes. There are 2 base classes, context_condition and context_reactions. We are creating 2 new conditions for a publish date/time and an expire date/time.

The first step to creating a context is declaring your plugins using hook_context_plugins().

/**
* Implementation of hook_context_plugins().
 */

function modulename_contexts_context_plugins() {
  $plugins = array();
  $plugins['context_condition_publish'] = array(
    'handler' => array(
      'path' => drupal_get_path('module', 'modulename') .'/plugins',
      'file' => 'context_condition_publish.inc',
      'class' => 'context_condition_publish',
      'parent' => 'context_condition',
    ),
  );
  $plugins['context_condition_expire'] = array(
    'handler' => array(
      'path' => drupal_get_path('module', 'modulename') .'/plugins',
      'file' => 'context_condition_expire.inc',
      'class' => 'context_condition_expire',
      'parent' => 'context_condition_publish',
    ),
  );
  return $plugins;
}

You can find the detail about this hook here. What this says is I am extending the context_condition class with context_condition_publish. Because the expire uses most of the same functions, I just extended the context_condition_publish class to create the context_condition_expire class.

The next step is to tell context about the new conditions. This is done using hook_context_registry().

/**
 * Implementation of hook_context_registry().
 */

function mymodule_contexts_context_registry() {
  return array(
    'conditions' => array(
      'publish' => array(
        'title' => t('Publish Date/Time'),
        'plugin' => 'context_condition_publish',
      ),
      'expire' => array(
        'title' => t('Expire Date/Time'),
        'plugin' => 'context_condition_expire',
      ),
    ),
  );
}

The plugin value refers to the key value you entered in hook_context_plugins().

How let's create the context_condition_publish class.

/**
 * Create a published date for a context
 */

class context_condition_publish extends context_condition {

  // Omit values since it could be any time
  function condition_values() {
    return array();
  }

  function condition_form($context) {
    $form = parent::condition_form($context);

    // Format the default value since it's saved as in the mktime() format
    if (!empty($form['#default_value'])) {
      $form['#default_value'] = array(
        'year' => date('Y', $form['#default_value'][0]),
        'month' => date('n', $form['#default_value'][0]),
        'day' => date('j', $form['#default_value'][0]),
      );
    } else {
      // Split up the current time
      $form['#default_value'] = array(
        'year' => date('Y', time()),
        'month' => date('n', time()),
        'day' => date('j', time()),
      );
    }

    unset($form['#options']);
    $form['#type'] = 'date';
    return $form;
  }

  function condition_form_submit($values) {
    // Change format of date to timestamp
    return array(
      mktime(
        0, // hour
        0, // min
        0, // sec
        $values['month'], // month
        $values['day'], // day
        $values['year'] // year
      ),
    );
  }

  /**
   * pass in the date to check against in timestamp format
   *
   * @param int $value The date/time to be evaluated in UNIX timestamp format
   *  defaults to time()
   */

  function execute($value = NULL) {
    // If nothing is passed then use now
    if (empty($value)) {
      $value = time();
    }

    foreach (context_enabled_contexts() as $context) {
      if ($date_time = $this->fetch_from_context($context, 'values')) {

        // If the current date is after the published date
       //then set the condition
        if ($date_time[0] <= $value) {
          $this->condition_met($context, $value);
        }
      }
    }
  }
}

This class is pretty simple. We are just changing a few functions. The function condition_form() returns a single FormAPI element. Since the date FromAPI element store the value as array('year' => $year, 'day' => $day, 'month' => $month) we change it to a UNIX timeseamp for faster processing on the page loads.

Next is the expire class

/**
 * Ability to expire a context
 */

class context_condition_expire extends context_condition_publish {

  /**
   * pass in the date to check against in timestamp format
   *
   * @param int $value The date/time to be evaluated in UNIX timestamp format
   *  defaults to time()
   */

  function execute($value = NULL) {
    // If nothing is passed then use now
    if (empty($value)) {
      $value = time();
    }

    foreach (context_enabled_contexts() as $context) {
      if ($date_time = $this->fetch_from_context($context, 'values')) {

        // If the current date is before the Expire date
        //then set the condition
        if ($date_time[0] > $value) {
          $this->condition_met($context, $value);
        }
      }
    }
  }
}

Wow, that was easy. Just one function. I love the power of objects.

So we have these 2 classes but what do we do with them? The best place to check this condition is early in the page call. We don't want hook_init() because this would add unnecessary load on AJAX request or any none page requests. Luckily, context provides us just a place: hook_context_page_condition().

/**
 * Implementation of hook_context_page_condition().
 */

function mymodule_context_page_condition() {
  // Load the publish plugin and see execute.
  if ($plugin = context_get_plugin('condition', 'publish')) {
    $plugin->execute();
  }
  // Load the expire plugin and see execute.
  if ($plugin = context_get_plugin('condition', 'expire')) {
    $plugin->execute();
  }
}

That's it!! Now we have the ability to publish and expire contexts. In my next post, I will show you how to customize the Context Editor sidebar that is used with the admin module.

Neil Hastings