Monday, November 10, 2014 - 07:01

Extending Drupal contexts with a plugin

There is a rich set of Drupal context modules and chances are you might find what you are looking for by sifting through what's already available. But once in a while you really need that context that's just not available.

Extending the context module is much easier than it sounds. In this case we'll assume that we need a context to detect mobile platforms and for some reason browsecap is not what we want. We want a more lightweight solution. We are going to use the mobile_detect module instead but that one has no context plugins. We are going to fix that now. And for our purpose it's remarkably easy.

Context has two features. Conditions and reactions. The former are like triggers in Drupal. The latter are like actions. The difference is that the action is setting a context. The two concepts are very similar. Context however is much easier.

For our purpose we need to extract the platform from the mobile_detect module and craft a context out of that. So in terms of Context we are talking about a condition.

Our module will be named context_mobile_detect. First of all we need to tell context that we are providing a plugin. We do that via Context's hook context_plugins()

function context_mobile_detect_context_plugins() {
	$plugins = array();
	$plugins['context_condition_mobile_detect'] = array(
		'handler' => array(
			'path' => drupal_get_path('module', 'context_mobile_detect') . '/plugins',
			'file' => '',
			'class' => 'ContextConditionMobileDetect',
			'parent' => 'context_condition',
	return $plugins;

We are defining a plugin called context_condition_mobile_detect. It's handler is defined as a class ContextConditionMobileDetect in in our module's plugins sub-directory. And it is derived from context_condition. Context's base class for conditions.

Next we need to register some stuff with Context. The hook is called context_registry.

function context_mobile_detect_context_registry() {
	return array(
		'conditions' => array(
			'mobile_detect' => array(
				'title' => t('Mobile Detect'),
				'plugin' => 'context_condition_mobile_detect',

Since we are just handling conditions this is also a pretty simple construct. The function is quite self explaining. We really just introduce our plugin as a condition into Context's plugin registry.

We also need to trigger our magic when our module is executed. We can do this in init() with the following snippet.

function context_mobile_detect_init() {
	$plugin = context_get_plugin('condition', 'mobile_detect');
	if ($plugin) {

Also quite obvious what we are doing. We get our plugin from Context's registry and if we got something back we fire. The execute() function is explained later.

So. Let's have a look at our actual plugin. It's a class derived from context_condition and the only relevant function in this case is execute().

function execute() {
	//let's get our mobile_detect object
	$device = mobile_detect_get_object();
	foreach ($this->get_contexts() as $context) {
		if (in_array('mobile', $context->conditions['mobile_detect']['values']) {
			if ($device->isMobile()) {

In this branch we just check if we are looking for a generic mobile device. If that is true we set the condition as met. All in all there are 3 conditions checked here but they are all the same. A phone is basically a mobile that's not a tablet. The rest of the functions are self explaining. You can fetch the entire module from the attachment.

As you can see creating a simple context plugin is fairly easy and hardly much code. Even though the module is lacking functionality on the reaction side it's perfectly suitable if you want to set an already existing reaction based on mobile platforms. For example different templates for different devices through template suggestions.

There's a lot of stuff that could be added to the context plugin. Most notably some reactions. For example setting a viewport meta tag if a mobile device was found and add options to further customize a condition. But that's just noise making the concept appear more complicated than it actually is.