Skip to main content

Creating a Custom Context Reaction in Drupal 7

May 26 '17

Recently I had a ticket come through that required me to add some JavaScript on several pages on a Drupal 7 site — with the possibility of needing to remove that JavaScript from certain pages depending on user reactions. Since I knew there was a good chance that things were going to change, I wanted to build something that could be changed quickly and easily.

The Problem

I needed to add some JavaScript to some landing pages, and also some node types. It is also possible that more pages/node types will need to be added or even removed based on user reactions.

The Solution

We are using the Context module on this particular site, so I opted to create a context reaction. That way, I can change the context condition for the reaction to meet the client’s needs, without needing to push new code. There is a fair amount of code for the reaction, but I think it is harder to make sense of it all in parts, so I’m just going to dump out the .module file and plugin itself and give a description of each after the fact.

my_reaction/my_reaction.module
<?php
 
/**
 * @file
 * Code for my_reaction.
 */
 
/**
 * Implements hook_ctools_plugin_api().
 */
function my_reaction_ctools_plugin_api($module = NULL, $api = NULL) {
  if ($module == "context" && $api == "plugins") {
    return array("version" => "3");
  }
}
 
/**
 * Implements hook_ctools_plugin_directory().
 */
function my_reaction_ctools_plugin_directory($module, $plugin) {
  return 'plugins/' . $plugin;
}
 
/**
 * Implements hook_context_registry().
 */
function my_reaction_context_registry() {
  $registry = array();
 
  $registry['reactions'] = array(
    'my_reaction' => array(
      'title' => t('My Reaction'),
      'description' => t('Does some stuff.'),
      'plugin' => 'my_reaction_plugin',
    ),
  );
 
  return $registry;
}
 
/**
 * Implements hook_context_plugins().
 */
function my_reaction_context_plugins() {
  $plugins = array();
 
  $plugins['my_reaction_plugin'] = array(
    'handler' => array(
      'path' => drupal_get_path('module', 'my_reaction') . '/plugins/context',
      'file' => 'my_reaction_plugin.inc',
      'class' => 'my_reaction_plugin',
      'parent' => 'context_reaction',
    ),
  );
 
  return $plugins;
}
 
/**
 * Implementation of hook_context_page_reaction().
 */
function my_reaction_context_page_reaction() {
  if ($plugin = context_get_plugin('reaction', 'my_reaction')) {
    $plugin->execute();
  }
}


Alright, the first two things that happen are hook_ctools_plugin_api() and hook_ctools_plugin_directory(). Those hooks are just letting Drupal know of the API version to use as well as where the plugins will be stored. Next we have hook_context_registry(), which defines the reaction and also associates it to the plugin called my_reaction_plugin. This is followed to a call to hook_context_plugins() which defines the plugin called my_reaction_plugin.

The last thing that happens in the module file is the definition of hook_context_page_reaction(). This hook is loading the custom reaction plugin and calls its execute() method. More on that later. Now we need to write the code for the plugin itself. The class definition of the my_reaction_plugin will extend context_reaction and will only have a few methods. Again, I will discuss the individual parts after the code itself.

 

my_reaction/plugins/context/my_reaction_plugin.inc
<?php
/**
 * @file
 * Extends class context_reaction for my_reaction.
 */
 
/**
 * Exposes My Reaction as a reaction in Context.
 */
class my_reaction_plugin extends context_reaction {
 
  /**
   * Provides the options form.
   */
  function options_form($context) {
    // NOTE: The context module will not save a reaction to a context unless it
    // provides some sort of option. I'm opting to use a value type here as it
    // will not render anything on screen.
 
    $form = array();
 
    $form['enabled'] = array(
      '#type' => 'value',
      '#value' => 'true'
    );
 
    return $form;
  }
 
  /**
   * Executes the logic the reaction if needed.
   */
  function execute() {
    $contexts = context_active_contexts();
 
    foreach ($contexts as $value) {
      if (!empty($value->reactions[$this->plugin])) {
        $this->doReaction();
        return;
      }
    }
  }
 
  /**
   * This function does the actual work of the reaction.
   */
  function doReaction() {
     drupal_add_js('alert("I reacted!");', 'inline');
  }
}


The first thing to note for the plugin is the options_form() method. If you want to make your reaction configurable, you would add your form fields here. You will notice that my example is empty with the exception of a comment and a value form field. While developing this plugin, I noticed that reactions that do not register options are not properly saved to the context. When I can, I would like to investigate this further to contribute a patch to fix that. For now, I have added a value form field as a workaround so that the reaction can be saved to the context.

Next, we have the execute() method. All this method does is to check the currently loaded context and call the doReaction() method if the plugin is found. Lastly, we have doReaction(). Normally this method does not exist, for usually the main section of code is in the execute() method. For better readability, I have pulled the code into its own method. All it does is add some JavaScript to the current page.

The Result

Once installed and enabled, you can configure the custom reaction to appear on any page, node type, or even some other condition that has been implemented within the context module. It wasn’t too hard to build, and it offers flexibility as I can change when this reaction fires with just a few click of the mouse. Better yet, this concept is not only for JavaScript — this has the potential to used for all sorts of things!

Happy Drupaling!

Read more about: